busy with lruhash.

git-svn-id: file:///svn/unbound/trunk@175 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-03-13 16:22:24 +00:00
parent 09db7f94ed
commit 8fb3bb8bef
7 changed files with 528 additions and 1 deletions

View file

@ -50,7 +50,7 @@ LINTFLAGS+="-DBN_ULONG=unsigned long" -Dkrb5_int32=int "-Dkrb5_ui_4=unsigned int
INSTALL=$(srcdir)/install-sh
COMMON_SRC=$(wildcard services/*.c util/*.c util/data/*.c) util/configparser.c util/configlexer.c testcode/checklocks.c
COMMON_SRC=$(wildcard services/*.c util/*.c util/data/*.c util/storage/*.c) util/configparser.c util/configlexer.c testcode/checklocks.c
COMMON_OBJ=$(addprefix $(BUILD),$(COMMON_SRC:.c=.o))
COMPAT_OBJ=$(addprefix $(BUILD)compat/,$(LIBOBJS))
UNITTEST_SRC=testcode/unitmain.c $(COMMON_SRC)

View file

@ -1,3 +1,7 @@
13 March 2007: Wouter
- lock_unprotect in checklocks.
- util/storage/lruhash.h for LRU hash table structure.
12 March 2007: Wouter
- configure.ac moved to 0.2.
- query_info and replymsg util/data structure.

View file

@ -132,6 +132,31 @@ lock_protect(void *p, void* area, size_t size)
LOCKRET(pthread_mutex_unlock(&lock->lock));
}
/** remove protected region */
void
lock_unprotect(void* mangled, void* area)
{
struct checked_lock* lock = *(struct checked_lock**)mangled;
struct protected_area* p, **prevp;
if(!lock)
return;
acquire_locklock(lock, __func__, __FILE__, __LINE__);
p = lock->prot;
prevp = &lock->prot;
while(p) {
if(p->region == area) {
*prevp = p->next;
free(p->hold);
free(p);
LOCKRET(pthread_mutex_unlock(&lock->lock));
return;
}
prevp = &p->next;
p = p->next;
}
LOCKRET(pthread_mutex_unlock(&lock->lock));
}
/**
* Check protected memory region. Memory compare. Exit on error.
* @param lock: which lock to check.

View file

@ -182,6 +182,14 @@ struct checked_lock {
*/
void lock_protect(void* lock, void* area, size_t size);
/**
* Remove protected area from lock.
* No need to call this when deleting the lock.
* @param lock: the lock, any type, (struct checked_lock**).
* @param area: pointer to memory.
*/
void lock_unprotect(void* lock, void* area);
/**
* Initialise checklock. Sets up internal debug structures.
*/

View file

@ -81,6 +81,7 @@
#else /* USE_THREAD_DEBUG */
#define lock_protect(lock, area, size) /* nop */
#define lock_unprotect(lock, area) /* nop */
#define checklock_start() /* nop */
#define checklock_stop() /* nop */

214
util/storage/lruhash.c Normal file
View file

@ -0,0 +1,214 @@
/*
* util/storage/lrulash.c - hashtable, hash function, LRU keeping.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains a hashtable with LRU keeping of entries.
*
*/
#include "config.h"
#include "util/storage/lruhash.h"
/* ------ local helper functions ------------- */
/** delete the hash bin and entries inside it */
static void bin_delete(struct lruhash* table, struct lruhash_bin* bin);
/** find entry in hash bin. You must have locked the bin.
* @param table: hash table with function pointers.
* @param bin: hash bin to look into.
* @param hash: hash value to look for.
* @param key: key to look for.
* @return: the entry or NULL if not found.
*/
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.
*/
static void reclaim_space(struct lruhash* table, size_t howmuch);
/**
* Grow the table lookup array. Becomes twice as large.
* Caller must hold the hash table lock.
* @param table: hash table.
* @return false on error (malloc).
*/
static int table_grow(struct lruhash* table);
/* ------ end local helper functions --------- */
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,
void* arg)
{
struct lruhash* table = (struct lruhash*)calloc(1,
sizeof(struct lruhash));
size_t i;
if(!table)
return NULL;
lock_quick_init(&table->lock);
table->sizefunc = sizefunc;
table->compfunc = compfunc;
table->delkeyfunc = delkeyfunc;
table->deldatafunc = deldatafunc;
table->cb_arg = arg;
table->size = start_size;
table->size_mask = start_size-1;
table->lru_start = NULL;
table->lru_end = NULL;
table->num = 0;
table->space_used = 0;
table->space_max = maxmem;
table->array = calloc(table->size, sizeof(struct lruhash_bin));
if(!table->array) {
lock_quick_destroy(&table->lock);
free(table);
return NULL;
}
for(i=0; i<table->size; i++) {
lock_quick_init(&table->array[i].lock);
}
lock_protect(&table->lock, table, sizeof(*table));
lock_protect(&table->lock, table->array,
table->size*sizeof(struct lruhash_bin));
return table;
}
static void bin_delete(struct lruhash* table, struct lruhash_bin* bin)
{
struct lruhash_entry* p, *np;
if(!bin)
return;
lock_quick_destroy(&bin->lock);
p = bin->overflow_list;
bin->overflow_list = NULL;
while(p) {
np = p->overflow_next;
(*table->delkeyfunc)(p->key, table->cb_arg);
(*table->deldatafunc)(p->data, table->cb_arg);
p = np;
}
}
void lruhash_delete(struct lruhash* table)
{
size_t i;
if(!table)
return;
/* delete lock on hashtable to force check its OK */
lock_quick_destroy(&table->lock);
for(i=0; i<table->size; i++)
bin_delete(table, &table->array[i]);
free(table->array);
free(table);
}
static void
reclaim_space(struct lruhash* table, size_t howmuch)
{
}
static struct lruhash_entry*
bin_find_entry(struct lruhash* table,
struct lruhash_bin* bin, hashvalue_t hash, void* key)
{
return NULL;
}
static int table_grow(struct lruhash* table)
{
return 0;
}
void lruhash_insert(struct lruhash* table, hashvalue_t hash,
struct lruhash_entry* entry, void* data)
{
struct lruhash_bin* bin;
struct lruhash_entry* found;
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 */
bin = &table->array[hash & table->size_mask];
lock_quick_lock(&bin->lock);
/* see if entry exists already */
if(!(found=bin_find_entry(table, bin, hash, entry->key))) {
/* if not: add to bin */
entry->overflow_next = bin->overflow_list;
bin->overflow_list = 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;
}
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. */
}
lock_quick_unlock(&table->lock);
}
struct lruhash_entry* lruhash_lookup(struct lruhash* table, void* key, int wr)
{
return NULL;
}
void lruhash_remove(struct lruhash* table, void* key)
{
}

275
util/storage/lruhash.h Normal file
View file

@ -0,0 +1,275 @@
/*
* util/storage/lrulash.h - hashtable, hash function, LRU keeping.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains a hashtable with LRU keeping of entries.
*
* The hash table keeps a maximum memory size. Old entries are removed
* to make space for new entries.
*
* The locking strategy is as follows:
* o since (almost) every read also implies a LRU update, the
* hashtable lock is a spinlock, not rwlock.
* o the idea is to move every thread through the hash lock quickly,
* so that the next thread can access the lookup table.
* o User performs hash function.
*
* For read:
* o lock hashtable.
* o lookup hash bin.
* o lock hash bin.
* o find entry (if failed, unlock hash, unl bin, exit).
* o swizzle pointers for LRU update.
* o unlock hashtable.
* o lock entry (rwlock).
* o unlock hash bin.
* o work on entry.
* o unlock entry.
*
* To update an entry, gain writelock and change the entry.
* (the entry must keep the same hashvalue, so a data update.)
* (you cannot upgrade a readlock to a writelock, because the item may
* be deleted, it would cause race conditions. So instead, unlock and
* relookup it in the hashtable.)
*
* To delete an entry:
* o unlock the entry if you hold the lock already.
* o lock hashtable.
* o lookup hash bin.
* o lock hash bin.
* o find entry (if failed, unlock hash, unl bin, exit).
* o remove entry from hashtable bin overflow chain.
* o unlock hashtable.
* o lock entry (writelock).
* o unlock hash bin.
* o unlock entry (nobody else should be waiting for this lock,
* since you removed it from hashtable, and you got writelock while
* holding the hashbinlock so you are the only one.)
* Note you are only allowed to obtain a lock while holding hashbinlock.
* o delete entry.
*
* The above sequence is:
* o race free, works with read, write and delete.
* o but has a queue, imagine someone needing a writelock on an item.
* but there are still readlocks. The writelocker waits, but holds
* the hashbinlock. The next thread that comes in and needs the same
* hashbin will wait for the lock while holding the hashtable lock.
* thus halting the entire system on hashtable.
* This is because of the delete protection.
* Readlocks will be easier on the rwlock on entries.
* While the writer is holding writelock, similar problems happen with
* a reader or writer needing the same item.
* the scenario requires more than three threads.
* o so the queue length is 3 threads in a bad situation. The fourth is
* unable to use the hashtable.
*/
#ifndef UTIL_STORAGE_LRUHASH_H
#define UTIL_STORAGE_LRUHASH_H
#include "util/locks.h"
struct lruhash_bin;
struct lruhash_entry;
/** default start size for hash arrays */
#define HASH_DEFAULT_STARTARRAY 1024 /* entries in array */
/** default max memory for hash arrays */
#define HASH_DEFAULT_MAXMEM 4*1024*1024 /* bytes */
/** the type of a hash value */
typedef uint32_t hashvalue_t;
/**
* Type of function that calculates the size of an entry.
* Result must include the size of struct lruhash_entry.
* Keys that are identical must also calculate to the same size.
* size = func(key, data).
*/
typedef size_t (*lruhash_sizefunc_t)(void*, void*);
/** type of function that compares two keys. return 0 if equal. */
typedef int (*lruhash_compfunc_t)(void*, void*);
/** old keys is deleted. This function is called: func(key, userarg) */
typedef void (*lruhash_delkeyfunc_t)(void*, void*);
/** old data is deleted. This function is called: func(data, userarg). */
typedef void (*lruhash_deldatafunc_t)(void*, void*);
/**
* Hash table that keeps LRU list of entries.
*/
struct lruhash {
/** lock for exclusive access, to the lookup array */
lock_quick_t lock;
/** the size function for entries in this table */
lruhash_sizefunc_t sizefunc;
/** the compare function for entries in this table. */
lruhash_compfunc_t compfunc;
/** how to delete keys. */
lruhash_delkeyfunc_t delkeyfunc;
/** how to delete data. */
lruhash_deldatafunc_t deldatafunc;
/** user argument for user functions */
void* cb_arg;
/** the size of the lookup array */
size_t size;
/** size bitmask - since size is a power of 2 */
int size_mask;
/** lookup array of bins */
struct lruhash_bin* array;
/** the lru list, start and end, noncyclical double linked list. */
struct lruhash_entry* lru_start;
/** lru list end item (least recently used) */
struct lruhash_entry* lru_end;
/** the number of entries in the hash table. */
size_t num;
/** the amount of space used, roughly the number of bytes in use. */
size_t space_used;
/** the amount of space the hash table is maximally allowed to use. */
size_t space_max;
};
/**
* A single bin with a linked list of entries in it.
*/
struct lruhash_bin {
/**
* Lock for exclusive access to the linked list
* This lock makes deletion of items safe in this overflow list.
*/
lock_quick_t lock;
/** linked list of overflow entries */
struct lruhash_entry* overflow_list;
};
/**
* An entry into the hash table.
* To change overflow_next you need to hold the bin lock.
* To change the lru items you need to hold the hashtable lock.
* This structure is designed as part of key struct. And key pointer helps
* to get the surrounding structure. Data should be allocated on its own.
*/
struct lruhash_entry {
/**
* rwlock for access to the contents of the entry
* Note that it does _not_ cover the lru_ and overflow_ ptrs.
* Even with a writelock, you cannot change hash and key.
* You need to delete it to change hash or key.
*/
lock_rw_t lock;
/** next entry in overflow chain */
struct lruhash_entry* overflow_next;
/** next entry in lru chain */
struct lruhash_entry* lru_next;
/** prev entry in lru chain */
struct lruhash_entry* lru_prev;
/** hash value of the key */
hashvalue_t hash;
/** key */
void* key;
/** data */
void* data;
};
/**
* Create new hash table.
* @param start_size: size of hashtable array at start, must be power of 2.
* @param maxmem: maximum amount of memory this table is allowed to use.
* @param sizefunc: calculates memory usage of entries.
* @param compfunc: compares entries, 0 on equality.
* @param delkeyfunc: deletes key.
* Calling both delkey and deldata will also free the struct lruhash_entry.
* Make it part of the key structure and delete it in delkeyfunc.
* @param deldatafunc: deletes data.
* @param arg: user argument that is passed to user function calls.
* @return: new hash table or NULL on malloc failure.
*/
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,
void* arg);
/**
* Delete hash table. Entries are all deleted.
* @param table: to delete.
*/
void lruhash_delete(struct lruhash* table);
/**
* Insert a new element into the hashtable.
* If key is already present data pointer in that entry is updated.
* The space calculation function is called with the key, data.
* If necessary the least recently used entries are deleted to make space.
* If necessary the hash array is grown up.
*
* @param table: hash table.
* @param hash: hash value. User calculates the hash.
* @param entry: identifies the entry.
* If key already present, this entry->key is deleted immediately.
* But entry->data is set to NULL before deletion, and put into
* the existing entry. The data is then freed.
* @param data: the data.
*/
void lruhash_insert(struct lruhash* table, hashvalue_t hash,
struct lruhash_entry* entry, void* data);
/**
* Lookup an entry in the hashtable.
* 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 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.
* with a writelock you can update the data part.
* @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);
/**
* Remove entry from hashtable. Does nothing if not found in hashtable.
* Delfunc is called for the entry.
* @param table: hash table.
* @param key: what to look for.
*/
void lruhash_remove(struct lruhash* table, void* key);
#endif /* UTIL_STORAGE_LRUHASH_H */