+// ANNA - Anna is Not Nothingness Anymore //
+// //
+// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
+// //
+// See project site at http://redmine.teslayout.com/projects/anna-suite //
+// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
+
+
+#include <typeinfo>
+
+#include <algorithm>
+
+#include <anna/core/tracing/Logger.hpp>
+#include <anna/core/functions.hpp>
+
+#include <anna/dbms/dbms.hpp>
+#include <anna/dbos/dbos.hpp>
+
+#include <anna/xml/Node.hpp>
+#include <anna/xml/Attribute.hpp>
+
+using namespace std;
+using namespace anna;
+
+StorageArea::StorageArea(const char* name, const Size maxSize, ObjectAllocator objectAllocator, const StorageArea::AccessMode::_v accessMode, const int errorCode) :
+ a_name(name),
+ a_maxSize(maxSize),
+ a_objectAllocator(objectAllocator),
+ a_accessMode(accessMode),
+ a_errorCode(errorCode),
+ a_indexBlock(0),
+ a_sizeof(0),
+ a_doneReuse(0) {
+ a_blocks.push_back(a_currentBlock = new Block(objectAllocator, maxSize));
+ a_hit = a_fault = 0;
+}
+
+StorageArea::~StorageArea() {
+}
+
+//------------------------------------------------------------------------------------------------
+// Carga los datos del objeto y los guarda en el area de almacemiento.
+//
+// (1) Si no esta en memoria => se carga.
+// (1.1) Si el registro no existe => se progresa la posible excepcion.
+//
+// (2) Cambia la politica de refresco de las areas de almacenamiento ReadWrite/Dirty. La carga solo se
+// realizara cuando la copia de utilizacion sea 0. Ademas de la mejora de rendimiento aseguramos que
+// la estabilidad de una instancia se mantiene durante toda la vida de esta. Por ejmplo evitamos que
+// una instancia A este trabajando con una copia de una instancia que estamos liberando y volviendo
+// a cargar con datos totalmente distintos ... imaginad que estamos recorriendo un vector asociado
+// o algo asi.
+//
+// (3) Si no ha podido ser recargada (seguramente tiene mas de una referencia) debe evitar el
+// uso en caso de que este marcada como Dirty.
+// (4) Si el contador de uso es cero => que ya esta marcado como memoria libre, pero recordar que
+// tambien puede estar en la memoria activa (pendiente de liberar y/o volver a usar). En este
+// caso se ha reusado.
+//------------------------------------------------------------------------------------------------
+Object* StorageArea::instance(Connection* connection, Loader& loader)
+throw(RuntimeException, DatabaseException) {
+ const Index index = loader.getIndex();
+ Object* result(NULL);
+ Instance* instance(NULL);
+ LOGDEBUG(
+ string msg("Instantiate (init) | ");
+ msg += asString();
+ msg += " | ";
+ msg += loader.asString();
+ msg += " | Index: ";
+ msg += functions::asHexString(index);
+ Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION)
+ );
+ loader.a_connection = connection;
+ std::string name("dbos::StorageArea::instance with ");
+ name += typeid(loader).name();
+ Guard guard(this, name.c_str());
+ iterator ii = a_directory.find(index);
+
+ if(ii == a_directory.end()) { // (1)
+ a_fault ++;
+
+ if(loader.load(connection, this) == true) { // (1.1)
+ pair <iterator, bool> rr;
+ bool wasInserted = false;
+ instance = allocate();
+
+ try {
+ instance->object->setIndex(index);
+ /* Al añadir la instancia antes de invocar al método Object::initialize
+ * nos aseguramos de que se permitan implementar relaciones circulares
+ */
+ rr = a_directory.insert(value_type(index, instance));
+ wasInserted = true;
+ result = instance->object;
+ instance->flags |= Flag::InProgress;
+ instance->object->initialize(loader);
+ instance->object->a_isStored = true;
+ instance->flags &= Flag::Done;
+ } catch(DatabaseException&) {
+ instance->flags &= Flag::Done;
+
+ if(wasInserted)
+ a_directory.erase(rr.first);
+
+ a_holes.insert(instance, Holes::Mode::ReadyToReuse);
+ throw;
+ } catch(RuntimeException&) {
+ instance->flags &= Flag::Done;
+
+ if(wasInserted)
+ a_directory.erase(rr.first);
+
+ a_holes.insert(instance, Holes::Mode::ReadyToReuse);
+ throw;
+ }
+ }
+ } else {
+ static const bool IgnoreDirty = true;
+ verifyStatus(instance = StorageArea::instance(ii), IgnoreDirty);
+
+ switch(a_accessMode) {
+ case AccessMode::ReadOnly:
+ result = ((instance->flags & Flag::Dirty) == 0) ? instance->object : reload(connection, loader, instance);
+ break;
+ case AccessMode::ReadWrite:
+ result = (instance->copyCounter > 0) ? instance->object : reload(connection, loader, instance);
+ break;
+ case AccessMode::ReadEver:
+ result = reload(connection, loader, instance);
+ break;
+ }
+
+ if(instance->flags & Flag::Dirty) { // (3)
+ string msg(asString());
+ msg += " | ";
+ msg += asString(instance);
+ msg += " | Instance selected as unusable";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ if(result != NULL) {
+ a_holes.erase(instance); // (4)
+ instance->copyCounter ++;
+ a_hit ++;
+ }
+ }
+
+ LOGINFORMATION(
+ string msg("Instantiate (final) | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(instance);
+ Logger::information(msg, ANNA_FILE_LOCATION)
+ );
+ return result;
+}
+
+Object* StorageArea::instance(Connection* connection, CrossedLoader& crossedLoader, Loader& loader)
+throw(RuntimeException, DatabaseException) {
+ Object* result = NULL;
+ crossedLoader.a_connection = connection;
+ // Si el seek devuelve 'true' es que ya tiene cargada la correspondencia entre la clave alternativa y la
+ // clave principal usada en el Loader recibido como parámetro.
+ bool loaded = (crossedLoader.seek() == false) ? crossedLoader.load(connection, this) : true;
+
+ if(loaded == true) {
+ /*
+ * Transfiere la clave principal conseguida por el cargador cruzado.
+ */
+ loader.upload(crossedLoader);
+ result = instance(connection, loader);
+ /*
+ * Da la posibilidad de que el cargador cruzado mantenga la correspondencia entre sus claves y las claves primarias
+ */
+ crossedLoader.download(loader);
+ }
+
+ return result;
+}
+
+//-------------------------------------------------------------------------
+// Crea un nuevo objeto en el area de almacenamiento.
+//-------------------------------------------------------------------------
+Object* StorageArea::create(Connection* connection, Creator& creator)
+throw(RuntimeException, DatabaseException) {
+ const Index index = creator.getIndex();
+ Instance* instance = NULL;
+ Object* result = NULL;
+
+ if(a_accessMode == AccessMode::ReadOnly) {
+ string msg(asString());
+ msg += " | Cannot create object with AccessMode::ReadOnly";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ LOGDEBUG(
+ string msg(asString());
+ msg += " | ";
+ msg += creator.asString();
+ msg += " | Index: ";
+ msg += functions::asHexString(index);
+ Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION)
+ );
+ creator.a_connection = connection;
+ std::string name("dbos::StorageArea::create with ");
+ name += typeid(creator).name();
+ Guard guard(this, name.c_str());
+ iterator ii = a_directory.find(index);
+
+ if(ii == a_directory.end()) {
+ pair <iterator, bool> rr;
+ bool wasInserted = false;
+ a_fault ++;
+ instance = allocate();
+
+ try {
+ instance->object->setIndex(index);
+ instance->copyCounter = 1;
+ result = instance->object;
+ rr = a_directory.insert(value_type(index, instance));
+ wasInserted = true;
+ instance->flags |= Flag::InProgress;
+ instance->object->create(creator);
+ instance->flags &= Flag::Done;
+ instance->object->a_isStored = false;
+ } catch(DatabaseException&) {
+ instance->flags &= Flag::Done;
+
+ if(wasInserted)
+ a_directory.erase(rr.first);
+
+ a_holes.insert(instance, Holes::Mode::ReadyToReuse);
+ throw;
+ } catch(RuntimeException&) {
+ instance->flags &= Flag::Done;
+
+ if(wasInserted)
+ a_directory.erase(rr.first);
+
+ a_holes.insert(instance, Holes::Mode::ReadyToReuse);
+ throw;
+ }
+ } else {
+ verifyStatus(instance = StorageArea::instance(ii));
+ a_hit ++;
+ a_holes.erase(instance);
+ instance->copyCounter ++;
+ result = instance->object;
+ }
+
+ LOGINFORMATION(
+ string msg("Create | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(instance);
+ Logger::information(msg, ANNA_FILE_LOCATION)
+ );
+ return result;
+}
+
+//-------------------------------------------------------------------------
+// Carga los datos del objeto y los guarda en el area de almacemiento.
+//
+// (1) Si tiene cuenta 0 => estaba en la lista de objetos liberados =>
+// lo sacamos de ah�
+//-------------------------------------------------------------------------
+Object* StorageArea::find(Loader& loader)
+throw(RuntimeException) {
+ const Index index = loader.getIndex();
+ Instance* instance = NULL;
+ Object* result = NULL;
+ LOGDEBUG(
+ string msg(asString());
+ msg += " | ";
+ msg += loader.asString();
+ msg += " | Index: ";
+ msg += functions::asHexString(index);
+ Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION)
+ );
+ std::string name("dbos::StorageArea::find with ");
+ name += typeid(loader).name();
+ Guard guard(this, name.c_str());
+ iterator ii = a_directory.find(index);
+
+ if(ii != a_directory.end()) {
+ verifyStatus(instance = StorageArea::instance(ii));
+ a_hit ++;
+ a_holes.erase(instance);
+ instance->copyCounter ++;
+ result = instance->object;
+ } else
+ a_fault ++;
+
+ LOGDEBUG(
+ string msg("Find | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(instance);
+ Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION)
+ );
+ return result;
+}
+
+Object* StorageArea::duplicate(const Object* object)
+throw(RuntimeException) {
+ if(object == NULL) return NULL;
+
+ std::string name("dbos::StorageArea::duplicate with ");
+ name += typeid(*object).name();
+ Guard guard(this, name.c_str());
+ iterator ii = a_directory.find(object->getIndex());
+
+ if(ii == a_directory.end()) {
+ a_fault ++;
+ string msg(asString());
+ msg += " | Index: ";
+ msg += functions::asHexString(object->getIndex());
+ msg += " | Invalid instance";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ Instance* instance = NULL;
+ verifyStatus(instance = StorageArea::instance(ii));
+ a_holes.erase(instance);
+ instance->copyCounter ++;
+ a_hit ++;
+ LOGINFORMATION(
+ string msg("Duplicate | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(instance);
+ Logger::information(msg, ANNA_FILE_LOCATION)
+ );
+ return instance->object;
+}
+
+bool StorageArea::isLoaded(const Loader& loader)
+throw(RuntimeException) {
+ const Index index = loader.getIndex();
+ std::string name("dbos::StorageArea::isLoaded with ");
+ name += typeid(loader).name();
+ Guard guard(this, name.c_str());
+ iterator ii = a_directory.find(index);
+ const bool result = (ii != a_directory.end());
+ LOGDEBUG(
+ string msg(asString());
+ msg += " | ";
+ msg += loader.asString();
+ msg += " | Index: ";
+ msg += functions::asHexString((int) index);
+ msg += functions::asText(" | isLoaded: ", result);
+ Logger::debug(msg, ANNA_FILE_LOCATION);
+ );
+ return result;
+}
+
+void StorageArea::apply(Connection& connection, Recorder& recorder)
+throw(RuntimeException, DatabaseException) {
+ ResultCode resultCode = connection.execute(recorder.getStatement());
+
+ if(resultCode.successful() == false)
+ throw DatabaseException(resultCode, ANNA_FILE_LOCATION);
+}
+
+//------------------------------------------------------------------------------------------------
+// Borra un objeto del medio fisico => lo borra tambien de la cache
+//
+// (1) Como copyCounter = 0 => Lo metera en lista de huecos, le quitara del directorio y
+// si fuera necesario invocara al 'destroy'.
+// (2) No la puede sacar de la memoria porque tiene referencias activas, pero por lo menos la
+// marca como no usable para intentar provocar los avisos de uso incorrecto y expulsar la
+// instancia en cuanto pueda.
+//------------------------------------------------------------------------------------------------
+void StorageArea::apply(Connection& connection, Eraser& eraser)
+throw(RuntimeException, DatabaseException) {
+ if(a_accessMode == AccessMode::ReadOnly) {
+ string msg(asString());
+ msg += " | Cannot erase object with AccessMode::ReadOnly";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ Object* object = eraser.getObject();
+ eraser.a_connection = &connection;
+ std::string name("dbos::StorageArea::apply with ");
+ name += typeid(eraser).name();
+ Guard guard(this, name.c_str());
+ Instance* instance = NULL;
+
+ if(object != NULL) {
+ iterator ii = a_directory.find(object->getIndex());
+
+ if(ii != a_directory.end()) {
+ instance = StorageArea::instance(ii);
+
+ if(instance->copyCounter > 1) {
+ instance->flags |= Flag::Incoherent; // (2)
+ string msg(eraser.asString());
+ msg += " | Instances: ";
+ msg += functions::asString(instance->copyCounter);
+ msg += " | Cannot delete object";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+ }
+ }
+
+ ResultCode resultCode = connection.execute(eraser.getStatement());
+
+ if(resultCode.successful() == false)
+ throw DatabaseException(resultCode, ANNA_FILE_LOCATION);
+
+ if(instance != NULL) {
+ instance->copyCounter = 0;
+ quickReusing(instance); // (1)
+ }
+}
+
+//------------------------------------------------------------------------------------------------
+// Decrementa la cuenta de utilizacin del objeto recibido como parametro.
+//
+// (1) Si la instancia que estamos liberando esta marcada como 'Incoherente' y es la ultima
+// referencia => es el momento de expulsarla de la memoria.
+// (2) Queda a la espera de que se vuelva a usar la referencia o que el numero de registros en
+// memoria alcance un numero tal que implique comenzar a reusar objetos liberados.
+//------------------------------------------------------------------------------------------------
+void StorageArea::release(Object** object)
+throw(RuntimeException) {
+ if(object == NULL) return;
+
+ if(*object == NULL) return;
+
+ std::string name("dbos::StorageArea::release with ");
+ name += typeid(**object).name();
+ Guard guard(this, name.c_str());
+ iterator ii = a_directory.find((*object)->getIndex());
+
+ if(ii == a_directory.end())
+ return;
+
+ Instance* instance = StorageArea::instance(ii);
+
+ if(instance->copyCounter > 0) {
+ if(-- instance->copyCounter == 0) {
+ if(instance->flags & Flag::Incoherent) // (1)
+ quickReusing(instance);
+ else
+ a_holes.insert(instance, Holes::Mode::TimeWait); // (2)
+ }
+ }
+
+ LOGINFORMATION(
+ string msg("Release | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(instance);
+ Logger::information(msg, ANNA_FILE_LOCATION)
+ );
+ *object = NULL;
+}
+
+//------------------------------------------------------------------------------------------------
+// Elimina toda la informacin referente al objeto recibido como parametro.
+//
+// (1) No la puede sacar de la memoria porque tiene referencias activas, pero por lo menos la
+// marca como no usable para intentar provocar los avisos de uso incorrecto y expulsar la
+// instancia en cuanto pueda.
+// (2) Como copyCounter = 0 => Lo metera en lista de huecos, le quitara del directorio y
+// si fuera necesario invocara al 'destroy'.
+//------------------------------------------------------------------------------------------------
+void StorageArea::erase(Object** object)
+throw(RuntimeException) {
+ if(object == NULL) return;
+
+ if(*object == NULL) return;
+
+ std::string name("dbos::StorageArea::erase with ");
+ name += typeid(**object).name();
+ Guard guard(this, name.c_str());
+ iterator ii = a_directory.find((*object)->getIndex());
+
+ if(ii == a_directory.end())
+ return;
+
+ Instance* instance = StorageArea::instance(ii);
+
+ if(instance->copyCounter > 1) {
+ instance->flags |= Flag::Incoherent; // (1)
+ string msg(asString());
+ msg += " | ";
+ msg += asString(instance);
+ msg += " | Cannot dump instance";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ LOGDEBUG(
+ string msg("Erase | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(instance);
+ Logger::debug(msg, ANNA_FILE_LOCATION)
+ );
+ instance->copyCounter = 0;
+ quickReusing(instance); // (2)
+ *object = NULL;
+}
+
+void StorageArea::dirty(Object* object)
+throw(RuntimeException) {
+ if(object == NULL) return;
+
+ std::string name("dbos::StorageArea::dirty with ");
+ name += typeid(*object).name();
+ Guard guard(this, name.c_str());
+ iterator ii = a_directory.find(object->getIndex());
+
+ if(ii == a_directory.end())
+ return;
+
+ Instance* instance = StorageArea::instance(ii);
+
+ if(instance->copyCounter > 1 && Logger::isActive(Logger::Warning)) {
+ string msg(asString());
+ msg += " | ";
+ msg += asString(instance);
+ msg += " | More than one copy on instance to be selected";
+ Logger::warning(msg, ANNA_FILE_LOCATION);
+ }
+
+ LOGDEBUG(
+ string msg("Dirty | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(instance);
+ Logger::debug(msg, ANNA_FILE_LOCATION)
+ );
+}
+
+//--------------------------------------------------------------------------------------------
+// Descarga todos los objetos de este area de almacenamiento.
+//
+// (1) Para que permite incluirla en los huecos.
+// (3) Si fuera necesario invoca al destroy => cuando se reuse no tendra que hacerlo.
+// (4) Si la entrada estaba marcada como 'incoherente' la desmarca, ya que hemos conseguido
+// expulsarla de cache.
+//--------------------------------------------------------------------------------------------
+void StorageArea::clear()
+throw(RuntimeException) {
+ Guard guard(this, "dbos::StorageArea::clear");
+ Instance* instance;
+ int n = 0;
+
+ for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
+ instance = StorageArea::instance(ii);
+ instance->copyCounter = 0; // (1)
+ a_holes.insert(instance, Holes::Mode::ReadyToReuse);
+
+ if((instance->flags & Flag::Empty) == 0) { // (3)
+ LOGINFORMATION(
+ string msg("Destroy (clear) | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(instance);
+ Logger::information(msg, ANNA_FILE_LOCATION);
+ );
+ instance->object->destroy();
+ instance->flags |= Flag::Empty;
+ }
+
+ instance->flags &= Flag::NoIncoherent; // (4)
+ n ++;
+ }
+
+ a_directory.clear();
+ LOGWARNING(
+ string msg("Clear | ");
+ msg += asString();
+ msg += functions::asText(" | Destroyed objects: ", n);
+ Logger::warning(msg, ANNA_FILE_LOCATION)
+ );
+}
+
+//---------------------------------------------------------------------------------------------------
+// Intenta recargar la informacion del medio fisico en la instancia del objeto.
+// Si hay algun problema y la instancia no puede guardarse en la de objetos disponibles
+// (a_holes.insert == false) => se marca como corrupto y no se podra usar hasta que
+// que no se libere su ultima instancia.
+//
+// (1) Si hubiera algun problema al invocar al 'initialize' no habria que volver a invocar
+// a este metodo.
+// (2) Recordar que la recarga solo se intenta cuando la copyCounter = 0, por tanto en el caso de
+// intentar recargar un registro que ha sido borrado no se vuelve a grabar como Ready porque
+// YA esta en la lista de huecos y seria necesario borrarlo de esta, cambiarle el msHoleTime y
+// volverlo a grabar, pero la cosa funciona porque se saca del directorio de objetos cargados
+// y se libera su memoria.
+//---------------------------------------------------------------------------------------------------
+Object* StorageArea::reload(dbms::Connection* connection, Loader& loader, StorageArea::Instance* instance)
+throw(RuntimeException, dbms::DatabaseException) {
+ const bool enableUpdate = (instance->flags & Flag::Dirty) ? true : instance->object->enableUpdate();
+ bool hasChanges(false);
+
+ if(enableUpdate == true) {
+ try {
+ if(loader.load(connection, this) == false) {
+ checkIncoherence(instance); // (2)
+ return NULL;
+ }
+ } catch(RuntimeException&) {
+ checkIncoherence(instance);
+ throw;
+ } catch(dbms::DatabaseException&) {
+ checkIncoherence(instance);
+ throw;
+ }
+
+ hasChanges = (instance->flags & Flag::Dirty) ? true : instance->object->hasChanges(loader);
+ }
+
+ LOGDEBUG(
+ string msg("Reload | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(instance);
+ msg += anna::functions::asText(" | EnableUpdate: ", enableUpdate);
+ msg += anna::functions::asText(" | HasChanges: ", hasChanges);
+ Logger::debug(msg, ANNA_FILE_LOCATION);
+ );
+
+ if(hasChanges == true) {
+ LOGINFORMATION(
+ string msg("Destroy (reload) | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(instance);
+ Logger::information(msg, ANNA_FILE_LOCATION);
+ );
+ instance->object->destroy();
+ instance->flags |= Flag::Empty; // (1)
+ instance->object->initialize(loader);
+ instance->flags &= Flag::NoEmpty;
+ instance->flags &= Flag::NoDirty;
+ }
+
+ return instance->object;
+}
+
+//------------------------------------------------------------------------------------------------
+// Cuando intenta recargar la informacion de una instancia desde el medio fisico, pero el
+// registro ha sido borrado =>
+// 1.- Si hay alguna instancia en uso => no puede liberar la instancia porque algun otro
+// objeto la tiene referenciada => la marca como corrupta.
+// 2.- Si hay una unica instancia en uso => puede liberarla
+//
+// (1) si la puede grabar en los huecos o la instancia ya esta en los huecos => saca el objeto
+// del directorio porque yo no es valido. Recupera la coherencia memoria-medio_fisico
+// porque ha conseguido "expulsar" de la cache el registro borrado.
+// (2) No puede "expulsar" el registro de la cache porque hay algun otro objeto que lo esta
+// referenciando => Marca ESTA instancia como incoherente => no se podra duplicar y volver
+// a instanciar, etc, etc
+//------------------------------------------------------------------------------------------------
+void StorageArea::checkIncoherence(StorageArea::Instance* instance)
+throw() {
+ if(quickReusing(instance) == true) {
+ LOGWARNING(
+ string msg("dbos::StorageArea::checkIncoherence | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(instance);
+ msg += " | Recover coherence between physical media and memory";
+ Logger::warning(msg, ANNA_FILE_LOCATION);
+ );
+ } else { // (2)
+ instance->flags |= Flag::Incoherent;
+ LOGWARNING(
+ string msg("dbos::StorageArea::checkIncoherence | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(instance);
+ msg += " | Detected incoherence between physical media and memory";
+ Logger::warning(msg, ANNA_FILE_LOCATION);
+ );
+ }
+}
+
+//------------------------------------------------------------------------------------------------
+// (1) si la puede grabar en los huecos o la instancia ya esta en los huecos => (2)
+// (2) saca el objeto del directorio porque yo no es valido.
+// (3) Si fuera necesario invoca al destroy => cuando se reuse no tendra que hacerlo.
+// (4) Si la entrada estaba marcada como 'incoherente' la desmarca, ya que hemos conseguido
+// expulsarla de cache.
+//
+// Recordar que en el caso de intentar recargar un registro que ha sido borrado no se vuelve
+// a grabar como Ready porque ya esta en la lista de huecos y seria necesario borrarlo de
+// esta, cambiarle el msHoleTime y volverlo a grabar.
+//------------------------------------------------------------------------------------------------
+bool StorageArea::quickReusing(StorageArea::Instance* instance)
+throw() {
+ bool result(false);
+
+ if(a_holes.insert(instance, Holes::Mode::ReadyToReuse) == true) { // (1)
+ iterator ii = a_directory.find(instance->object->getIndex());
+
+ if(ii != a_directory.end()) // (2)
+ a_directory.erase(ii);
+
+ if((instance->flags & Flag::Empty) == 0) { // (3)
+ LOGINFORMATION(
+ string msg("Destroy (quickreusing) | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(instance);
+ Logger::information(msg, ANNA_FILE_LOCATION);
+ );
+ instance->object->destroy();
+ instance->flags |= Flag::Empty;
+ }
+
+ instance->flags &= Flag::NoIncoherent; // (4)
+ result = true;
+ }
+
+ return result;
+}
+
+void StorageArea::verifyStatus(StorageArea::Instance* instance, const bool ignoreDirty)
+throw(RuntimeException) {
+ if(instance->flags & Flag::Incoherent) {
+ string msg(asString());
+ msg += " | ";
+ msg += asString(instance);
+ msg += " | Physical media and memory do not match";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ if(instance->flags & Flag::Empty) {
+ string msg(asString());
+ msg += " | ";
+ msg += asString(instance);
+ msg += " | Instance is empty";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ if(ignoreDirty == false && (instance->flags & Flag::Dirty)) {
+ string msg(asString());
+ msg += " | ";
+ msg += asString(instance);
+ msg += " | Instance is temporary locked";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ if(instance->flags & Flag::InProgress) {
+ string msg(asString());
+ msg += " | ";
+ msg += asString(instance);
+ msg += " | Instance already has word in progress";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+}
+
+string StorageArea::asString() const
+throw() {
+ string result("dbos::StorageArea { Name: ");
+ const int ratio = (a_hit == 0) ? 0 : (a_hit * 100) / (a_fault + a_hit);
+ /* Nº real de objetos en uso. En el directorio también se mantienen los que tienen la cuenta de utilización a 0 */
+ const int n = a_directory.size() - a_holes.size();
+ result += a_name;
+ result += " | N: ";
+ result += functions::asString(a_maxSize);
+ result += " | n: ";
+ result += functions::asString(n);
+ result += " | r: ";
+ result += functions::asString(a_directory.size());
+ result += " | AccessMode: ";
+ result += AccessMode::asString(a_accessMode);
+ result += " | Holes: ";
+ result += functions::asString(a_holes.size());
+ result += " | Reuse: ";
+ result += functions::asString(a_doneReuse);
+ result += " | Hit: ";
+ result += functions::asString(a_hit);
+ result += " | Fault: ";
+ result += functions::asString(a_fault);
+ result += " | Ratio: ";
+ result += functions::asString(ratio);
+ return result += "% }";
+}
+
+xml::Node* StorageArea::asXML(xml::Node* parent) const
+throw() {
+ xml::Node* result = parent->createChild("dbos.StorageArea");
+ xml::Node* node;
+ const int ratio = (a_hit == 0) ? 0 : (a_hit * 100) / (a_fault + a_hit);
+ result->createAttribute("Name", a_name);
+ result->createAttribute("AccessMode", AccessMode::asString(a_accessMode));
+ node = result->createChild("Size");
+ node->createAttribute("Value", a_directory.size() - a_holes.size());
+ node->createAttribute("Reserve", a_directory.size());
+ node->createAttribute("MaxValue", a_maxSize);
+ node->createAttribute("Holes", a_holes.size());
+ node = result->createChild("SizeOf");
+ node->createAttribute("Value", asMemorySize(getSizeOf()));
+ node->createAttribute("MaxValue", asMemorySize(getMaxSizeOf()));
+ node = result->createChild("Statistics");
+ node->createAttribute("Hit", a_hit);
+ node->createAttribute("Fault", a_fault);
+ node->createAttribute("Ratio", anna::functions::asString("%d%%", ratio));
+ node->createAttribute("DoneReuse", a_doneReuse);
+ return result;
+}
+
+string StorageArea::asMemorySize(const Size size)
+throw() {
+ string result;
+
+ if(size < 1024) {
+ result = functions::asString("%u bytes", size);
+ } else if(size < 1024 * 1024) {
+ static const Size ka = 1024;
+ result += functions::asString("%u Kb", size / ka);
+ } else {
+ static const Size mega = 1024 * 1024;
+ result += functions::asString("%u Mb", size / mega);
+ }
+
+ return result;
+}
+
+StorageArea::Instance* StorageArea::allocate()
+throw() {
+ Instance* result = NULL;
+
+ if((result = reuse()) == NULL) {
+ if((result = a_currentBlock->getInstance()) == NULL) {
+ if(++ a_indexBlock == a_blocks.size())
+ a_blocks.push_back(a_currentBlock = new Block(a_objectAllocator, a_maxSize));
+ else
+ a_currentBlock = a_blocks [a_indexBlock];
+
+ result = a_currentBlock->getInstance();
+ }
+ }
+
+ result->copyCounter = 1;
+//xxx result->msHoleTime = 0;
+ result->flags = Flag::None;
+//xxx result->hasHole = false;
+ return result;
+}
+
+//-----------------------------------------------------------------------------------------------------
+// Reusa un objeto que lleva demasiado tiempo sin ser utilizado.
+//
+// (0) Si el tiempo que lleva en la lista de objetos liberados es cero => Podemos usar el re-usado
+// rapido; ya ha sido eliminado del directorio, invoca al destroy, etc, etc.
+// (0.1) Para asegurar que NO desborde los 32 bits.
+// (1) Cuando n -> Nmax => Talpha = now => a poco tiempo que pase sin reusar los registros, estos
+// se comienzan a resuar. Cuando n -> 0 => talpha = 0 => No se reusan registros, sino que se crearan
+// nuevos.
+// (2) Si el primero de los registros disponibles no es suficientemente antiguo => no hay huecos libres
+//-----------------------------------------------------------------------------------------------------
+StorageArea::Instance* StorageArea::reuse()
+throw() {
+ if(a_holes.empty() == true)
+ return NULL;
+
+ Instance* result = NULL;
+ Instance* front = a_holes.front();
+ bool quickReuse;
+
+ if((front->flags & Flag::Ready) == false) {
+ if(a_directory.size() >= a_maxSize) { // El directorio contiene los que tienen cuenta 0 + los que están activos.
+ result = front;
+ iterator ii = a_directory.find(result->object->getIndex());
+
+ if(ii != a_directory.end())
+ a_directory.erase(ii);
+
+ if((result->flags & Flag::Empty) == 0) {
+ LOGINFORMATION(
+ string msg("Destroy (reuse) | ");
+ msg += asString();
+ msg += " | ";
+ msg += asString(result);
+ Logger::information(msg, ANNA_FILE_LOCATION);
+ );
+ result->object->destroy();
+ result->flags &= Flag::NoEmpty;
+ }
+ }
+
+ quickReuse = false;
+ } else {
+ // Si la entrada se cargó en los huecos como "usado rápido" se toma
+ result = front;
+ quickReuse = true;
+ }
+
+ if(result != NULL) {
+ a_doneReuse ++;
+ result->flags &= Flag::NoReady;
+ result->flags &= Flag::NoHasHole;
+ a_holes.pop_front();
+ }
+
+ LOGDEBUG(
+ string msg("dbos::StorageArea::reuse | ");
+ msg += asString();
+ msg += " | ";
+ msg += StorageArea::asString(result);
+ msg += functions::asText(" | QuickReuse: ", quickReuse);
+ Logger::debug(msg, ANNA_FILE_LOCATION);
+ );
+ return result;
+}
+
+std::string StorageArea::asString(const Instance* instance)
+throw() {
+ std::string result("Instance { ");
+
+ if(instance == NULL)
+ return result += "<null> } ";
+
+ result += "Reference: ";
+ result += functions::asHexString(anna_ptrnumber_cast(instance->object));
+ result += " | Index: ";
+ result += functions::asHexString(instance->object->getIndex());
+ result += functions::asText(" | Stored: ", instance->object->a_isStored);
+ result += " | CopyCounter: ";
+ result += functions::asString((unsigned int) instance->copyCounter);
+ result += " | Flags (";
+ result += functions::asHexString(instance->flags);
+ result += "):";
+
+ if(instance->flags == Flag::None)
+ result += " None";
+ else {
+ if(instance->flags & Flag::Dirty)
+ result += " Dirty";
+
+ if(instance->flags & Flag::Incoherent)
+ result += " Incoherent";
+
+ if(instance->flags & Flag::Empty)
+ result += " Empty";
+
+ if(instance->flags & Flag::HasHole)
+ result += " HasHole";
+
+ if(instance->flags & Flag::InProgress)
+ result += " InProgress";
+
+ result += (instance->flags & Flag::Ready) ? " Ready" : " TimeWait";
+ }
+
+ return result += " }";
+}
+
+const char* StorageArea::AccessMode::asString(const AccessMode::_v v)
+throw() {
+ static const char* text [] = { "ReadOnly", "ReadWrite", "ReadEver" };
+ return text [v];
+}
+
+/****************************************************************************************
+* Bloque de memoria.
+****************************************************************************************/
+StorageArea::Block::Block(ObjectAllocator objectAllocator, const Size maxSize) :
+ a_size(0) {
+ a_maxSize = std::min(8U, std::max(256U, maxSize >> 7));
+ a_instances = new Instance [a_maxSize];
+
+ for(int i = 0; i < a_maxSize; i ++)
+ a_instances [i].object = (*objectAllocator)();
+}
+
+bool StorageArea::Holes::insert(Instance* instance, const StorageArea::Holes::Mode::_v mode)
+throw() {
+ if(instance->copyCounter > 0)
+ return false;
+
+ /* Si la instancia ya ha sido registrada en la lista de huecos, sale sin más */
+ if((instance->flags & Flag::HasHole) == true)
+ return true;
+
+ switch(mode) {
+ case Mode::ReadyToReuse:
+ instance->holeIterator = a_holes.insert(a_holes.begin(), instance);
+ instance->flags |= Flag::HasHole;
+ instance->flags |= Flag::Ready;
+ a_size ++;
+ break;
+ case Mode::TimeWait:
+ instance->holeIterator = a_holes.insert(a_holes.end(), instance);
+ instance->flags |= Flag::HasHole;
+ instance->flags &= Flag::NoReady;
+ a_size ++;
+ break;
+ }
+
+ LOGLOCAL6(
+ string msg("dbos::StorageArea::Holes::insert | This: ");
+ msg += functions::asHexString(anna_ptrnumber_cast(this));
+ msg += " | ";
+ msg += StorageArea::asString(instance);
+ Logger::write(Logger::Local6, msg, ANNA_FILE_LOCATION);
+ );
+ return true;
+}
+
+void StorageArea::Holes::erase(Instance* instance)
+throw() {
+ // instance->msHoleTime = 0;
+ instance->flags |= Flag::Ready;
+
+ if(instance->copyCounter != 0)
+ return;
+
+ /* Si la instancia NO ha sido registrada en la lista de huecos, sale sin más */
+ if((instance->flags & Flag::HasHole) == false)
+ return;
+
+ LOGLOCAL6(
+ string msg("dbos::StorageArea::Holes::erase | This: ");
+ msg += functions::asHexString(anna_ptrnumber_cast(this));
+ msg += " | ";
+ msg += StorageArea::asString(instance);
+ Logger::write(Logger::Local6, msg, ANNA_FILE_LOCATION);
+ );
+ a_holes.erase(instance->holeIterator);
+ instance->flags &= Flag::NoHasHole;
+ a_size --;
+}
+
+/****************************************************************************************
+* Iterador
+
+class A {
+public:
+ A () : a (0) { cout << "C0: " << (int) this << endl; }
+ A (const int _a): a (_a) { cout << "C1: " << (int) this << " " << a << endl; }
+ A (const A& other) : a (other.a) { cout << "C2: " << (int) this << " " << a << endl; }
+
+ A& operator = (const A& other) {
+ cout << "CP: " << (int) this << " " << (a = other.a) << endl;
+ return *this;
+ }
+
+ int get () const { return a; }
+private:
+ int a;
+};
+
+A fx () { return A (2000); }
+
+int main ()
+{
+ A aa = 100;
+ A bb (200);
+ A xx = aa;
+ A cc = fx ();
+
+ cout << "CC: " << (int) &cc << " " << cc.get () << endl;
+
+}
+
+La salida del programucho es la siguiente:
+
+C1: -4198808 100
+C1: -4198812 200
+C2: -4198816 100
+C1: -4198820 2000
+CC: -4198820 2000
+
+Lo que quiere decir que la "cc" la crea directamente sobre la pila y la asigna en fx sin aplicar ningn
+otro constructor.
+
+Por eso cuando hac� StorageArea::Iterator ii = xxx->begin (), maxi = xxx->end (); .....
+no estaba pasando por el contructor copia ni por ningn otro constructor.
+
+****************************************************************************************/
+// Dejo todo el ejemplo para que sirva de recordatorio.
+