// Weak hash tables for making objects unique #ifndef _CL_HASHUNIQWEAK_H #define _CL_HASHUNIQWEAK_H #include "cl_hashuniq.h" // This is a hashuniq table in which an entry can be removed when the // value is not referenced any more. // Best example: string -> symbol uniquification. When a symbol is not // referenced any more (except from the hash table itself), the (string,symbol) // pair may be removed from the hash table. // 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 hashuniq, // - value_type must be a subclass of cl_[gc|rc][object|pointer]. // Note that since the reference counts are compared against 1, it doesn't // make sense to have more than one weak hash table for the same value_type. template struct cl_heap_weak_hashtable_uniq : public cl_heap_hashtable_uniq { // Allocation. void* operator new (size_t size) { return cl_malloc_hook(size); } // Deallocation. void operator delete (void* ptr) { cl_free_hook(ptr); } public: // Constructor. cl_heap_weak_hashtable_uniq () : cl_heap_hashtable_uniq () { _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_uniq* ht = (cl_heap_weak_hashtable_uniq*)_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 value_type& v = ht->_entries[i].entry.val; if (!v.pointer_p() || (v.heappointer->refcount == 1)) { // 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 (depending on // how the C++ compiler optimizes hashkey()) // we have to temporarily increase the refcount. if (v.pointer_p()) v.inc_pointer_refcount(); ht->remove(hashkey(v)); if (v.pointer_p()) { var cl_heap* p = v.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_uniq* ht = (cl_heap_weak_hashtable_uniq*)_ht; // Now ht->_garcol_fun = garcol_nexttime. ht->_garcol_fun = garcol; return cl_false; } }; #endif /* _CL_HASHUNIQWEAK_H */