]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
busy with lruhash.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 13 Mar 2007 16:22:24 +0000 (16:22 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 13 Mar 2007 16:22:24 +0000 (16:22 +0000)
git-svn-id: file:///svn/unbound/trunk@175 be551aaa-1e26-0410-a405-d3ace91eadb9

Makefile.in
doc/Changelog
testcode/checklocks.c
testcode/checklocks.h
util/locks.h
util/storage/lruhash.c [new file with mode: 0644]
util/storage/lruhash.h [new file with mode: 0644]

index 7f25edd0eb85d2220e94f993199e3810a2269fe8..0c095c631624876129bfc422593c83762901e6f5 100644 (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)
index 983ce0de23b50c29753f281aa1d0f543c9ef540b..121d30648b22e428805e6559ddad05fc6640e608 100644 (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.
index e07ca67b51340aa89a0f5f56d819742e76b50688..dd9f8531d5e09260193e7666f3be5274725efe31 100644 (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.
index 05d494468e80e6eff708a6886f0492c5713303a5..d2f62873a4858be947419337ede3140e6417e38d 100644 (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.
  */
index 36cc1e50ecf146fe2d300703fc145eff9b8e8f4c..34f4f76076e451a0837e71ccd84a5310660e4dbe 100644 (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 */
 
diff --git a/util/storage/lruhash.c b/util/storage/lruhash.c
new file mode 100644 (file)
index 0000000..01a1b33
--- /dev/null
@@ -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)
+{
+}
diff --git a/util/storage/lruhash.h b/util/storage/lruhash.h
new file mode 100644 (file)
index 0000000..f4cddc2
--- /dev/null
@@ -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 */