diff --git a/doc/Changelog b/doc/Changelog index 0031d40a0..f6727a0c1 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -3,6 +3,7 @@ - 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. diff --git a/testcode/checklocks.c b/testcode/checklocks.c index dda3be2fa..445dbaa79 100644 --- a/testcode/checklocks.c +++ b/testcode/checklocks.c @@ -51,6 +51,11 @@ */ #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. */ @@ -326,10 +331,11 @@ checklock_destroy(enum check_lock_type type, struct checked_lock** lock, /* 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 */ diff --git a/testcode/checklocks.h b/testcode/checklocks.h index 72ad9ef04..500bb9fc3 100644 --- a/testcode/checklocks.h +++ b/testcode/checklocks.h @@ -71,10 +71,6 @@ /******************* THREAD DEBUG ************************/ #include -/** 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 */ diff --git a/testcode/unitlruhash.c b/testcode/unitlruhash.c index a6af77758..72138f0e9 100644 --- a/testcode/unitlruhash.c +++ b/testcode/unitlruhash.c @@ -72,6 +72,7 @@ static hashvalue_t myhash(int id) {return (hashvalue_t)id & 0x0f;} /** 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; @@ -82,6 +83,7 @@ static struct testkey* newkey(int id) { 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; } @@ -102,6 +104,7 @@ test_bin_find_entry(struct lruhash* table) 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 */ @@ -488,7 +491,9 @@ void lruhash_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 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); diff --git a/testcode/unitmain.c b/testcode/unitmain.c index c51537716..479023507 100644 --- a/testcode/unitmain.c +++ b/testcode/unitmain.c @@ -111,7 +111,7 @@ dname_to_buf(ldns_buffer* b, const char* str) 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; } @@ -150,6 +150,7 @@ main(int argc, char* argv[]) alloc_test(); msgreply_test(); lruhash_test(); + slabhash_test(); checklock_stop(); printf("%d tests succeeded\n", testcount); return 0; diff --git a/testcode/unitmain.h b/testcode/unitmain.h index ae0b37057..220d757de 100644 --- a/testcode/unitmain.h +++ b/testcode/unitmain.h @@ -49,5 +49,7 @@ extern int testcount; /** unit test lruhashtable implementation */ void lruhash_test(); +/** unit test slabhashtable implementation */ +void slabhash_test(); #endif /* TESTCODE_UNITMAIN_H */ diff --git a/testcode/unitslabhash.c b/testcode/unitslabhash.c new file mode 100644 index 000000000..e6bf7bbde --- /dev/null +++ b/testcode/unitslabhash.c @@ -0,0 +1,415 @@ +/* + * 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; isize; 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; iid == 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); +} diff --git a/util/storage/lruhash.c b/util/storage/lruhash.c index ba00fc494..3db99d82b 100644 --- a/util/storage/lruhash.c +++ b/util/storage/lruhash.c @@ -1,5 +1,5 @@ /* - * 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. * @@ -329,9 +329,10 @@ lruhash_insert(struct lruhash* table, hashvalue_t hash, /* 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; } } @@ -363,6 +364,7 @@ lruhash_remove(struct lruhash* table, hashvalue_t hash, void* key) { struct lruhash_entry* entry; struct lruhash_bin* bin; + void *d; lock_quick_lock(&table->lock); bin = &table->array[hash & table->size_mask]; @@ -382,8 +384,9 @@ lruhash_remove(struct lruhash* table, hashvalue_t hash, void* key) 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); } diff --git a/util/storage/lruhash.h b/util/storage/lruhash.h index c8df86501..76a939cf2 100644 --- a/util/storage/lruhash.h +++ b/util/storage/lruhash.h @@ -1,5 +1,5 @@ /* - * 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. * diff --git a/util/storage/slabhash.c b/util/storage/slabhash.c new file mode 100644 index 000000000..312e68810 --- /dev/null +++ b/util/storage/slabhash.c @@ -0,0 +1,133 @@ +/* + * 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; isize; 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; isize; 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; isize; i++) { + snprintf(num, sizeof(num), "table %d", i); + lruhash_status(sl->array[i], num, extended); + } +} diff --git a/util/storage/slabhash.h b/util/storage/slabhash.h new file mode 100644 index 000000000..2e9a74029 --- /dev/null +++ b/util/storage/slabhash.h @@ -0,0 +1,137 @@ +/* + * 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 */