X-Git-Url: https://git.teslayout.com/public/public/public/?a=blobdiff_plain;f=source%2Fdbos%2FStorageArea.cpp;fp=source%2Fdbos%2FStorageArea.cpp;h=830f64777845bac0a646ee627c3c2189bdb63f2b;hb=78be86969d2f26a9084b0c4af6ce43d5fa4ed3fd;hp=0000000000000000000000000000000000000000;hpb=a3b95648bd76140ef55e0b5941d423eee6c3856f;p=anna.git diff --git a/source/dbos/StorageArea.cpp b/source/dbos/StorageArea.cpp new file mode 100644 index 0000000..830f647 --- /dev/null +++ b/source/dbos/StorageArea.cpp @@ -0,0 +1,1081 @@ +// 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 + +#include + +#include +#include + +#include +#include + +#include +#include + +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 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 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 += " } "; + + 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. +