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