tpl_hash_cache.H

00001 
00002 /*
00003   This file is part of Aleph system
00004 
00005   Copyright (c) 2002, 2003, 2004, 2005, 2006
00006   UNIVERSITY LOS ANDES (ULA) Merida - REPÚBLICA BOLIVARIANA DE VENEZUELA  
00007 
00008   - Center of Studies in Microelectronics & Distributed Systems (CEMISID) 
00009   - ULA Computer Science Department
00010   - FUNDACITE Mérida - Ministerio de Ciencia y Tecnología
00011 
00012   PERMISSION TO USE, COPY, MODIFY AND DISTRIBUTE THIS SOFTWARE AND ITS
00013   DOCUMENTATION IS HEREBY GRANTED, PROVIDED THAT BOTH THE COPYRIGHT
00014   NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES OF THE
00015   SOFTWARE, DERIVATIVE WORKS OR MODIFIED VERSIONS, AND ANY PORTIONS
00016   THEREOF, AND THAT BOTH NOTICES APPEAR IN SUPPORTING DOCUMENTATION.
00017 
00018   Aleph is distributed in the hope that it will be useful, but WITHOUT
00019   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
00020   or FITNESS FOR A PARTICULAR PURPOSE.
00021 
00022   UNIVERSIDAD DE LOS ANDES requests users of this software to return to 
00023 
00024   Leandro Leon
00025   CEMISID 
00026   Ed La Hechicera 
00027   3er piso, ala sur
00028   Facultad de Ingenieria 
00029   Universidad de Los Andes 
00030   Merida - REPÚBLICA BOLIVARIANA DE VENEZUELA    or
00031 
00032   lrleon@ula.ve
00033 
00034   any improvements or extensions that they make and grant Universidad 
00035   de Los Andes (ULA) the rights to redistribute these changes. 
00036 
00037   Aleph is (or was) granted by: 
00038   - Consejo de Desarrollo Cientifico, Humanistico, Tecnico de la ULA 
00039     (CDCHT)
00040   - Fundacite Mérida
00041 */
00042 
00043 
00044 # ifndef TPL_HASH_CACHE_H
00045 # define TPL_HASH_CACHE_H
00046 
00047 # include <memory>
00048 # include <aleph.H>
00049 # include <tpl_dnode.H>
00050 # include <tpl_lhash.H>
00051 
00069     template <typename  Key, typename Data, class Cmp = Aleph::equal_to<Key> >
00070 class Hash_Cache
00071 {
00072 
00073 public:
00074 
00085   class Cache_Entry : public LhashTable<Key, Cmp>::Bucket
00086   { 
00087 
00088     friend class Hash_Cache<Key, Data>;
00089 
00090     Data    data; 
00091 
00092     Dlink   dlink_lru; // enlace a la cola lru 
00093     Dlink* link_lru() { return &dlink_lru; }
00094     bool locked; // indica si la entrada está trancada
00095     bool is_in_hash_table; // indica si la entrada está contenida dentro de
00096                            // la tabla hash 
00097     void lock() 
00098     { 
00099       if (locked)
00100         throw std::runtime_error("Cache_Entry is already locked");
00101 
00102       locked = true; 
00103     }
00104 
00105     void unlock()
00106     { 
00107       if (not locked)
00108         throw std::runtime_error("Cache_Entry is not locked");
00109 
00110       locked = false; 
00111     }
00112     Dlink   dlink_inside; // enlace a la lista de entradas que 
00113 
00114     Dlink * link_inside() { return &dlink_inside; }
00115 
00116     LINKNAME_TO_TYPE(Cache_Entry, dlink_inside);
00117 
00118   public:
00119     Cache_Entry() : locked(false), is_in_hash_table(false) { /* empty */ }
00120 
00121 
00123     Data & get_data() { return data; }
00125     const bool & is_locked() const { return locked; }
00126 
00128     const bool & is_in_table() const { return is_in_hash_table; }
00129   }; // fin class Cache_Entry
00130 
00131 private:
00132 
00133   LhashTable<Key, Cmp> hash_table;
00134   LINKNAME_TO_TYPE(Cache_Entry, dlink_lru);
00135   Dlink    lru_list; // cabecera de la lista lru
00136   size_t   num_lru;  // número de elementos en lista lru
00137   Dlink  inside_list; // lista de entradas (cubetas) ya apartadas y
00138                       // metidas en la tabla hash
00139 
00140   size_t cache_size;  // tamaño del cache; máximo número de entradas que
00141                       // puede contener  
00142   Dlink  locked_list; // lista de entradas trancadas
00143   size_t num_locked;  // número de elementos trancados
00144   typedef Dnode<Cache_Entry*> Chunk_Descriptor;
00145 
00146   Chunk_Descriptor chunk_list;  
00147   void insert_entry_to_lru_list(Cache_Entry * cache_entry)
00148   {
00149     num_lru++;
00150     lru_list.insert(cache_entry->link_lru());
00151   }
00152 
00153   void remove_entry_from_lru_list(Cache_Entry * cache_entry)
00154   {
00155     num_lru--;
00156     cache_entry->link_lru()->del();
00157   }
00158 
00159   void insert_entry_to_locked_list(Cache_Entry * cache_entry)
00160   {
00161     num_locked++;
00162     locked_list.insert(cache_entry->link_lru());
00163   }
00164 
00165   void remove_entry_from_locked_list(Cache_Entry * cache_entry)
00166   {
00167     num_locked--;
00168     cache_entry->link_lru()->del();
00169   }
00170 
00171   void move_to_inside_front(Cache_Entry * cache_entry)
00172   {
00173     cache_entry->link_inside()->del();
00174     inside_list.insert(cache_entry->link_inside());
00175   }
00176 
00177   void do_mru(Cache_Entry * cache_entry) 
00178   { 
00179     cache_entry->link_lru()->del(); // elimine de su posición actual
00180     lru_list.insert(cache_entry->link_lru()); // colóquelo en el trasero
00181   }
00182   void do_lru(Cache_Entry * cache_entry) 
00183   { 
00184     cache_entry->link_lru()->del(); // elimine de su posición actual
00185     lru_list.append(cache_entry->link_lru()); // insértelo en el frente
00186   }
00187   void remove_entry_from_hash_table(Cache_Entry * cache_entry)
00188   {
00189     cache_entry->link_inside()->del();
00190 
00191     hash_table.remove(cache_entry);
00192     cache_entry->is_in_hash_table = false;
00193     do_lru(cache_entry);
00194   }
00195 
00196   Cache_Entry * get_lru_entry()
00197   {
00198 
00199     if (lru_list.is_empty())   // ¿existe una entrada disponible?
00200       throw std::underflow_error("All entries are locked"); // no ==> ¡excepción! 
00201 
00202         // obtenga la entrada más antigua -la menos recientemente accedida
00203     Dlink * lru_entry_link = lru_list.get_prev();
00204     Cache_Entry * cache_entry = dlink_lru_to_Cache_Entry(lru_entry_link);
00205 
00206         // si cache_entry está contenida en la tabla hay que eliminarlo
00207     if (cache_entry->is_in_hash_table) 
00208       remove_entry_from_hash_table(cache_entry);
00209 
00210     do_mru(cache_entry); // la entrada deviene la más recientemente accedida
00211             
00212     return cache_entry;
00213   }
00214 
00215 public:
00216 
00235   Hash_Cache(size_t         (*hash_fct)(const Key&), 
00236              const size_t & __hash_size,
00237              const size_t & __cache_size) 
00238 
00239     throw (std::exception, std::bad_alloc)
00240 
00241     : hash_table(hash_fct, __hash_size, false),
00242       num_lru(0), cache_size(__cache_size), num_locked(0)
00243   {
00244         // apartar entradas del cache
00245     Cache_Entry * entries_array = new Cache_Entry [cache_size];
00246 
00247     try 
00248     { 
00249 
00250 
00251           // apartar el descriptor del arreglo
00252       std::auto_ptr<Chunk_Descriptor> 
00253         chunk_descriptor (new Chunk_Descriptor (entries_array));
00254 
00255       chunk_list.insert(chunk_descriptor.get());
00256 
00257             // insertar todos los Cache_Entry en la lista lru
00258       for (int i = 0; i < cache_size; i++)
00259         insert_entry_to_lru_list(&entries_array[i]);
00260           
00261       chunk_descriptor.release();
00262 
00263     }
00264     catch (...)
00265       {
00266         delete [] entries_array;
00267         throw;
00268       }
00269 
00270   }
00271 
00272   virtual ~Hash_Cache() 
00273   {
00274         // recorrer lista de bloques y liberarlos
00275     while (not chunk_list.is_empty())
00276       {
00277         Chunk_Descriptor * current_chunk = chunk_list.remove_next();
00278 
00279         delete [] current_chunk->get_data();
00280         delete current_chunk;
00281       }
00282   }      
00283 
00301   Cache_Entry * insert(const Key & key, const Data & data) 
00302   {
00303     Cache_Entry * cache_entry = get_lru_entry(); // obtener entrada más antigua
00304           
00305         // escribirle el par
00306     cache_entry->get_key()  = key;  
00307     cache_entry->get_data() = data;
00308           
00309     inside_list.insert(cache_entry->link_inside()); // insértela en inside_list
00310 
00311         // insertarla en la tabla hash
00312     hash_table.insert(cache_entry);
00313     cache_entry->is_in_hash_table = true;
00314 
00315     return cache_entry;
00316   }
00326   Cache_Entry * search(const Key & key)
00327   {
00328         // buscar en la tabla hash
00329     Cache_Entry * cache_entry = static_cast<Cache_Entry*>(hash_table.search(key));
00330       
00331     if (cache_entry != NULL) // ¿fue encontrada la clave?
00332       {     // sí ==> hacerla la más recientemente usada
00333         do_mru(cache_entry);
00334         move_to_inside_front(cache_entry);
00335       } 
00336       
00337     return cache_entry;
00338   }
00339 
00350   Cache_Entry * search_next(Cache_Entry * cache_entry)
00351   {
00352     Cache_Entry *next_entry = 
00353       static_cast<Cache_Entry*>(hash_table.search_next(cache_entry));
00354       
00355     if (next_entry != NULL)
00356       {
00357         do_mru(next_entry);
00358         move_to_inside_front(cache_entry);
00359       } 
00360       
00361     return next_entry;
00362   }
00363 
00367   void lock_entry(Cache_Entry * cache_entry) 
00368 
00369     throw(std::exception, std::runtime_error, std::domain_error)
00370 
00371   {
00372 
00373     if (cache_entry->is_locked())
00374       throw std::runtime_error("Cache_Entry is already locked");
00375 
00376     if (not cache_entry->is_in_table())
00377       throw std::domain_error("Cache_Entry is not in the cache");
00378 
00379     remove_entry_from_lru_list(cache_entry);
00380     insert_entry_to_locked_list(cache_entry);
00381       
00382     cache_entry->lock();
00383   }
00386   void unlock_entry(Cache_Entry * cache_entry) 
00387 
00388     throw(std::exception, std::runtime_error)
00389 
00390   { 
00391 
00392     if (not cache_entry->is_locked())
00393       throw std::runtime_error("Cache_Entry is not locked");
00394 
00395     remove_entry_from_locked_list(cache_entry);
00396     insert_entry_to_lru_list(cache_entry);
00397 
00398     cache_entry->unlock();
00399   }
00403   void remove(Cache_Entry * cache_entry) 
00404 
00405     throw(std::exception, std::runtime_error, std::domain_error)
00406 
00407   {
00408 
00409     if (cache_entry->is_locked())
00410       throw std::runtime_error("Cache_Entry is already locked");
00411 
00412     if (not cache_entry->is_in_table())
00413       throw std::domain_error("Cache_Entry is not in the cache");
00414 
00415     remove_entry_from_hash_table(cache_entry);
00416   }
00421   void expand(const size_t & plus_size) 
00422 
00423     throw(std::exception, std::range_error, std::bad_alloc)
00424 
00425   {
00426 
00427     if (plus_size == 0)
00428       throw std::range_error ("bad plus_size");
00429 
00430     const size_t new_cache_size = cache_size + plus_size;
00431 
00433     Cache_Entry * entries_array = new Cache_Entry [plus_size];
00434 
00435     try
00436       {
00437 
00438 
00439             // apartar el descriptor
00440         std::auto_ptr<Chunk_Descriptor> 
00441           chunk_descriptor (new Chunk_Descriptor (entries_array));
00442 
00443             // Calcular el nuevo tamaño de la tabla hash y relocalizar sus
00444             // entradas  
00445         const float curr_hash_ratio = 1.0*cache_size/hash_table.capacity();
00446         const size_t new_hash_capacity = new_cache_size/curr_hash_ratio;
00447         hash_table.resize(new_hash_capacity);
00448 
00449             // Meter nuevas entradas del cache en la lista lru
00450         for (int i = 0; i < plus_size; i++)
00451           insert_entry_to_lru_list(&entries_array[i]);
00452              
00453         chunk_list.insert(chunk_descriptor.release());
00454 
00455         cache_size = new_cache_size;
00456 
00457       }
00458     catch (...)
00459       {
00460         delete []  entries_array;
00461         throw;
00462       }
00463 
00464   }
00465 
00466 
00468   const size_t & capacity() const { return cache_size; }
00469 
00471   const size_t & size() const { return hash_table.size(); }
00472 
00476   const size_t & get_num_locked() const { return num_locked; }
00477 
00481   const size_t & get_num_busy_slots() const 
00482   { 
00483     return hash_table.get_num_busy_slots(); 
00484   }
00485 
00487   const size_t & get_hash_capacity() const 
00488   {
00489     return hash_table.capacity();
00490   }
00491 
00495   class Iterator : public Dlink::Iterator
00496   {
00497 
00498   public:
00499 
00501     Iterator(Hash_Cache & cache) 
00502       : Dlink::Iterator(&cache.inside_list) { /* empty */ }
00503 
00505     Cache_Entry * get_current()  
00506     { 
00507       Cache_Entry * ret_val =  
00508         Cache_Entry::dlink_inside_to_Cache_Entry(Dlink::Iterator::get_current()); 
00509 
00510       return ret_val;  
00511     } 
00512   };
00513 };
00514 
00515 # endif // TPL_HASH_CACHE_H 
00516 

Leandro R. León