- covered up memory leak of the entry locks.
- answers from the cache correctly. Copies flags correctly.
- sanity check for incoming query replies.
+ - slabbed hash table. Much nicer contention, need dual cpu to see.
22 March 2007: Wouter
- AIX configure check.
*/
#ifdef USE_THREAD_DEBUG
+/** How long to wait before lock attempt is a failure. */
+#define CHECK_LOCK_TIMEOUT 15 /* seconds */
+/** How long to wait before join attempt is a failure. */
+#define CHECK_JOIN_TIMEOUT 120 /* seconds */
+
/** if key has been created */
static int key_created = 0;
/** we hide the thread debug info with this key. */
/* contention, look at fraction in trouble. */
if(e->history_count > 1 &&
1000*e->contention_count/e->history_count > contention_interest) {
- log_info("lock created %s %s %d has contention %u of %u",
+ log_info("lock created %s %s %d has contention %u of %u (%d%%)",
e->create_func, e->create_file, e->create_line,
(unsigned int)e->contention_count,
- (unsigned int)e->history_count);
+ (unsigned int)e->history_count,
+ 100*e->contention_count/e->history_count);
}
/* delete it */
/******************* THREAD DEBUG ************************/
#include <pthread.h>
-/** How long to wait before lock attempt is a failure. */
-#define CHECK_LOCK_TIMEOUT 5 /* seconds */
-/** How long to wait before join attempt is a failure. */
-#define CHECK_JOIN_TIMEOUT 120 /* seconds */
/** How many threads to allocate for */
#define THRDEBUG_MAX_THREADS 32 /* threads */
/** do we check locking order */
/** allocate new key, fill in hash. */
static struct testkey* newkey(int id) {
struct testkey* k = (struct testkey*)calloc(1, sizeof(struct testkey));
+ if(!k) fatal_exit("out of memory");
k->id = id;
k->entry.hash = myhash(id);
k->entry.key = k;
static struct testdata* newdata(int val) {
struct testdata* d = (struct testdata*)calloc(1,
sizeof(struct testdata));
+ if(!d) fatal_exit("out of memory");
d->data = val;
return d;
}
struct testkey* k4 = newkey(12 + 1024*2);
hashvalue_t h = myhash(12);
struct lruhash_bin bin;
+ memset(&bin, 0, sizeof(bin));
bin_init(&bin, 1);
/* remove from empty list */
{
/* start very very small array, so it can do lots of table_grow() */
/* also small in size so that reclaim has to be done quickly. */
- struct lruhash* table = lruhash_create(2, 4096,
+ struct lruhash* table ;
+ printf("lruhash test\n");
+ table = lruhash_create(2, 4096,
test_sizefunc, test_compfunc, test_delkey, test_deldata, NULL);
test_bin_find_entry(table);
test_lru(table);
if(status != LDNS_STATUS_OK)
fatal_exit("%s ldns: %s", __func__,
ldns_get_errorstr_by_id(status));
- ldns_rdf_free(rdf);
+ ldns_rdf_deep_free(rdf);
ldns_buffer_flip(b);
return b;
}
alloc_test();
msgreply_test();
lruhash_test();
+ slabhash_test();
checklock_stop();
printf("%d tests succeeded\n", testcount);
return 0;
/** unit test lruhashtable implementation */
void lruhash_test();
+/** unit test slabhashtable implementation */
+void slabhash_test();
#endif /* TESTCODE_UNITMAIN_H */
--- /dev/null
+/*
+ * testcode/unitslabhash.c - unit test for slabhash table.
+ *
+ * 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
+ * Tests the locking LRU keeping hash table implementation.
+ */
+
+#include "config.h"
+#include "testcode/unitmain.h"
+#include "util/log.h"
+#include "util/storage/slabhash.h"
+
+/* --- test representation --- */
+/** structure contains test key */
+struct slabtestkey {
+ /** the key id */
+ int id;
+ /** the entry */
+ struct lruhash_entry entry;
+};
+/** structure contains test data */
+struct slabtestdata {
+ /** data value */
+ int data;
+};
+
+/** sizefunc for lruhash */
+static size_t test_sizefunc(void*, void*);
+/** comparefunc for lruhash */
+static int test_compfunc(void*, void*);
+/** delkey for lruhash */
+static void test_delkey(void*, void*);
+/** deldata for lruhash */
+static void test_deldata(void*, void*);
+/* --- end test representation --- */
+
+/** hash func, very bad to improve collisions, both high and low bits. */
+static hashvalue_t myhash(int id) {
+ hashvalue_t h = (hashvalue_t)id & 0x0f;
+ h |= (h << 28);
+ return h;
+}
+
+/** allocate new key, fill in hash. */
+static struct slabtestkey* newkey(int id) {
+ struct slabtestkey* k = (struct slabtestkey*)calloc(1, sizeof(struct slabtestkey));
+ if(!k) fatal_exit("out of memory");
+ k->id = id;
+ k->entry.hash = myhash(id);
+ k->entry.key = k;
+ lock_rw_init(&k->entry.lock);
+ return k;
+}
+/** new data el */
+static struct slabtestdata* newdata(int val) {
+ struct slabtestdata* d = (struct slabtestdata*)calloc(1,
+ sizeof(struct slabtestdata));
+ if(!d) fatal_exit("out of memory");
+ d->data = val;
+ return d;
+}
+/** delete key */
+static void delkey(struct slabtestkey* k) {
+ lock_rw_destroy(&k->entry.lock); free(k);}
+/** delete data */
+static void deldata(struct slabtestdata* d) {free(d);}
+
+/** test hashtable using short sequence */
+static void
+test_short_table(struct slabhash* table)
+{
+ struct slabtestkey* k = newkey(12);
+ struct slabtestkey* k2 = newkey(14);
+ struct slabtestdata* d = newdata(128);
+ struct slabtestdata* d2 = newdata(129);
+
+ k->entry.data = d;
+ k2->entry.data = d2;
+
+ slabhash_insert(table, myhash(12), &k->entry, d);
+ slabhash_insert(table, myhash(14), &k2->entry, d2);
+
+ unit_assert( slabhash_lookup(table, myhash(12), k, 0) == &k->entry);
+ lock_rw_unlock( &k->entry.lock );
+ unit_assert( slabhash_lookup(table, myhash(14), k2, 0) == &k2->entry);
+ lock_rw_unlock( &k2->entry.lock );
+ slabhash_remove(table, myhash(12), k);
+ slabhash_remove(table, myhash(14), k2);
+}
+
+/** number of hash test max */
+#define HASHTESTMAX 100
+
+/** test adding a random element */
+static void
+testadd(struct slabhash* table, struct slabtestdata* ref[])
+{
+ int numtoadd = random() % HASHTESTMAX;
+ struct slabtestdata* data = newdata(numtoadd);
+ struct slabtestkey* key = newkey(numtoadd);
+ key->entry.data = data;
+ slabhash_insert(table, myhash(numtoadd), &key->entry, data);
+ ref[numtoadd] = data;
+}
+
+/** test adding a random element */
+static void
+testremove(struct slabhash* table, struct slabtestdata* ref[])
+{
+ int num = random() % HASHTESTMAX;
+ struct slabtestkey* key = newkey(num);
+ slabhash_remove(table, myhash(num), key);
+ ref[num] = NULL;
+ delkey(key);
+}
+
+/** test adding a random element */
+static void
+testlookup(struct slabhash* table, struct slabtestdata* ref[])
+{
+ int num = random() % HASHTESTMAX;
+ struct slabtestkey* key = newkey(num);
+ struct lruhash_entry* en = slabhash_lookup(table, myhash(num), key, 0);
+ struct slabtestdata* data = en? (struct slabtestdata*)en->data : NULL;
+ if(en) {
+ unit_assert(en->key);
+ unit_assert(en->data);
+ }
+ if(0) log_info("lookup %d got %d, expect %d", num, en? data->data :-1,
+ ref[num]? ref[num]->data : -1);
+ unit_assert( data == ref[num] );
+ if(en) lock_rw_unlock(&en->lock);
+ delkey(key);
+}
+
+/** check integrity of hash table */
+static void
+check_lru_table(struct lruhash* table)
+{
+ struct lruhash_entry* p;
+ size_t c = 0;
+ lock_quick_lock(&table->lock);
+ unit_assert( table->num <= table->size);
+ unit_assert( table->size_mask == (int)table->size-1 );
+ unit_assert( (table->lru_start && table->lru_end) ||
+ (!table->lru_start && !table->lru_end) );
+ unit_assert( table->space_used <= table->space_max );
+ /* check lru list integrity */
+ if(table->lru_start)
+ unit_assert(table->lru_start->lru_prev == NULL);
+ if(table->lru_end)
+ unit_assert(table->lru_end->lru_next == NULL);
+ p = table->lru_start;
+ while(p) {
+ if(p->lru_prev) {
+ unit_assert(p->lru_prev->lru_next == p);
+ }
+ if(p->lru_next) {
+ unit_assert(p->lru_next->lru_prev == p);
+ }
+ c++;
+ p = p->lru_next;
+ }
+ unit_assert(c == table->num);
+
+ /* this assertion is specific to the unit test */
+ unit_assert( table->space_used ==
+ table->num * test_sizefunc(NULL, NULL) );
+ lock_quick_unlock(&table->lock);
+}
+
+/** check integrity of hash table */
+static void
+check_table(struct slabhash* table)
+{
+ size_t i;
+ for(i=0; i<table->size; i++)
+ check_lru_table(table->array[i]);
+}
+
+/** test adding a random element (unlimited range) */
+static void
+testadd_unlim(struct slabhash* table, struct slabtestdata** ref)
+{
+ int numtoadd = random() % (HASHTESTMAX * 10);
+ struct slabtestdata* data = newdata(numtoadd);
+ struct slabtestkey* key = newkey(numtoadd);
+ key->entry.data = data;
+ slabhash_insert(table, myhash(numtoadd), &key->entry, data);
+ if(ref)
+ ref[numtoadd] = data;
+}
+
+/** test adding a random element (unlimited range) */
+static void
+testremove_unlim(struct slabhash* table, struct slabtestdata** ref)
+{
+ int num = random() % (HASHTESTMAX*10);
+ struct slabtestkey* key = newkey(num);
+ slabhash_remove(table, myhash(num), key);
+ if(ref)
+ ref[num] = NULL;
+ delkey(key);
+}
+
+/** test adding a random element (unlimited range) */
+static void
+testlookup_unlim(struct slabhash* table, struct slabtestdata** ref)
+{
+ int num = random() % (HASHTESTMAX*10);
+ struct slabtestkey* key = newkey(num);
+ struct lruhash_entry* en = slabhash_lookup(table, myhash(num), key, 0);
+ struct slabtestdata* data = en? (struct slabtestdata*)en->data : NULL;
+ if(en) {
+ unit_assert(en->key);
+ unit_assert(en->data);
+ }
+ if(0 && ref) log_info("lookup unlim %d got %d, expect %d", num, en ?
+ data->data :-1, ref[num] ? ref[num]->data : -1);
+ if(data && ref) {
+ /* its okay for !data, it fell off the lru */
+ unit_assert( data == ref[num] );
+ }
+ if(en) lock_rw_unlock(&en->lock);
+ delkey(key);
+}
+
+/** test with long sequence of adds, removes and updates, and lookups */
+static void
+test_long_table(struct slabhash* table)
+{
+ /* assuming it all fits in the hastable, this check will work */
+ struct slabtestdata* ref[HASHTESTMAX * 100];
+ size_t i;
+ memset(ref, 0, sizeof(ref));
+ /* test assumption */
+ if(0) slabhash_status(table, "unit test", 1);
+ srandom(48);
+ for(i=0; i<1000; i++) {
+ /* what to do? */
+ switch(random() % 4) {
+ case 0:
+ case 3:
+ testadd(table, ref);
+ break;
+ case 1:
+ testremove(table, ref);
+ break;
+ case 2:
+ testlookup(table, ref);
+ break;
+ default:
+ unit_assert(0);
+ }
+ if(0) slabhash_status(table, "unit test", 1);
+ check_table(table);
+ }
+
+ /* test more, but 'ref' assumption does not hold anymore */
+ for(i=0; i<1000; i++) {
+ /* what to do? */
+ switch(random() % 4) {
+ case 0:
+ case 3:
+ testadd_unlim(table, ref);
+ break;
+ case 1:
+ testremove_unlim(table, ref);
+ break;
+ case 2:
+ testlookup_unlim(table, ref);
+ break;
+ default:
+ unit_assert(0);
+ }
+ if(0) slabhash_status(table, "unlim", 1);
+ check_table(table);
+ }
+}
+
+/** structure to threaded test the lru hash table */
+struct slab_test_thr {
+ /** thread num, first entry. */
+ int num;
+ /** id */
+ ub_thread_t id;
+ /** hash table */
+ struct slabhash* table;
+};
+
+/** main routine for threaded hash table test */
+static void*
+test_thr_main(void* arg)
+{
+ struct slab_test_thr* t = (struct slab_test_thr*)arg;
+ int i;
+ log_thread_set(&t->num);
+ for(i=0; i<1000; i++) {
+ switch(random() % 4) {
+ case 0:
+ case 3:
+ testadd_unlim(t->table, NULL);
+ break;
+ case 1:
+ testremove_unlim(t->table, NULL);
+ break;
+ case 2:
+ testlookup_unlim(t->table, NULL);
+ break;
+ default:
+ unit_assert(0);
+ }
+ if(0) slabhash_status(t->table, "hashtest", 1);
+ if(i % 100 == 0) /* because of locking, not all the time */
+ check_table(t->table);
+ }
+ check_table(t->table);
+ return NULL;
+}
+
+/** test hash table access by multiple threads. */
+static void
+test_threaded_table(struct slabhash* table)
+{
+ int numth = 10;
+ struct slab_test_thr t[100];
+ int i;
+
+ for(i=1; i<numth; i++) {
+ t[i].num = i;
+ t[i].table = table;
+ ub_thread_create(&t[i].id, test_thr_main, &t[i]);
+ }
+
+ for(i=1; i<numth; i++) {
+ ub_thread_join(t[i].id);
+ }
+ if(0) slabhash_status(table, "hashtest", 1);
+}
+
+void slabhash_test()
+{
+ /* start very very small array, so it can do lots of table_grow() */
+ /* also small in size so that reclaim has to be done quickly. */
+ struct slabhash* table;
+ printf("slabhash test\n");
+ table = slabhash_create(4, 2, 4096,
+ test_sizefunc, test_compfunc, test_delkey, test_deldata, NULL);
+ test_short_table(table);
+ test_long_table(table);
+ slabhash_delete(table);
+ table = slabhash_create(4, 2, 4096,
+ test_sizefunc, test_compfunc, test_delkey, test_deldata, NULL);
+ test_threaded_table(table);
+ slabhash_delete(table);
+}
+
+static size_t test_sizefunc(void* ATTR_UNUSED(key), void* ATTR_UNUSED(data))
+{
+ return sizeof(struct slabtestkey) + sizeof(struct slabtestdata);
+}
+
+static int test_compfunc(void* key1, void* key2)
+{
+ struct slabtestkey* k1 = (struct slabtestkey*)key1;
+ struct slabtestkey* k2 = (struct slabtestkey*)key2;
+ if(k1->id == k2->id)
+ return 0;
+ if(k1->id > k2->id)
+ return 1;
+ return -1;
+}
+
+static void test_delkey(void* key, void* ATTR_UNUSED(arg))
+{
+ delkey((struct slabtestkey*)key);
+}
+
+static void test_deldata(void* data, void* ATTR_UNUSED(arg))
+{
+ deldata((struct slabtestdata*)data);
+}
/*
- * util/storage/lrulash.c - hashtable, hash function, LRU keeping.
+ * util/storage/lruhash.c - hashtable, hash function, LRU keeping.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
/* finish reclaim if any (outside of critical region) */
while(reclaimlist) {
struct lruhash_entry* n = reclaimlist->overflow_next;
+ void* d = reclaimlist->data;
lock_rw_unlock(&reclaimlist->lock);
(*table->delkeyfunc)(reclaimlist->key, table->cb_arg);
- (*table->deldatafunc)(reclaimlist->data, table->cb_arg);
+ (*table->deldatafunc)(d, table->cb_arg);
reclaimlist = n;
}
}
{
struct lruhash_entry* entry;
struct lruhash_bin* bin;
+ void *d;
lock_quick_lock(&table->lock);
bin = &table->array[hash & table->size_mask];
lock_quick_unlock(&bin->lock);
/* finish removal */
lock_rw_unlock(&entry->lock);
+ d = entry->data;
(*table->delkeyfunc)(entry->key, table->cb_arg);
- (*table->deldatafunc)(entry->data, table->cb_arg);
+ (*table->deldatafunc)(d, table->cb_arg);
}
/*
- * util/storage/lrulash.h - hashtable, hash function, LRU keeping.
+ * util/storage/lruhash.h - hashtable, hash function, LRU keeping.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
--- /dev/null
+/*
+ * util/storage/slabhash.c - hashtable consisting of several smaller tables.
+ *
+ * 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
+ *
+ * Implementation of hash table that consists of smaller hash tables.
+ * It cannot grow, but that gives it the ability to have multiple
+ * locks. Also this means there are multiple LRU lists.
+ */
+
+#include "config.h"
+#include "util/storage/slabhash.h"
+
+struct slabhash* slabhash_create(size_t numtables, 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)
+{
+ size_t i;
+ struct slabhash* sl = (struct slabhash*)calloc(1,
+ sizeof(struct slabhash));
+ if(!sl) return NULL;
+ sl->size = numtables;
+ log_assert(sl->size > 0);
+ sl->array = (struct lruhash**)calloc(sl->size, sizeof(struct lruhash*));
+ if(!sl->array) {
+ free(sl);
+ return NULL;
+ }
+ sl->mask = (uint32_t)(sl->size - 1);
+ if(sl->mask == 0) {
+ sl->shift = 0;
+ } else {
+ log_assert( (sl->size & sl->mask) == 0
+ /* size must be power of 2 */ );
+ sl->shift = 0;
+ while(!(sl->mask & 0x80000000)) {
+ sl->mask <<= 1;
+ sl->shift ++;
+ }
+ }
+ for(i=0; i<sl->size; i++) {
+ sl->array[i] = lruhash_create(start_size, maxmem / sl->size,
+ sizefunc, compfunc, delkeyfunc, deldatafunc, arg);
+ if(!sl->array[i]) {
+ slabhash_delete(sl);
+ return NULL;
+ }
+ }
+ return sl;
+}
+
+void slabhash_delete(struct slabhash* sl)
+{
+ if(!sl)
+ return;
+ if(sl->array) {
+ size_t i;
+ for(i=0; i<sl->size; i++)
+ lruhash_delete(sl->array[i]);
+ free(sl->array);
+ }
+ free(sl);
+}
+
+/** helper routine to calculate the slabhash index */
+static unsigned int
+slab_idx(struct slabhash* sl, hashvalue_t hash)
+{
+ return ((hash & sl->mask) >> sl->shift);
+}
+
+void slabhash_insert(struct slabhash* sl, hashvalue_t hash,
+ struct lruhash_entry* entry, void* data)
+{
+ lruhash_insert(sl->array[slab_idx(sl, hash)], hash, entry, data);
+}
+
+struct lruhash_entry* slabhash_lookup(struct slabhash* sl,
+ hashvalue_t hash, void* key, int wr)
+{
+ return lruhash_lookup(sl->array[slab_idx(sl, hash)], hash, key, wr);
+}
+
+void slabhash_remove(struct slabhash* sl, hashvalue_t hash, void* key)
+{
+ lruhash_remove(sl->array[slab_idx(sl, hash)], hash, key);
+}
+
+void slabhash_status(struct slabhash* sl, const char* id, int extended)
+{
+ size_t i;
+ char num[17];
+ log_info("Slabhash %s: %d tables mask=%x shift=%d",
+ id, sl->size, sl->mask, sl->shift);
+ for(i=0; i<sl->size; i++) {
+ snprintf(num, sizeof(num), "table %d", i);
+ lruhash_status(sl->array[i], num, extended);
+ }
+}
--- /dev/null
+/*
+ * util/storage/slabhash.h - hashtable consisting of several smaller tables.
+ *
+ * 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
+ *
+ * Hash table that consists of smaller hash tables.
+ * It cannot grow, but that gives it the ability to have multiple
+ * locks. Also this means there are multiple LRU lists.
+ */
+
+#ifndef UTIL_STORAGE_SLABHASH_H
+#define UTIL_STORAGE_SLABHASH_H
+#include "util/storage/lruhash.h"
+
+/**
+ * Hash table formed from several smaller ones.
+ * None of the data inside the slabhash may be altered.
+ * Therefore, no locks are needed to access the structure.
+ */
+struct slabhash {
+ /** the size of the array - must be power of 2 */
+ size_t size;
+ /** size bitmask - uses high bits. */
+ uint32_t mask;
+ /** shift right this many bits to get index into array. */
+ unsigned int shift;
+ /** lookup array of hash tables */
+ struct lruhash** array;
+};
+
+/**
+ * Create new slabbed hash table.
+ * @param numtables: number of hash tables to use, other parameters used to
+ * initialize these smaller hashtables.
+ * @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.
+ * so every table gets maxmem/numtables to use for itself.
+ * @param sizefunc: calculates memory usage of entries.
+ * @param compfunc: compares entries, 0 on equality.
+ * @param delkeyfunc: deletes key.
+ * @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 slabhash* slabhash_create(size_t numtables, 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 slabhash_delete(struct slabhash* table);
+
+/**
+ * Insert a new element into the hashtable, uses lruhash_insert.
+ * If key is already present data pointer in that entry is updated.
+ *
+ * @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 slabhash_insert(struct slabhash* table, hashvalue_t hash,
+ struct lruhash_entry* entry, void* data);
+
+/**
+ * Lookup an entry in the hashtable. Uses lruhash_lookup.
+ * 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.
+ * 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* slabhash_lookup(struct slabhash* table,
+ hashvalue_t hash, void* key, int wr);
+
+/**
+ * Remove entry from hashtable. Does nothing if not found in hashtable.
+ * Delfunc is called for the entry. Uses lruhash_remove.
+ * @param table: hash table.
+ * @param hash: hash of key.
+ * @param key: what to look for.
+ */
+void slabhash_remove(struct slabhash* table, hashvalue_t hash, void* key);
+
+/**
+ * Output debug info to the log as to state of the hash table.
+ * @param table: hash table.
+ * @param id: string printed with table to identify the hash table.
+ * @param extended: set to true to print statistics on overflow bin lengths.
+ */
+void slabhash_status(struct slabhash* table, const char* id, int extended);
+
+#endif /* UTIL_STORAGE_SLABHASH_H */