From: Wouter Wijngaards Date: Tue, 13 Mar 2007 16:22:24 +0000 (+0000) Subject: busy with lruhash. X-Git-Tag: release-0.2~52 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8fb3bb8beffc673c68b030ef8816c470de8bac6f;p=thirdparty%2Funbound.git busy with lruhash. git-svn-id: file:///svn/unbound/trunk@175 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/Makefile.in b/Makefile.in index 7f25edd0e..0c095c631 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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) diff --git a/doc/Changelog b/doc/Changelog index 983ce0de2..121d30648 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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. diff --git a/testcode/checklocks.c b/testcode/checklocks.c index e07ca67b5..dd9f8531d 100644 --- a/testcode/checklocks.c +++ b/testcode/checklocks.c @@ -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. diff --git a/testcode/checklocks.h b/testcode/checklocks.h index 05d494468..d2f62873a 100644 --- a/testcode/checklocks.h +++ b/testcode/checklocks.h @@ -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. */ diff --git a/util/locks.h b/util/locks.h index 36cc1e50e..34f4f7607 100644 --- a/util/locks.h +++ b/util/locks.h @@ -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 */ diff --git a/util/storage/lruhash.c b/util/storage/lruhash.c new file mode 100644 index 000000000..01a1b339e --- /dev/null +++ b/util/storage/lruhash.c @@ -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; isize; 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; isize; 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) +{ +} diff --git a/util/storage/lruhash.h b/util/storage/lruhash.h new file mode 100644 index 000000000..f4cddc2b5 --- /dev/null +++ b/util/storage/lruhash.h @@ -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 */