mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
lruhash insert and lookup.
git-svn-id: file:///svn/unbound/trunk@176 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
8fb3bb8bef
commit
3f61aca920
3 changed files with 264 additions and 38 deletions
|
|
@ -1,3 +1,6 @@
|
|||
14 March 2007: Wouter
|
||||
- hash table insert (and subroutines) and lookup implemented.
|
||||
|
||||
13 March 2007: Wouter
|
||||
- lock_unprotect in checklocks.
|
||||
- util/storage/lruhash.h for LRU hash table structure.
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@
|
|||
|
||||
/* ------ local helper functions ------------- */
|
||||
|
||||
/** init the hash bins for the table. */
|
||||
static void bin_init(struct lruhash_bin* array, size_t size);
|
||||
|
||||
/** delete the hash bin and entries inside it */
|
||||
static void bin_delete(struct lruhash* table, struct lruhash_bin* bin);
|
||||
|
||||
|
|
@ -59,24 +62,82 @@ static struct lruhash_entry* bin_find_entry(struct lruhash* table,
|
|||
struct lruhash_bin* bin, hashvalue_t hash, void* key);
|
||||
|
||||
/**
|
||||
* Try to make howmuch space available for a new entry into the table.
|
||||
* works by deleting old entries.
|
||||
* Assumes that the lock on the hashtable is being held by caller.
|
||||
* @param table: hash table.
|
||||
* @param howmuch: will try to make max-used at least this amount.
|
||||
* Remove entry from bin overflow chain.
|
||||
* You must have locked the bin.
|
||||
* @param bin: hash bin to look into.
|
||||
* @param entry: entry ptr that needs removal.
|
||||
*/
|
||||
static void reclaim_space(struct lruhash* table, size_t howmuch);
|
||||
static void bin_overflow_remove(struct lruhash_bin* bin,
|
||||
struct lruhash_entry* entry);
|
||||
|
||||
/**
|
||||
* Split hash bin into two new ones. Based on increased size_mask.
|
||||
* Caller must hold hash table lock.
|
||||
* At the end the routine acquires all hashbin locks (in the old array).
|
||||
* This makes it wait for other threads to finish with the bins.
|
||||
* So the bins are ready to be deleted after this function.
|
||||
* @param table: hash table with function pointers.
|
||||
* @param newa: new increased array.
|
||||
* @param newmask: new lookup mask.
|
||||
*/
|
||||
static void bin_split(struct lruhash* table, struct lruhash_bin* newa,
|
||||
int newmask);
|
||||
|
||||
/**
|
||||
* Try to make space available by deleting old entries.
|
||||
* Assumes that the lock on the hashtable is being held by caller.
|
||||
* Caller must not hold bin locks.
|
||||
* @param table: hash table.
|
||||
* @param list: list of entries that are to be deleted later.
|
||||
* Entries have been removed from the hash table and writelock is held.
|
||||
*/
|
||||
static void reclaim_space(struct lruhash* table, struct lruhash_entry** list);
|
||||
|
||||
/**
|
||||
* Grow the table lookup array. Becomes twice as large.
|
||||
* Caller must hold the hash table lock.
|
||||
* Caller must hold the hash table lock. Must not hold any bin locks.
|
||||
* Tries to grow, on malloc failure, nothing happened.
|
||||
* @param table: hash table.
|
||||
* @return false on error (malloc).
|
||||
*/
|
||||
static int table_grow(struct lruhash* table);
|
||||
static void table_grow(struct lruhash* table);
|
||||
|
||||
/**
|
||||
* Touch entry, so it becomes the most recently used in the LRU list.
|
||||
* Caller must hold hash table lock. The entry must be inserted already.
|
||||
* @param table: hash table.
|
||||
* @param entry: entry to make first in LRU.
|
||||
*/
|
||||
static void lru_touch(struct lruhash* table, struct lruhash_entry* entry);
|
||||
|
||||
/**
|
||||
* Put entry at front of lru. entry must be unlinked from lru.
|
||||
* Caller must hold hash table lock.
|
||||
* @param table: hash table with lru head and tail.
|
||||
* @param entry: entry to make most recently used.
|
||||
*/
|
||||
static void lru_front(struct lruhash* table, struct lruhash_entry* entry);
|
||||
|
||||
/**
|
||||
* Remove entry from lru list.
|
||||
* Caller must hold hash table lock.
|
||||
* @param table: hash table with lru head and tail.
|
||||
* @param entry: entry to remove from lru.
|
||||
*/
|
||||
static void lru_remove(struct lruhash* table, struct lruhash_entry* entry);
|
||||
|
||||
/* ------ end local helper functions --------- */
|
||||
|
||||
static void
|
||||
bin_init(struct lruhash_bin* array, size_t size)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<size; i++) {
|
||||
lock_quick_init(&array[i].lock);
|
||||
lock_protect(&array[i].lock, &array[i],
|
||||
sizeof(struct lruhash_bin));
|
||||
}
|
||||
}
|
||||
|
||||
struct lruhash* lruhash_create(size_t start_size, size_t maxmem,
|
||||
lruhash_sizefunc_t sizefunc, lruhash_compfunc_t compfunc,
|
||||
lruhash_delkeyfunc_t delkeyfunc, lruhash_deldatafunc_t deldatafunc,
|
||||
|
|
@ -84,7 +145,6 @@ struct lruhash* lruhash_create(size_t start_size, size_t maxmem,
|
|||
{
|
||||
struct lruhash* table = (struct lruhash*)calloc(1,
|
||||
sizeof(struct lruhash));
|
||||
size_t i;
|
||||
if(!table)
|
||||
return NULL;
|
||||
lock_quick_init(&table->lock);
|
||||
|
|
@ -94,7 +154,7 @@ struct lruhash* lruhash_create(size_t start_size, size_t maxmem,
|
|||
table->deldatafunc = deldatafunc;
|
||||
table->cb_arg = arg;
|
||||
table->size = start_size;
|
||||
table->size_mask = start_size-1;
|
||||
table->size_mask = (int)(start_size-1);
|
||||
table->lru_start = NULL;
|
||||
table->lru_end = NULL;
|
||||
table->num = 0;
|
||||
|
|
@ -106,9 +166,7 @@ struct lruhash* lruhash_create(size_t start_size, size_t maxmem,
|
|||
free(table);
|
||||
return NULL;
|
||||
}
|
||||
for(i=0; i<table->size; i++) {
|
||||
lock_quick_init(&table->array[i].lock);
|
||||
}
|
||||
bin_init(table->array, table->size);
|
||||
lock_protect(&table->lock, table, sizeof(*table));
|
||||
lock_protect(&table->lock, table->array,
|
||||
table->size*sizeof(struct lruhash_bin));
|
||||
|
|
@ -131,6 +189,34 @@ static void bin_delete(struct lruhash* table, struct lruhash_bin* bin)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bin_split(struct lruhash* table, struct lruhash_bin* newa,
|
||||
int newmask)
|
||||
{
|
||||
size_t i;
|
||||
struct lruhash_entry *p, *np;
|
||||
int newbin;
|
||||
/* move entries to new table. Notice that since hash x is mapped to
|
||||
* bin x & mask, and new mask uses one more bit, so all entries in
|
||||
* one bin will go into the old bin or bin | newbit */
|
||||
/* newbit = newmask - table->size_mask; */
|
||||
/* so, really, this task could also be threaded, per bin. */
|
||||
/* LRU list is not changed */
|
||||
for(i=0; i<table->size; i++)
|
||||
{
|
||||
lock_quick_lock(&table->array[i].lock);
|
||||
p = table->array[i].overflow_list;
|
||||
while(p) {
|
||||
np = p->overflow_next;
|
||||
/* link into correct new bin */
|
||||
newbin = p->hash & newmask;
|
||||
p->overflow_next = newa[newbin].overflow_list;
|
||||
newa[newbin].overflow_list = p;
|
||||
p=np;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lruhash_delete(struct lruhash* table)
|
||||
{
|
||||
size_t i;
|
||||
|
|
@ -145,35 +231,146 @@ void lruhash_delete(struct lruhash* table)
|
|||
}
|
||||
|
||||
static void
|
||||
reclaim_space(struct lruhash* table, size_t howmuch)
|
||||
bin_overflow_remove(struct lruhash_bin* bin, struct lruhash_entry* entry)
|
||||
{
|
||||
struct lruhash_entry* p = bin->overflow_list;
|
||||
struct lruhash_entry** prevp = &bin->overflow_list;
|
||||
while(p) {
|
||||
if(p == entry) {
|
||||
*prevp = p->overflow_next;
|
||||
return;
|
||||
}
|
||||
prevp = &p->overflow_next;
|
||||
p = p->overflow_next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
reclaim_space(struct lruhash* table, struct lruhash_entry** list)
|
||||
{
|
||||
struct lruhash_entry* d;
|
||||
struct lruhash_bin* bin;
|
||||
log_assert(table);
|
||||
/* does not delete MRU entry, so table will not be empty. */
|
||||
while(table->num > 1 && table->space_used > table->space_max) {
|
||||
/* notice that since we hold the hashtable lock, nobody
|
||||
can change the lru chain. So it cannot be deleted underneath
|
||||
us. We still need the hashbin and entry write lock to make
|
||||
sure we flush all users away from the entry.
|
||||
which is unlikely, since it is LRU, if someone got a rdlock
|
||||
it would be moved to front, but to be sure. */
|
||||
d = table->lru_end;
|
||||
/* specialised, delete from end of double linked list,
|
||||
and we know num>1, so there is a previous lru entry. */
|
||||
log_assert(d && d->lru_prev);
|
||||
table->lru_end = d->lru_prev;
|
||||
d->lru_prev->lru_next = NULL;
|
||||
/* schedule entry for deletion */
|
||||
bin = &table->array[d->hash & table->size_mask];
|
||||
table->num --;
|
||||
lock_quick_lock(&bin->lock);
|
||||
bin_overflow_remove(bin, d);
|
||||
d->overflow_next = *list;
|
||||
*list = d;
|
||||
lock_rw_wrlock(&d->lock);
|
||||
table->space_used -= table->sizefunc(d->key, d->data);
|
||||
lock_quick_unlock(&bin->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static struct lruhash_entry*
|
||||
bin_find_entry(struct lruhash* table,
|
||||
struct lruhash_bin* bin, hashvalue_t hash, void* key)
|
||||
{
|
||||
struct lruhash_entry* p = bin->overflow_list;
|
||||
while(p) {
|
||||
if(p->hash == hash && table->compfunc(p->key, key) == 0)
|
||||
return p;
|
||||
p = p->overflow_next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int table_grow(struct lruhash* table)
|
||||
static void
|
||||
table_grow(struct lruhash* table)
|
||||
{
|
||||
return 0;
|
||||
struct lruhash_bin* newa;
|
||||
int newmask;
|
||||
size_t i;
|
||||
if(table->size_mask == (int)(((size_t)-1)>>1)) {
|
||||
log_err("hash array malloc: size_t too small");
|
||||
return;
|
||||
}
|
||||
/* try to allocate new array, if not fail */
|
||||
newa = calloc(table->size*2, sizeof(struct lruhash_bin));
|
||||
if(!newa) {
|
||||
log_err("hash grow: malloc failed");
|
||||
/* continue with smaller array. Though its slower. */
|
||||
return;
|
||||
}
|
||||
bin_init(newa, table->size*2);
|
||||
newmask = (table->size_mask << 1) | 1;
|
||||
bin_split(table, newa, newmask);
|
||||
/* delete the old bins */
|
||||
lock_unprotect(&table->lock, table->array);
|
||||
for(i=0; i<table->size; i++) {
|
||||
lock_quick_unlock(&table->array[i].lock);
|
||||
lock_quick_destroy(&table->array[i].lock);
|
||||
}
|
||||
free(table->array);
|
||||
|
||||
table->size *= 2;
|
||||
table->size_mask = newmask;
|
||||
table->array = newa;
|
||||
lock_protect(&table->lock, table->array,
|
||||
table->size*sizeof(struct lruhash_bin));
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
lru_front(struct lruhash* table, struct lruhash_entry* entry)
|
||||
{
|
||||
entry->lru_prev = NULL;
|
||||
entry->lru_next = table->lru_start;
|
||||
if(!table->lru_start)
|
||||
table->lru_end = entry;
|
||||
else table->lru_start->lru_prev = entry;
|
||||
table->lru_start = entry;
|
||||
}
|
||||
|
||||
static void
|
||||
lru_remove(struct lruhash* table, struct lruhash_entry* entry)
|
||||
{
|
||||
if(entry->lru_prev)
|
||||
entry->lru_prev->lru_next = entry->lru_next;
|
||||
else table->lru_start = entry->lru_next;
|
||||
if(entry->lru_next)
|
||||
entry->lru_next->lru_prev = entry->lru_prev;
|
||||
else table->lru_end = entry->lru_prev;
|
||||
}
|
||||
|
||||
static void
|
||||
lru_touch(struct lruhash* table, struct lruhash_entry* entry)
|
||||
{
|
||||
log_assert(table && entry);
|
||||
if(entry == table->lru_start)
|
||||
return; /* nothing to do */
|
||||
/* remove from current lru position */
|
||||
lru_remove(table, entry);
|
||||
/* add at front */
|
||||
lru_front(table, entry);
|
||||
}
|
||||
|
||||
void lruhash_insert(struct lruhash* table, hashvalue_t hash,
|
||||
struct lruhash_entry* entry, void* data)
|
||||
{
|
||||
struct lruhash_bin* bin;
|
||||
struct lruhash_entry* found;
|
||||
struct lruhash_entry* found, *reclaimlist=NULL;
|
||||
size_t need_size;
|
||||
lock_quick_lock(&table->lock);
|
||||
/* see if table memory exceeded and needs clipping. */
|
||||
need_size = table->sizefunc(entry->key, data);
|
||||
if(table->space_used + need_size > table->space_max)
|
||||
reclaim_space(table, need_size);
|
||||
|
||||
/* find bin */
|
||||
lock_quick_lock(&table->lock);
|
||||
bin = &table->array[hash & table->size_mask];
|
||||
lock_quick_lock(&bin->lock);
|
||||
|
||||
|
|
@ -182,31 +379,55 @@ void lruhash_insert(struct lruhash* table, hashvalue_t hash,
|
|||
/* if not: add to bin */
|
||||
entry->overflow_next = bin->overflow_list;
|
||||
bin->overflow_list = entry;
|
||||
lru_front(table, entry);
|
||||
table->num++;
|
||||
table->space_used += need_size;
|
||||
} else {
|
||||
/* if so: update data */
|
||||
table->space_used += need_size -
|
||||
(*table->sizefunc)(found->key, found->data);
|
||||
entry->data = NULL;
|
||||
(*table->delkeyfunc)(entry->key, table->cb_arg);
|
||||
(*table->deldatafunc)(found->data, table->cb_arg);
|
||||
found->data = data;
|
||||
lru_touch(table, found);
|
||||
}
|
||||
lock_quick_unlock(&bin->lock);
|
||||
|
||||
/* see if table lookup must be grown up */
|
||||
if(table->num == table->size) {
|
||||
if(!table_grow(table))
|
||||
log_err("hash grow: malloc failed");
|
||||
/* continue with smaller array. Slower. */
|
||||
}
|
||||
if(table->space_used > table->space_max)
|
||||
reclaim_space(table, &reclaimlist);
|
||||
if(table->num >= table->size)
|
||||
table_grow(table);
|
||||
lock_quick_unlock(&table->lock);
|
||||
|
||||
/* finish reclaim if any (outside of critical region) */
|
||||
while(reclaimlist) {
|
||||
struct lruhash_entry* n = reclaimlist->overflow_next;
|
||||
lock_rw_unlock(&reclaimlist->lock);
|
||||
(*table->delkeyfunc)(reclaimlist->key, table->cb_arg);
|
||||
(*table->deldatafunc)(reclaimlist->data, table->cb_arg);
|
||||
reclaimlist = n;
|
||||
}
|
||||
}
|
||||
|
||||
struct lruhash_entry* lruhash_lookup(struct lruhash* table, void* key, int wr)
|
||||
struct lruhash_entry* lruhash_lookup(struct lruhash* table, hashvalue_t hash,
|
||||
void* key, int wr)
|
||||
{
|
||||
return NULL;
|
||||
struct lruhash_entry* entry = NULL;
|
||||
struct lruhash_bin* bin;
|
||||
|
||||
lock_quick_lock(&table->lock);
|
||||
bin = &table->array[hash & table->size_mask];
|
||||
lock_quick_lock(&bin->lock);
|
||||
if((entry=bin_find_entry(table, bin, hash, key)))
|
||||
lru_touch(table, entry);
|
||||
lock_quick_unlock(&table->lock);
|
||||
|
||||
if(entry) {
|
||||
if(wr)
|
||||
lock_rw_wrlock(&entry->lock);
|
||||
else lock_rw_rdlock(&entry->lock);
|
||||
}
|
||||
lock_quick_unlock(&bin->lock);
|
||||
return entry;
|
||||
}
|
||||
|
||||
void lruhash_remove(struct lruhash* table, void* key)
|
||||
|
|
|
|||
|
|
@ -194,13 +194,13 @@ struct lruhash_entry {
|
|||
* You need to delete it to change hash or key.
|
||||
*/
|
||||
lock_rw_t lock;
|
||||
/** next entry in overflow chain */
|
||||
/** next entry in overflow chain. Covered by hashlock and binlock. */
|
||||
struct lruhash_entry* overflow_next;
|
||||
/** next entry in lru chain */
|
||||
/** next entry in lru chain. covered by hashlock. */
|
||||
struct lruhash_entry* lru_next;
|
||||
/** prev entry in lru chain */
|
||||
/** prev entry in lru chain. covered by hashlock. */
|
||||
struct lruhash_entry* lru_prev;
|
||||
/** hash value of the key */
|
||||
/** hash value of the key. It may not change, until entry deleted. */
|
||||
hashvalue_t hash;
|
||||
/** key */
|
||||
void* key;
|
||||
|
|
@ -255,6 +255,7 @@ void lruhash_insert(struct lruhash* table, hashvalue_t hash,
|
|||
* At the end of the function you hold a (read/write)lock on the entry.
|
||||
* The LRU is updated for the entry (if found).
|
||||
* @param table: hash table.
|
||||
* @param hash: hash of key.
|
||||
* @param key: what to look for, compared against entries in overflow chain.
|
||||
* the hash value must be set, and must work with compare function.
|
||||
* @param wr: set to true if you desire a writelock on the entry.
|
||||
|
|
@ -262,7 +263,8 @@ void lruhash_insert(struct lruhash* table, hashvalue_t hash,
|
|||
* @return: pointer to the entry or NULL. The entry is locked.
|
||||
* The user must unlock the entry when done.
|
||||
*/
|
||||
struct lruhash_entry* lruhash_lookup(struct lruhash* table, void* key, int wr);
|
||||
struct lruhash_entry* lruhash_lookup(struct lruhash* table, hashvalue_t hash,
|
||||
void* key, int wr);
|
||||
|
||||
/**
|
||||
* Remove entry from hashtable. Does nothing if not found in hashtable.
|
||||
|
|
|
|||
Loading…
Reference in a new issue