// Weak hash tables with 2 keys and a value #ifndef _CL_HASH2WEAK_H #define _CL_HASH2WEAK_H #include "cl_hash2.h" namespace cln { // This is a hash table in which an entry can be removed when a user-defined // condition is fulfilled (e.g. the value is not referenced any more). // We don't remove unused entries immediately, only when the hash table // wants to grow. This way the hash table also serves as a cache. // Requirements: // - same as for hash2, // - key1_type and key2_type must be subclasses of cl_gc[object|pointer], // - value_type must be a subclass of cl_[gc|rc][object|pointer], // - function maygc_htentry(const cl_htentry2&); // must be filled in at runtime. template struct cl_heap_weak_hashtable_2 : public cl_heap_hashtable_2 { // Allocation. void* operator new (size_t size) { return malloc_hook(size); } // Deallocation. void operator delete (void* ptr) { free_hook(ptr); } public: // Function which tells when an unused entry may be garbage collected. cl_boolean (* const _maygc_htentry) (const cl_htentry2&); // Constructor. cl_heap_weak_hashtable_2 (cl_boolean (*maygc_htentry) (const cl_htentry2&)) : cl_heap_hashtable_2 (), _maygc_htentry (maygc_htentry) { this->_garcol_fun = garcol; } private: // Garbage collection. // Before growing the table, we check whether we can remove unused // entries. static cl_boolean garcol (cl_heap* _ht) { var cl_heap_weak_hashtable_2* ht = (cl_heap_weak_hashtable_2*)_ht; // Now ht->_garcol_fun = garcol. // It is not worth doing a garbage collection if the table // is small, say, has fewer than 100 entries. if (ht->_count < 100) return cl_false; // Do a garbage collection. var long removed = 0; for (long i = 0; i < ht->_size; i++) if (ht->_entries[i].next >= 0) { var cl_htentry2& entry = ht->_entries[i].entry; if (ht->_maygc_htentry(entry)) { // This is hairy. We remove the entry and // free the value after its refcount has // dropped to zero. But in order to protect // against too early destruction // we have to temporarily increase the refcount. if (entry.val.pointer_p()) entry.val.inc_pointer_refcount(); ht->remove(entry.key1,entry.key2); if (entry.val.pointer_p()) { var cl_heap* p = entry.val.heappointer; if (!(--p->refcount == 0)) cl_abort(); cl_free_heap_object(p); } removed++; } } if (removed == 0) // Unsuccessful. Let the table grow immediately. return cl_false; else if (2*removed < ht->_count) { // Table shrank by less than a factor of 1/1.5. // Don't expand the table now, but expand it next time. ht->_garcol_fun = garcol_nexttime; return cl_true; } else { // Table shrank much. Don't expand the table now, // and try a GC next time. return cl_true; } } static cl_boolean garcol_nexttime (cl_heap* _ht) { var cl_heap_weak_hashtable_2* ht = (cl_heap_weak_hashtable_2*)_ht; // Now ht->_garcol_fun = garcol_nexttime. ht->_garcol_fun = cl_heap_weak_hashtable_2::garcol; return cl_false; } }; } // namespace cln #endif /* _CL_HASH2WEAK_H */