Normalize xml processing
[anna.git] / source / dbos / StorageArea.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #include <typeinfo>
10
11 #include <algorithm>
12
13 #include <anna/core/tracing/Logger.hpp>
14 #include <anna/core/functions.hpp>
15
16 #include <anna/dbms/dbms.hpp>
17 #include <anna/dbos/dbos.hpp>
18
19 #include <anna/xml/Node.hpp>
20 #include <anna/xml/Attribute.hpp>
21
22 using namespace std;
23 using namespace anna;
24
25 StorageArea::StorageArea(const char* name, const Size maxSize, ObjectAllocator objectAllocator, const StorageArea::AccessMode::_v accessMode, const int errorCode) :
26   a_name(name),
27   a_maxSize(maxSize),
28   a_objectAllocator(objectAllocator),
29   a_accessMode(accessMode),
30   a_errorCode(errorCode),
31   a_indexBlock(0),
32   a_sizeof(0),
33   a_doneReuse(0) {
34   a_blocks.push_back(a_currentBlock = new Block(objectAllocator, maxSize));
35   a_hit = a_fault = 0;
36 }
37
38 StorageArea::~StorageArea() {
39 }
40
41 //------------------------------------------------------------------------------------------------
42 // Carga los datos del objeto y los guarda en el area de almacemiento.
43 //
44 // (1) Si no esta en memoria => se carga.
45 //     (1.1) Si el registro no existe => se progresa la posible excepcion.
46 //
47 // (2) Cambia la politica de refresco de las areas de almacenamiento ReadWrite/Dirty. La carga solo se
48 // realizara cuando la copia de utilizacion sea 0. Ademas de la mejora de rendimiento aseguramos que
49 // la estabilidad de una instancia se mantiene durante toda la vida de esta. Por ejmplo evitamos que
50 // una instancia A este trabajando con una copia de una instancia que estamos liberando y volviendo
51 // a cargar con datos totalmente distintos ... imaginad que estamos recorriendo un vector asociado
52 // o algo asi.
53 //
54 // (3) Si no ha podido ser recargada (seguramente tiene mas de una referencia) debe evitar el
55 // uso en caso de que este marcada como Dirty.
56 // (4) Si el contador de uso es cero => que ya esta marcado como memoria libre, pero recordar que
57 // tambien puede estar en la memoria activa (pendiente de liberar y/o volver a usar). En este
58 // caso se ha reusado.
59 //------------------------------------------------------------------------------------------------
60 Object* StorageArea::instance(Connection* connection, Loader& loader)
61 throw(RuntimeException, DatabaseException) {
62   const Index index = loader.getIndex();
63   Object* result(NULL);
64   Instance* instance(NULL);
65   LOGDEBUG(
66     string msg("Instantiate (init) | ");
67     msg += asString();
68     msg += " | ";
69     msg += loader.asString();
70     msg += " | Index: ";
71     msg += functions::asHexString(index);
72     Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION)
73   );
74   loader.a_connection = connection;
75   std::string name("dbos::StorageArea::instance with ");
76   name += typeid(loader).name();
77   Guard guard(this, name.c_str());
78   iterator ii = a_directory.find(index);
79
80   if(ii == a_directory.end()) {                                                 // (1)
81     a_fault ++;
82
83     if(loader.load(connection, this) == true) {                                     // (1.1)
84       pair <iterator, bool> rr;
85       bool wasInserted = false;
86       instance = allocate();
87
88       try {
89         instance->object->setIndex(index);
90         /* Al añadir la instancia antes de invocar al método Object::initialize
91          * nos aseguramos de que se permitan implementar relaciones circulares
92          */
93         rr = a_directory.insert(value_type(index, instance));
94         wasInserted = true;
95         result = instance->object;
96         instance->flags |= Flag::InProgress;
97         instance->object->initialize(loader);
98         instance->object->a_isStored = true;
99         instance->flags &= Flag::Done;
100       } catch(DatabaseException&) {
101         instance->flags &= Flag::Done;
102
103         if(wasInserted)
104           a_directory.erase(rr.first);
105
106         a_holes.insert(instance, Holes::Mode::ReadyToReuse);
107         throw;
108       } catch(RuntimeException&) {
109         instance->flags &= Flag::Done;
110
111         if(wasInserted)
112           a_directory.erase(rr.first);
113
114         a_holes.insert(instance, Holes::Mode::ReadyToReuse);
115         throw;
116       }
117     }
118   } else {
119     static const bool IgnoreDirty = true;
120     verifyStatus(instance = StorageArea::instance(ii), IgnoreDirty);
121
122     switch(a_accessMode) {
123     case AccessMode::ReadOnly:
124       result = ((instance->flags & Flag::Dirty) == 0) ? instance->object : reload(connection, loader, instance);
125       break;
126     case AccessMode::ReadWrite:
127       result = (instance->copyCounter > 0) ? instance->object : reload(connection, loader, instance);
128       break;
129     case AccessMode::ReadEver:
130       result = reload(connection, loader, instance);
131       break;
132     }
133
134     if(instance->flags & Flag::Dirty) {                                        // (3)
135       string msg(asString());
136       msg += " | ";
137       msg += asString(instance);
138       msg += " | Instance selected as unusable";
139       throw RuntimeException(msg, ANNA_FILE_LOCATION);
140     }
141
142     if(result != NULL) {
143       a_holes.erase(instance);                                                // (4)
144       instance->copyCounter ++;
145       a_hit ++;
146     }
147   }
148
149   LOGINFORMATION(
150     string msg("Instantiate (final) | ");
151     msg += asString();
152     msg += " | ";
153     msg += asString(instance);
154     Logger::information(msg, ANNA_FILE_LOCATION)
155   );
156   return result;
157 }
158
159 Object* StorageArea::instance(Connection* connection, CrossedLoader& crossedLoader, Loader& loader)
160 throw(RuntimeException, DatabaseException) {
161   Object* result = NULL;
162   crossedLoader.a_connection = connection;
163   // Si el seek devuelve 'true' es que ya tiene cargada la correspondencia entre la clave alternativa y la
164   // clave principal usada en el Loader recibido como parámetro.
165   bool loaded = (crossedLoader.seek() == false) ? crossedLoader.load(connection, this) : true;
166
167   if(loaded == true) {
168     /*
169      * Transfiere la clave principal conseguida por el cargador cruzado.
170      */
171     loader.upload(crossedLoader);
172     result = instance(connection, loader);
173     /*
174      * Da la posibilidad de que el cargador cruzado mantenga la correspondencia entre sus claves y las claves primarias
175      */
176     crossedLoader.download(loader);
177   }
178
179   return result;
180 }
181
182 //-------------------------------------------------------------------------
183 // Crea un nuevo objeto en el area de almacenamiento.
184 //-------------------------------------------------------------------------
185 Object* StorageArea::create(Connection* connection, Creator& creator)
186 throw(RuntimeException, DatabaseException) {
187   const Index index = creator.getIndex();
188   Instance* instance = NULL;
189   Object* result = NULL;
190
191   if(a_accessMode == AccessMode::ReadOnly) {
192     string msg(asString());
193     msg += " | Cannot create object with AccessMode::ReadOnly";
194     throw RuntimeException(msg, ANNA_FILE_LOCATION);
195   }
196
197   LOGDEBUG(
198     string msg(asString());
199     msg += " | ";
200     msg += creator.asString();
201     msg += " | Index: ";
202     msg += functions::asHexString(index);
203     Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION)
204   );
205   creator.a_connection = connection;
206   std::string name("dbos::StorageArea::create with ");
207   name += typeid(creator).name();
208   Guard guard(this, name.c_str());
209   iterator ii = a_directory.find(index);
210
211   if(ii == a_directory.end()) {
212     pair <iterator, bool> rr;
213     bool wasInserted = false;
214     a_fault ++;
215     instance = allocate();
216
217     try {
218       instance->object->setIndex(index);
219       instance->copyCounter = 1;
220       result = instance->object;
221       rr = a_directory.insert(value_type(index, instance));
222       wasInserted = true;
223       instance->flags |= Flag::InProgress;
224       instance->object->create(creator);
225       instance->flags &= Flag::Done;
226       instance->object->a_isStored = false;
227     } catch(DatabaseException&) {
228       instance->flags &= Flag::Done;
229
230       if(wasInserted)
231         a_directory.erase(rr.first);
232
233       a_holes.insert(instance, Holes::Mode::ReadyToReuse);
234       throw;
235     } catch(RuntimeException&) {
236       instance->flags &= Flag::Done;
237
238       if(wasInserted)
239         a_directory.erase(rr.first);
240
241       a_holes.insert(instance, Holes::Mode::ReadyToReuse);
242       throw;
243     }
244   } else {
245     verifyStatus(instance = StorageArea::instance(ii));
246     a_hit ++;
247     a_holes.erase(instance);
248     instance->copyCounter ++;
249     result = instance->object;
250   }
251
252   LOGINFORMATION(
253     string msg("Create | ");
254     msg += asString();
255     msg += " | ";
256     msg += asString(instance);
257     Logger::information(msg, ANNA_FILE_LOCATION)
258   );
259   return result;
260 }
261
262 //-------------------------------------------------------------------------
263 // Carga los datos del objeto y los guarda en el area de almacemiento.
264 //
265 // (1) Si tiene cuenta 0 => estaba en la lista de objetos liberados =>
266 //     lo sacamos de ah�
267 //-------------------------------------------------------------------------
268 Object* StorageArea::find(Loader& loader)
269 throw(RuntimeException) {
270   const Index index = loader.getIndex();
271   Instance* instance = NULL;
272   Object* result = NULL;
273   LOGDEBUG(
274     string msg(asString());
275     msg += " | ";
276     msg += loader.asString();
277     msg += " | Index: ";
278     msg += functions::asHexString(index);
279     Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION)
280   );
281   std::string name("dbos::StorageArea::find with ");
282   name += typeid(loader).name();
283   Guard guard(this, name.c_str());
284   iterator ii = a_directory.find(index);
285
286   if(ii != a_directory.end()) {
287     verifyStatus(instance = StorageArea::instance(ii));
288     a_hit ++;
289     a_holes.erase(instance);
290     instance->copyCounter ++;
291     result = instance->object;
292   } else
293     a_fault ++;
294
295   LOGDEBUG(
296     string msg("Find | ");
297     msg += asString();
298     msg += " | ";
299     msg += asString(instance);
300     Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION)
301   );
302   return result;
303 }
304
305 Object* StorageArea::duplicate(const Object* object)
306 throw(RuntimeException) {
307   if(object == NULL) return NULL;
308
309   std::string name("dbos::StorageArea::duplicate with ");
310   name += typeid(*object).name();
311   Guard guard(this, name.c_str());
312   iterator ii = a_directory.find(object->getIndex());
313
314   if(ii == a_directory.end()) {
315     a_fault ++;
316     string msg(asString());
317     msg += " | Index: ";
318     msg += functions::asHexString(object->getIndex());
319     msg += " | Invalid instance";
320     throw RuntimeException(msg, ANNA_FILE_LOCATION);
321   }
322
323   Instance* instance = NULL;
324   verifyStatus(instance = StorageArea::instance(ii));
325   a_holes.erase(instance);
326   instance->copyCounter ++;
327   a_hit ++;
328   LOGINFORMATION(
329     string msg("Duplicate | ");
330     msg += asString();
331     msg += " | ";
332     msg += asString(instance);
333     Logger::information(msg, ANNA_FILE_LOCATION)
334   );
335   return instance->object;
336 }
337
338 bool StorageArea::isLoaded(const Loader& loader)
339 throw(RuntimeException) {
340   const Index index = loader.getIndex();
341   std::string name("dbos::StorageArea::isLoaded with ");
342   name += typeid(loader).name();
343   Guard guard(this, name.c_str());
344   iterator ii = a_directory.find(index);
345   const bool result = (ii != a_directory.end());
346   LOGDEBUG(
347     string msg(asString());
348     msg += " | ";
349     msg += loader.asString();
350     msg += " | Index: ";
351     msg += functions::asHexString((int) index);
352     msg += functions::asText(" | isLoaded: ", result);
353     Logger::debug(msg, ANNA_FILE_LOCATION);
354   );
355   return result;
356 }
357
358 void StorageArea::apply(Connection& connection, Recorder& recorder)
359 throw(RuntimeException, DatabaseException) {
360   ResultCode resultCode = connection.execute(recorder.getStatement());
361
362   if(resultCode.successful() == false)
363     throw DatabaseException(resultCode, ANNA_FILE_LOCATION);
364 }
365
366 //------------------------------------------------------------------------------------------------
367 // Borra un objeto del medio fisico => lo borra tambien de la cache
368 //
369 // (1) Como copyCounter = 0 => Lo metera en lista de huecos, le quitara del directorio y
370 // si fuera necesario invocara al 'destroy'.
371 // (2) No la puede sacar de la memoria porque tiene referencias activas, pero por lo menos la
372 // marca como no usable para intentar provocar los avisos de uso incorrecto y expulsar la
373 // instancia en cuanto pueda.
374 //------------------------------------------------------------------------------------------------
375 void StorageArea::apply(Connection& connection, Eraser& eraser)
376 throw(RuntimeException, DatabaseException) {
377   if(a_accessMode == AccessMode::ReadOnly) {
378     string msg(asString());
379     msg += " | Cannot erase object with AccessMode::ReadOnly";
380     throw RuntimeException(msg, ANNA_FILE_LOCATION);
381   }
382
383   Object* object = eraser.getObject();
384   eraser.a_connection = &connection;
385   std::string name("dbos::StorageArea::apply with ");
386   name += typeid(eraser).name();
387   Guard guard(this, name.c_str());
388   Instance* instance = NULL;
389
390   if(object != NULL) {
391     iterator ii = a_directory.find(object->getIndex());
392
393     if(ii != a_directory.end()) {
394       instance = StorageArea::instance(ii);
395
396       if(instance->copyCounter > 1) {
397         instance->flags |= Flag::Incoherent;                         // (2)
398         string msg(eraser.asString());
399         msg += " | Instances: ";
400         msg += functions::asString(instance->copyCounter);
401         msg += " | Cannot delete object";
402         throw RuntimeException(msg, ANNA_FILE_LOCATION);
403       }
404     }
405   }
406
407   ResultCode resultCode = connection.execute(eraser.getStatement());
408
409   if(resultCode.successful() == false)
410     throw DatabaseException(resultCode, ANNA_FILE_LOCATION);
411
412   if(instance != NULL) {
413     instance->copyCounter = 0;
414     quickReusing(instance);                                             // (1)
415   }
416 }
417
418 //------------------------------------------------------------------------------------------------
419 // Decrementa la cuenta de utilizacin del objeto recibido como parametro.
420 //
421 // (1) Si la instancia que estamos liberando esta marcada como 'Incoherente' y es la ultima
422 // referencia => es el momento de expulsarla de la memoria.
423 // (2) Queda a la espera de que se vuelva a usar la referencia o que el numero de registros en
424 // memoria alcance un numero tal que implique comenzar a reusar objetos liberados.
425 //------------------------------------------------------------------------------------------------
426 void StorageArea::release(Object** object)
427 throw(RuntimeException) {
428   if(object == NULL) return;
429
430   if(*object == NULL) return;
431
432   std::string name("dbos::StorageArea::release with ");
433   name += typeid(**object).name();
434   Guard guard(this, name.c_str());
435   iterator ii = a_directory.find((*object)->getIndex());
436
437   if(ii == a_directory.end())
438     return;
439
440   Instance* instance = StorageArea::instance(ii);
441
442   if(instance->copyCounter > 0) {
443     if(-- instance->copyCounter == 0) {
444       if(instance->flags & Flag::Incoherent)                                   // (1)
445         quickReusing(instance);
446       else
447         a_holes.insert(instance, Holes::Mode::TimeWait);                     // (2)
448     }
449   }
450
451   LOGINFORMATION(
452     string msg("Release | ");
453     msg += asString();
454     msg += " | ";
455     msg += asString(instance);
456     Logger::information(msg, ANNA_FILE_LOCATION)
457   );
458   *object = NULL;
459 }
460
461 //------------------------------------------------------------------------------------------------
462 // Elimina toda la informacin referente al objeto recibido como parametro.
463 //
464 // (1) No la puede sacar de la memoria porque tiene referencias activas, pero por lo menos la
465 // marca como no usable para intentar provocar los avisos de uso incorrecto y expulsar la
466 // instancia en cuanto pueda.
467 // (2) Como copyCounter = 0 => Lo metera en lista de huecos, le quitara del directorio y
468 // si fuera necesario invocara al 'destroy'.
469 //------------------------------------------------------------------------------------------------
470 void StorageArea::erase(Object** object)
471 throw(RuntimeException) {
472   if(object == NULL) return;
473
474   if(*object == NULL) return;
475
476   std::string name("dbos::StorageArea::erase with ");
477   name += typeid(**object).name();
478   Guard guard(this, name.c_str());
479   iterator ii = a_directory.find((*object)->getIndex());
480
481   if(ii == a_directory.end())
482     return;
483
484   Instance* instance = StorageArea::instance(ii);
485
486   if(instance->copyCounter > 1) {
487     instance->flags |= Flag::Incoherent;                                 // (1)
488     string msg(asString());
489     msg += " | ";
490     msg += asString(instance);
491     msg += " | Cannot dump instance";
492     throw RuntimeException(msg, ANNA_FILE_LOCATION);
493   }
494
495   LOGDEBUG(
496     string msg("Erase | ");
497     msg += asString();
498     msg += " | ";
499     msg += asString(instance);
500     Logger::debug(msg, ANNA_FILE_LOCATION)
501   );
502   instance->copyCounter = 0;
503   quickReusing(instance);                                                 // (2)
504   *object = NULL;
505 }
506
507 void StorageArea::dirty(Object* object)
508 throw(RuntimeException) {
509   if(object == NULL) return;
510
511   std::string name("dbos::StorageArea::dirty with ");
512   name += typeid(*object).name();
513   Guard guard(this, name.c_str());
514   iterator ii = a_directory.find(object->getIndex());
515
516   if(ii == a_directory.end())
517     return;
518
519   Instance* instance = StorageArea::instance(ii);
520
521   if(instance->copyCounter > 1 && Logger::isActive(Logger::Warning)) {
522     string msg(asString());
523     msg += " | ";
524     msg += asString(instance);
525     msg += " | More than one copy on instance to be selected";
526     Logger::warning(msg, ANNA_FILE_LOCATION);
527   }
528
529   LOGDEBUG(
530     string msg("Dirty | ");
531     msg += asString();
532     msg += " | ";
533     msg += asString(instance);
534     Logger::debug(msg, ANNA_FILE_LOCATION)
535   );
536 }
537
538 //--------------------------------------------------------------------------------------------
539 // Descarga todos los objetos de este area de almacenamiento.
540 //
541 // (1) Para que permite incluirla en los huecos.
542 // (3) Si fuera necesario invoca al destroy => cuando se reuse no tendra que hacerlo.
543 // (4) Si la entrada estaba marcada como 'incoherente' la desmarca, ya que hemos conseguido
544 // expulsarla de cache.
545 //--------------------------------------------------------------------------------------------
546 void StorageArea::clear()
547 throw(RuntimeException) {
548   Guard guard(this, "dbos::StorageArea::clear");
549   Instance* instance;
550   int n = 0;
551
552   for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
553     instance = StorageArea::instance(ii);
554     instance->copyCounter = 0;                                              // (1)
555     a_holes.insert(instance, Holes::Mode::ReadyToReuse);
556
557     if((instance->flags & Flag::Empty) == 0) {                              // (3)
558       LOGINFORMATION(
559         string msg("Destroy (clear) | ");
560         msg += asString();
561         msg += " | ";
562         msg += asString(instance);
563         Logger::information(msg, ANNA_FILE_LOCATION);
564       );
565       instance->object->destroy();
566       instance->flags |= Flag::Empty;
567     }
568
569     instance->flags &= Flag::NoIncoherent;                                  // (4)
570     n ++;
571   }
572
573   a_directory.clear();
574   LOGWARNING(
575     string msg("Clear | ");
576     msg += asString();
577     msg += functions::asText(" | Destroyed objects: ", n);
578     Logger::warning(msg, ANNA_FILE_LOCATION)
579   );
580 }
581
582 //---------------------------------------------------------------------------------------------------
583 // Intenta recargar la informacion del medio fisico en la instancia del objeto.
584 // Si hay algun problema y la instancia no puede guardarse en la de objetos disponibles
585 // (a_holes.insert == false) => se marca como corrupto y no se podra usar hasta que
586 // que no se libere su ultima instancia.
587 //
588 // (1) Si hubiera algun problema al invocar al 'initialize' no habria que volver a invocar
589 // a este metodo.
590 // (2) Recordar que la recarga solo se intenta cuando la copyCounter = 0, por tanto en el caso de
591 // intentar recargar un registro que ha sido borrado no se vuelve a grabar como Ready porque
592 // YA esta en la lista de huecos y seria necesario borrarlo de esta, cambiarle el msHoleTime y
593 // volverlo a grabar, pero la cosa funciona porque se saca del directorio de objetos cargados
594 // y se libera su memoria.
595 //---------------------------------------------------------------------------------------------------
596 Object* StorageArea::reload(dbms::Connection* connection, Loader& loader, StorageArea::Instance* instance)
597 throw(RuntimeException, dbms::DatabaseException) {
598   const bool enableUpdate = (instance->flags & Flag::Dirty) ? true : instance->object->enableUpdate();
599   bool hasChanges(false);
600
601   if(enableUpdate == true) {
602     try {
603       if(loader.load(connection, this) == false) {
604         checkIncoherence(instance);                                          // (2)
605         return NULL;
606       }
607     } catch(RuntimeException&) {
608       checkIncoherence(instance);
609       throw;
610     } catch(dbms::DatabaseException&) {
611       checkIncoherence(instance);
612       throw;
613     }
614
615     hasChanges = (instance->flags & Flag::Dirty) ? true : instance->object->hasChanges(loader);
616   }
617
618   LOGDEBUG(
619     string msg("Reload | ");
620     msg += asString();
621     msg += " | ";
622     msg += asString(instance);
623     msg += anna::functions::asText(" | EnableUpdate: ", enableUpdate);
624     msg += anna::functions::asText(" | HasChanges: ", hasChanges);
625     Logger::debug(msg, ANNA_FILE_LOCATION);
626   );
627
628   if(hasChanges == true) {
629     LOGINFORMATION(
630       string msg("Destroy (reload) | ");
631       msg += asString();
632       msg += " | ";
633       msg += asString(instance);
634       Logger::information(msg, ANNA_FILE_LOCATION);
635     );
636     instance->object->destroy();
637     instance->flags |= Flag::Empty;                                            // (1)
638     instance->object->initialize(loader);
639     instance->flags &= Flag::NoEmpty;
640     instance->flags &= Flag::NoDirty;
641   }
642
643   return instance->object;
644 }
645
646 //------------------------------------------------------------------------------------------------
647 // Cuando intenta recargar la informacion de una instancia desde el medio fisico, pero el
648 // registro ha sido borrado =>
649 // 1.- Si hay alguna instancia en uso => no puede liberar la instancia porque algun otro
650 // objeto la tiene referenciada => la marca como corrupta.
651 // 2.- Si hay una unica instancia en uso => puede liberarla
652 //
653 // (1) si la puede grabar en los huecos o la instancia ya esta en los huecos => saca el objeto
654 // del directorio porque yo no es valido. Recupera la coherencia memoria-medio_fisico
655 // porque ha conseguido "expulsar" de la cache el registro borrado.
656 // (2) No puede "expulsar" el registro de la cache porque hay algun otro objeto que lo esta
657 // referenciando => Marca ESTA instancia como incoherente => no se podra duplicar y volver
658 // a instanciar, etc, etc
659 //------------------------------------------------------------------------------------------------
660 void StorageArea::checkIncoherence(StorageArea::Instance* instance)
661 throw() {
662   if(quickReusing(instance) == true) {
663     LOGWARNING(
664       string msg("dbos::StorageArea::checkIncoherence | ");
665       msg += asString();
666       msg += " | ";
667       msg += asString(instance);
668       msg += " | Recover coherence between physical media and memory";
669       Logger::warning(msg, ANNA_FILE_LOCATION);
670     );
671   } else {                                                             // (2)
672     instance->flags |= Flag::Incoherent;
673     LOGWARNING(
674       string msg("dbos::StorageArea::checkIncoherence | ");
675       msg += asString();
676       msg += " | ";
677       msg += asString(instance);
678       msg += " | Detected incoherence between physical media and memory";
679       Logger::warning(msg, ANNA_FILE_LOCATION);
680     );
681   }
682 }
683
684 //------------------------------------------------------------------------------------------------
685 // (1) si la puede grabar en los huecos o la instancia ya esta en los huecos => (2)
686 // (2) saca el objeto del directorio porque yo no es valido.
687 // (3) Si fuera necesario invoca al destroy => cuando se reuse no tendra que hacerlo.
688 // (4) Si la entrada estaba marcada como 'incoherente' la desmarca, ya que hemos conseguido
689 // expulsarla de cache.
690 //
691 // Recordar que en el caso de intentar recargar un registro que ha sido borrado no se vuelve
692 // a grabar como Ready porque ya esta en la lista de huecos y seria necesario borrarlo de
693 // esta, cambiarle el msHoleTime y volverlo a grabar.
694 //------------------------------------------------------------------------------------------------
695 bool StorageArea::quickReusing(StorageArea::Instance* instance)
696 throw() {
697   bool result(false);
698
699   if(a_holes.insert(instance, Holes::Mode::ReadyToReuse) == true) {          // (1)
700     iterator ii = a_directory.find(instance->object->getIndex());
701
702     if(ii != a_directory.end())                                             // (2)
703       a_directory.erase(ii);
704
705     if((instance->flags & Flag::Empty) == 0) {                              // (3)
706       LOGINFORMATION(
707         string msg("Destroy (quickreusing) | ");
708         msg += asString();
709         msg += " | ";
710         msg += asString(instance);
711         Logger::information(msg, ANNA_FILE_LOCATION);
712       );
713       instance->object->destroy();
714       instance->flags |= Flag::Empty;
715     }
716
717     instance->flags &= Flag::NoIncoherent;                                  // (4)
718     result = true;
719   }
720
721   return result;
722 }
723
724 void StorageArea::verifyStatus(StorageArea::Instance* instance, const bool ignoreDirty)
725 throw(RuntimeException) {
726   if(instance->flags & Flag::Incoherent) {
727     string msg(asString());
728     msg += " | ";
729     msg += asString(instance);
730     msg += " | Physical media and memory do not match";
731     throw RuntimeException(msg, ANNA_FILE_LOCATION);
732   }
733
734   if(instance->flags & Flag::Empty) {
735     string msg(asString());
736     msg += " | ";
737     msg += asString(instance);
738     msg += " | Instance is empty";
739     throw RuntimeException(msg, ANNA_FILE_LOCATION);
740   }
741
742   if(ignoreDirty == false && (instance->flags & Flag::Dirty)) {
743     string msg(asString());
744     msg += " | ";
745     msg += asString(instance);
746     msg += " | Instance is temporary locked";
747     throw RuntimeException(msg, ANNA_FILE_LOCATION);
748   }
749
750   if(instance->flags & Flag::InProgress) {
751     string msg(asString());
752     msg += " | ";
753     msg += asString(instance);
754     msg += " | Instance already has word in progress";
755     throw RuntimeException(msg, ANNA_FILE_LOCATION);
756   }
757 }
758
759 string StorageArea::asString() const
760 throw() {
761   string result("dbos::StorageArea { Name: ");
762   const int ratio = (a_hit == 0) ? 0 : (a_hit * 100) / (a_fault + a_hit);
763   /* Nº real de objetos en uso. En el directorio también se mantienen los que tienen la cuenta de utilización a 0 */
764   const int n = a_directory.size() - a_holes.size();
765   result += a_name;
766   result += " | N: ";
767   result += functions::asString(a_maxSize);
768   result += " | n: ";
769   result += functions::asString(n);
770   result += " | r: ";
771   result += functions::asString(a_directory.size());
772   result += " | AccessMode: ";
773   result += AccessMode::asString(a_accessMode);
774   result += " | Holes: ";
775   result += functions::asString(a_holes.size());
776   result += " | Reuse: ";
777   result += functions::asString(a_doneReuse);
778   result += " | Hit: ";
779   result += functions::asString(a_hit);
780   result += " | Fault: ";
781   result += functions::asString(a_fault);
782   result += " | Ratio: ";
783   result += functions::asString(ratio);
784   return result += "% }";
785 }
786
787 xml::Node* StorageArea::asXML(xml::Node* parent) const
788 throw() {
789   xml::Node* result = parent->createChild("dbos.StorageArea");
790   xml::Node* node;
791   const int ratio = (a_hit == 0) ? 0 : (a_hit * 100) / (a_fault + a_hit);
792   result->createAttribute("Name", a_name);
793   result->createAttribute("AccessMode", AccessMode::asString(a_accessMode));
794   node = result->createChild("Size");
795   node->createAttribute("Value", a_directory.size() - a_holes.size());
796   node->createAttribute("Reserve", a_directory.size());
797   node->createAttribute("MaxValue", a_maxSize);
798   node->createAttribute("Holes", a_holes.size());
799   node = result->createChild("SizeOf");
800   node->createAttribute("Value", asMemorySize(getSizeOf()));
801   node->createAttribute("MaxValue", asMemorySize(getMaxSizeOf()));
802   node = result->createChild("Statistics");
803   node->createAttribute("Hit", a_hit);
804   node->createAttribute("Fault", a_fault);
805   node->createAttribute("Ratio", anna::functions::asString("%d%%", ratio));
806   node->createAttribute("DoneReuse", a_doneReuse);
807   return result;
808 }
809
810 string StorageArea::asMemorySize(const Size size)
811 throw() {
812   string result;
813
814   if(size < 1024) {
815     result = functions::asString("%u bytes", size);
816   } else if(size < 1024 * 1024) {
817     static const Size ka = 1024;
818     result += functions::asString("%u Kb", size / ka);
819   } else {
820     static const Size mega = 1024 * 1024;
821     result += functions::asString("%u Mb", size / mega);
822   }
823
824   return result;
825 }
826
827 StorageArea::Instance* StorageArea::allocate()
828 throw() {
829   Instance* result = NULL;
830
831   if((result = reuse()) == NULL) {
832     if((result = a_currentBlock->getInstance()) == NULL) {
833       if(++ a_indexBlock == a_blocks.size())
834         a_blocks.push_back(a_currentBlock = new Block(a_objectAllocator, a_maxSize));
835       else
836         a_currentBlock = a_blocks [a_indexBlock];
837
838       result = a_currentBlock->getInstance();
839     }
840   }
841
842   result->copyCounter = 1;
843 //xxx   result->msHoleTime = 0;
844   result->flags = Flag::None;
845 //xxx   result->hasHole = false;
846   return result;
847 }
848
849 //-----------------------------------------------------------------------------------------------------
850 // Reusa un objeto que lleva demasiado tiempo sin ser utilizado.
851 //
852 // (0) Si el tiempo que lleva en la lista de objetos liberados es cero => Podemos usar el re-usado
853 // rapido; ya ha sido eliminado del directorio, invoca al destroy, etc, etc.
854 //     (0.1) Para asegurar que NO desborde los 32 bits.
855 // (1) Cuando n -> Nmax => Talpha = now => a poco tiempo que pase sin reusar los registros, estos
856 // se comienzan a resuar. Cuando n -> 0 => talpha = 0 => No se reusan registros, sino que se crearan
857 // nuevos.
858 // (2) Si el primero de los registros disponibles no es suficientemente antiguo => no hay huecos libres
859 //-----------------------------------------------------------------------------------------------------
860 StorageArea::Instance* StorageArea::reuse()
861 throw() {
862   if(a_holes.empty() == true)
863     return NULL;
864
865   Instance* result = NULL;
866   Instance* front = a_holes.front();
867   bool quickReuse;
868
869   if((front->flags & Flag::Ready) == false) {
870     if(a_directory.size() >= a_maxSize) {   // El directorio contiene los que tienen cuenta 0 + los que están activos.
871       result = front;
872       iterator ii = a_directory.find(result->object->getIndex());
873
874       if(ii != a_directory.end())
875         a_directory.erase(ii);
876
877       if((result->flags & Flag::Empty) == 0) {
878         LOGINFORMATION(
879           string msg("Destroy (reuse) | ");
880           msg += asString();
881           msg += " | ";
882           msg += asString(result);
883           Logger::information(msg, ANNA_FILE_LOCATION);
884         );
885         result->object->destroy();
886         result->flags &= Flag::NoEmpty;
887       }
888     }
889
890     quickReuse = false;
891   } else {
892     // Si la entrada se cargó en los huecos como "usado rápido" se toma
893     result = front;
894     quickReuse = true;
895   }
896
897   if(result != NULL) {
898     a_doneReuse ++;
899     result->flags &= Flag::NoReady;
900     result->flags &= Flag::NoHasHole;
901     a_holes.pop_front();
902   }
903
904   LOGDEBUG(
905     string msg("dbos::StorageArea::reuse | ");
906     msg += asString();
907     msg += " | ";
908     msg += StorageArea::asString(result);
909     msg += functions::asText(" | QuickReuse: ", quickReuse);
910     Logger::debug(msg, ANNA_FILE_LOCATION);
911   );
912   return result;
913 }
914
915 std::string StorageArea::asString(const Instance* instance)
916 throw() {
917   std::string result("Instance { ");
918
919   if(instance == NULL)
920     return result += "<null> } ";
921
922   result += "Reference: ";
923   result += functions::asHexString(anna_ptrnumber_cast(instance->object));
924   result += " | Index: ";
925   result += functions::asHexString(instance->object->getIndex());
926   result += functions::asText(" | Stored: ", instance->object->a_isStored);
927   result += " | CopyCounter: ";
928   result += functions::asString((unsigned int) instance->copyCounter);
929   result += " | Flags (";
930   result += functions::asHexString(instance->flags);
931   result += "):";
932
933   if(instance->flags == Flag::None)
934     result += " None";
935   else {
936     if(instance->flags & Flag::Dirty)
937       result += " Dirty";
938
939     if(instance->flags & Flag::Incoherent)
940       result += " Incoherent";
941
942     if(instance->flags & Flag::Empty)
943       result += " Empty";
944
945     if(instance->flags & Flag::HasHole)
946       result += " HasHole";
947
948     if(instance->flags & Flag::InProgress)
949       result += " InProgress";
950
951     result += (instance->flags & Flag::Ready) ? " Ready" : " TimeWait";
952   }
953
954   return result += " }";
955 }
956
957 const char* StorageArea::AccessMode::asString(const AccessMode::_v v)
958 throw() {
959   static const char* text [] = { "ReadOnly", "ReadWrite", "ReadEver" };
960   return text [v];
961 }
962
963 /****************************************************************************************
964 * Bloque de memoria.
965 ****************************************************************************************/
966 StorageArea::Block::Block(ObjectAllocator objectAllocator, const Size maxSize) :
967   a_size(0) {
968   a_maxSize = std::min(8U, std::max(256U, maxSize >> 7));
969   a_instances = new Instance [a_maxSize];
970
971   for(int i = 0; i < a_maxSize; i ++)
972     a_instances [i].object = (*objectAllocator)();
973 }
974
975 bool StorageArea::Holes::insert(Instance* instance, const StorageArea::Holes::Mode::_v mode)
976 throw() {
977   if(instance->copyCounter > 0)
978     return false;
979
980   /* Si la instancia ya ha sido registrada en la lista de huecos, sale sin más */
981   if((instance->flags & Flag::HasHole) == true)
982     return true;
983
984   switch(mode)  {
985   case Mode::ReadyToReuse:
986     instance->holeIterator = a_holes.insert(a_holes.begin(), instance);
987     instance->flags |= Flag::HasHole;
988     instance->flags |= Flag::Ready;
989     a_size ++;
990     break;
991   case Mode::TimeWait:
992     instance->holeIterator = a_holes.insert(a_holes.end(), instance);
993     instance->flags |= Flag::HasHole;
994     instance->flags &= Flag::NoReady;
995     a_size ++;
996     break;
997   }
998
999   LOGLOCAL6(
1000     string msg("dbos::StorageArea::Holes::insert | This: ");
1001     msg += functions::asHexString(anna_ptrnumber_cast(this));
1002     msg += " | ";
1003     msg += StorageArea::asString(instance);
1004     Logger::write(Logger::Local6, msg, ANNA_FILE_LOCATION);
1005   );
1006   return true;
1007 }
1008
1009 void StorageArea::Holes::erase(Instance* instance)
1010 throw() {
1011   //   instance->msHoleTime = 0;
1012   instance->flags |= Flag::Ready;
1013
1014   if(instance->copyCounter != 0)
1015     return;
1016
1017   /* Si la instancia NO ha sido registrada en la lista de huecos, sale sin más */
1018   if((instance->flags & Flag::HasHole) == false)
1019     return;
1020
1021   LOGLOCAL6(
1022     string msg("dbos::StorageArea::Holes::erase | This: ");
1023     msg += functions::asHexString(anna_ptrnumber_cast(this));
1024     msg += " | ";
1025     msg += StorageArea::asString(instance);
1026     Logger::write(Logger::Local6, msg, ANNA_FILE_LOCATION);
1027   );
1028   a_holes.erase(instance->holeIterator);
1029   instance->flags &= Flag::NoHasHole;
1030   a_size --;
1031 }
1032
1033 /****************************************************************************************
1034 * Iterador
1035
1036 class A {
1037 public:
1038    A () : a (0) { cout << "C0: " << (int) this << endl; }
1039    A (const int _a): a (_a) { cout << "C1: " << (int) this << " " << a << endl; }
1040    A (const A& other) : a (other.a) { cout << "C2: " << (int) this << " " << a << endl; }
1041
1042    A& operator = (const A& other) {
1043       cout << "CP: " << (int) this << " " << (a = other.a) << endl;
1044       return *this;
1045    }
1046
1047    int get () const { return a; }
1048 private:
1049    int a;
1050 };
1051
1052 A fx () { return A (2000); }
1053
1054 int main ()
1055 {
1056    A aa = 100;
1057    A bb (200);
1058    A xx = aa;
1059    A cc = fx ();
1060
1061    cout << "CC: " << (int) &cc << " " << cc.get () << endl;
1062
1063 }
1064
1065 La salida del programucho es la siguiente:
1066
1067 C1: -4198808 100
1068 C1: -4198812 200
1069 C2: -4198816 100
1070 C1: -4198820 2000
1071 CC: -4198820 2000
1072
1073 Lo que quiere decir que la "cc" la crea directamente sobre la pila y la asigna en fx sin aplicar ningn
1074 otro constructor.
1075
1076 Por eso cuando hac� StorageArea::Iterator ii = xxx->begin (), maxi = xxx->end (); .....
1077 no estaba pasando por el contructor copia ni por ningn otro constructor.
1078
1079 ****************************************************************************************/
1080 // Dejo todo el ejemplo para que sirva de recordatorio.
1081