You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
97 lines
3.1 KiB
97 lines
3.1 KiB
// Weak hash tables with 1 key and a value
|
|
|
|
#ifndef _CL_HASH1WEAK_H
|
|
#define _CL_HASH1WEAK_H
|
|
|
|
#include "cl_hash1.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 hash1,
|
|
// - key1_type must be a subclass of cl_gc[object|pointer],
|
|
// - value_type must be a subclass of cl_[gc|rc][object|pointer],
|
|
// - function maygc_htentry(const cl_htentry1<key1_type,value_type>&);
|
|
// must be filled in at runtime.
|
|
|
|
template <class key1_type, class value_type>
|
|
struct cl_heap_weak_hashtable_1 : public cl_heap_hashtable_1 <key1_type,value_type> {
|
|
// 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_htentry1<key1_type,value_type>&);
|
|
// Constructor.
|
|
cl_heap_weak_hashtable_1 (cl_boolean (*maygc_htentry) (const cl_htentry1<key1_type,value_type>&))
|
|
: cl_heap_hashtable_1 <key1_type,value_type> (),
|
|
_maygc_htentry (maygc_htentry)
|
|
{
|
|
_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_1* ht = (cl_heap_weak_hashtable_1*)_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_htentry1<key1_type,value_type>& 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.key);
|
|
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_1* ht = (cl_heap_weak_hashtable_1*)_ht;
|
|
// Now ht->_garcol_fun = garcol_nexttime.
|
|
ht->_garcol_fun = garcol;
|
|
return cl_false;
|
|
}
|
|
};
|
|
|
|
} // namespace cln
|
|
|
|
#endif /* _CL_HASH1WEAK_H */
|