Home | History | Annotate | Line # | Download | only in testcode
      1      1.1  christos /*
      2      1.1  christos  * testcode/unitlruhash.c - unit test for lruhash table.
      3      1.1  christos  *
      4      1.1  christos  * Copyright (c) 2007, NLnet Labs. All rights reserved.
      5      1.1  christos  *
      6      1.1  christos  * This software is open source.
      7      1.1  christos  *
      8      1.1  christos  * Redistribution and use in source and binary forms, with or without
      9      1.1  christos  * modification, are permitted provided that the following conditions
     10      1.1  christos  * are met:
     11      1.1  christos  *
     12      1.1  christos  * Redistributions of source code must retain the above copyright notice,
     13      1.1  christos  * this list of conditions and the following disclaimer.
     14      1.1  christos  *
     15      1.1  christos  * Redistributions in binary form must reproduce the above copyright notice,
     16      1.1  christos  * this list of conditions and the following disclaimer in the documentation
     17      1.1  christos  * and/or other materials provided with the distribution.
     18      1.1  christos  *
     19      1.1  christos  * Neither the name of the NLNET LABS nor the names of its contributors may
     20      1.1  christos  * be used to endorse or promote products derived from this software without
     21      1.1  christos  * specific prior written permission.
     22      1.1  christos  *
     23      1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     24      1.1  christos  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     25      1.1  christos  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     26      1.1  christos  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     27      1.1  christos  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     28      1.1  christos  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
     29      1.1  christos  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     30      1.1  christos  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     31      1.1  christos  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     32      1.1  christos  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     33      1.1  christos  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     34      1.1  christos  *
     35      1.1  christos  */
     36      1.1  christos /**
     37      1.1  christos  * \file
     38      1.1  christos  * Tests the locking LRU keeping hash table implementation.
     39      1.1  christos  */
     40      1.1  christos 
     41      1.1  christos #include "config.h"
     42      1.1  christos #include "testcode/unitmain.h"
     43      1.1  christos #include "util/log.h"
     44      1.1  christos #include "util/storage/lruhash.h"
     45      1.1  christos #include "util/storage/slabhash.h" /* for the test structures */
     46      1.1  christos 
     47      1.1  christos /** use this type for the lruhash test key */
     48  1.1.1.2  christos typedef struct slabhash_testkey testkey_type;
     49      1.1  christos /** use this type for the lruhash test data */
     50  1.1.1.2  christos typedef struct slabhash_testdata testdata_type;
     51      1.1  christos 
     52      1.1  christos /** delete key */
     53      1.1  christos static void delkey(struct slabhash_testkey* k) {
     54      1.1  christos 	lock_rw_destroy(&k->entry.lock); free(k);}
     55      1.1  christos /** delete data */
     56      1.1  christos static void deldata(struct slabhash_testdata* d) {free(d);}
     57      1.1  christos 
     58      1.1  christos /** hash func, very bad to improve collisions */
     59  1.1.1.2  christos static hashvalue_type myhash(int id) {return (hashvalue_type)id & 0x0f;}
     60      1.1  christos /** allocate new key, fill in hash */
     61  1.1.1.2  christos static testkey_type* newkey(int id) {
     62  1.1.1.2  christos 	testkey_type* k = (testkey_type*)calloc(1, sizeof(testkey_type));
     63      1.1  christos 	if(!k) fatal_exit("out of memory");
     64      1.1  christos 	k->id = id;
     65      1.1  christos 	k->entry.hash = myhash(id);
     66      1.1  christos 	k->entry.key = k;
     67      1.1  christos 	lock_rw_init(&k->entry.lock);
     68      1.1  christos 	return k;
     69      1.1  christos }
     70      1.1  christos /** new data el */
     71  1.1.1.2  christos static testdata_type* newdata(int val) {
     72  1.1.1.2  christos 	testdata_type* d = (testdata_type*)calloc(1,
     73  1.1.1.2  christos 		sizeof(testdata_type));
     74      1.1  christos 	if(!d) fatal_exit("out of memory");
     75      1.1  christos 	d->data = val;
     76      1.1  christos 	return d;
     77      1.1  christos }
     78      1.1  christos 
     79      1.1  christos /** test bin_find_entry function and bin_overflow_remove */
     80      1.1  christos static void
     81      1.1  christos test_bin_find_entry(struct lruhash* table)
     82      1.1  christos {
     83  1.1.1.2  christos 	testkey_type* k = newkey(12);
     84  1.1.1.2  christos 	testdata_type* d = newdata(128);
     85  1.1.1.2  christos 	testkey_type* k2 = newkey(12 + 1024);
     86  1.1.1.2  christos 	testkey_type* k3 = newkey(14);
     87  1.1.1.2  christos 	testkey_type* k4 = newkey(12 + 1024*2);
     88  1.1.1.2  christos 	hashvalue_type h = myhash(12);
     89      1.1  christos 	struct lruhash_bin bin;
     90      1.1  christos 	memset(&bin, 0, sizeof(bin));
     91      1.1  christos 	bin_init(&bin, 1);
     92      1.1  christos 
     93      1.1  christos 	/* remove from empty list */
     94      1.1  christos 	bin_overflow_remove(&bin, &k->entry);
     95      1.1  christos 
     96      1.1  christos 	/* find in empty list */
     97  1.1.1.3  christos 	unit_assert( bin_find_entry(table, &bin, h, k, NULL) == NULL );
     98      1.1  christos 
     99      1.1  christos 	/* insert */
    100      1.1  christos 	lock_quick_lock(&bin.lock);
    101      1.1  christos 	bin.overflow_list = &k->entry;
    102      1.1  christos 	lock_quick_unlock(&bin.lock);
    103      1.1  christos 
    104      1.1  christos 	/* find, hash not OK. */
    105  1.1.1.3  christos 	unit_assert( bin_find_entry(table, &bin, myhash(13), k, NULL) == NULL );
    106      1.1  christos 
    107      1.1  christos 	/* find, hash OK, but cmp not */
    108      1.1  christos 	unit_assert( k->entry.hash == k2->entry.hash );
    109  1.1.1.3  christos 	unit_assert( bin_find_entry(table, &bin, h, k2, NULL) == NULL );
    110      1.1  christos 
    111      1.1  christos 	/* find, hash OK, and cmp too */
    112  1.1.1.3  christos 	unit_assert( bin_find_entry(table, &bin, h, k, NULL) == &k->entry );
    113      1.1  christos 
    114      1.1  christos 	/* remove the element */
    115      1.1  christos 	lock_quick_lock(&bin.lock);
    116      1.1  christos 	bin_overflow_remove(&bin, &k->entry);
    117      1.1  christos 	lock_quick_unlock(&bin.lock);
    118  1.1.1.3  christos 	unit_assert( bin_find_entry(table, &bin, h, k, NULL) == NULL );
    119      1.1  christos 
    120      1.1  christos 	/* prepend two different elements; so the list is long */
    121      1.1  christos 	/* one has the same hash, but different cmp */
    122      1.1  christos 	lock_quick_lock(&bin.lock);
    123      1.1  christos 	unit_assert( k->entry.hash == k4->entry.hash );
    124      1.1  christos 	k4->entry.overflow_next = &k->entry;
    125      1.1  christos 	k3->entry.overflow_next = &k4->entry;
    126      1.1  christos 	bin.overflow_list = &k3->entry;
    127      1.1  christos 	lock_quick_unlock(&bin.lock);
    128      1.1  christos 
    129      1.1  christos 	/* find, hash not OK. */
    130  1.1.1.3  christos 	unit_assert( bin_find_entry(table, &bin, myhash(13), k, NULL) == NULL );
    131      1.1  christos 
    132      1.1  christos 	/* find, hash OK, but cmp not */
    133      1.1  christos 	unit_assert( k->entry.hash == k2->entry.hash );
    134  1.1.1.3  christos 	unit_assert( bin_find_entry(table, &bin, h, k2, NULL) == NULL );
    135      1.1  christos 
    136      1.1  christos 	/* find, hash OK, and cmp too */
    137  1.1.1.3  christos 	unit_assert( bin_find_entry(table, &bin, h, k, NULL) == &k->entry );
    138      1.1  christos 
    139      1.1  christos 	/* remove middle element */
    140  1.1.1.3  christos 	unit_assert( bin_find_entry(table, &bin, k4->entry.hash, k4, NULL)
    141      1.1  christos 		== &k4->entry );
    142      1.1  christos 	lock_quick_lock(&bin.lock);
    143      1.1  christos 	bin_overflow_remove(&bin, &k4->entry);
    144      1.1  christos 	lock_quick_unlock(&bin.lock);
    145  1.1.1.3  christos 	unit_assert( bin_find_entry(table, &bin, k4->entry.hash, k4, NULL) == NULL);
    146      1.1  christos 
    147      1.1  christos 	/* remove last element */
    148      1.1  christos 	lock_quick_lock(&bin.lock);
    149      1.1  christos 	bin_overflow_remove(&bin, &k->entry);
    150      1.1  christos 	lock_quick_unlock(&bin.lock);
    151  1.1.1.3  christos 	unit_assert( bin_find_entry(table, &bin, h, k, NULL) == NULL );
    152      1.1  christos 
    153      1.1  christos 	lock_quick_destroy(&bin.lock);
    154      1.1  christos 	delkey(k);
    155      1.1  christos 	delkey(k2);
    156      1.1  christos 	delkey(k3);
    157      1.1  christos 	delkey(k4);
    158      1.1  christos 	deldata(d);
    159      1.1  christos }
    160      1.1  christos 
    161      1.1  christos /** test lru_front lru_remove */
    162      1.1  christos static void test_lru(struct lruhash* table)
    163      1.1  christos {
    164  1.1.1.2  christos 	testkey_type* k = newkey(12);
    165  1.1.1.2  christos 	testkey_type* k2 = newkey(14);
    166      1.1  christos 	lock_quick_lock(&table->lock);
    167      1.1  christos 
    168      1.1  christos 	unit_assert( table->lru_start == NULL && table->lru_end == NULL);
    169      1.1  christos 	lru_remove(table, &k->entry);
    170      1.1  christos 	unit_assert( table->lru_start == NULL && table->lru_end == NULL);
    171      1.1  christos 
    172      1.1  christos 	/* add one */
    173      1.1  christos 	lru_front(table, &k->entry);
    174      1.1  christos 	unit_assert( table->lru_start == &k->entry &&
    175      1.1  christos 		table->lru_end == &k->entry);
    176      1.1  christos 	/* remove it */
    177      1.1  christos 	lru_remove(table, &k->entry);
    178      1.1  christos 	unit_assert( table->lru_start == NULL && table->lru_end == NULL);
    179      1.1  christos 
    180      1.1  christos 	/* add two */
    181      1.1  christos 	lru_front(table, &k->entry);
    182      1.1  christos 	unit_assert( table->lru_start == &k->entry &&
    183      1.1  christos 		table->lru_end == &k->entry);
    184      1.1  christos 	lru_front(table, &k2->entry);
    185      1.1  christos 	unit_assert( table->lru_start == &k2->entry &&
    186      1.1  christos 		table->lru_end == &k->entry);
    187      1.1  christos 	/* remove first in list */
    188      1.1  christos 	lru_remove(table, &k2->entry);
    189      1.1  christos 	unit_assert( table->lru_start == &k->entry &&
    190      1.1  christos 		table->lru_end == &k->entry);
    191      1.1  christos 	lru_front(table, &k2->entry);
    192      1.1  christos 	unit_assert( table->lru_start == &k2->entry &&
    193      1.1  christos 		table->lru_end == &k->entry);
    194      1.1  christos 	/* remove last in list */
    195      1.1  christos 	lru_remove(table, &k->entry);
    196      1.1  christos 	unit_assert( table->lru_start == &k2->entry &&
    197      1.1  christos 		table->lru_end == &k2->entry);
    198      1.1  christos 
    199      1.1  christos 	/* empty the list */
    200      1.1  christos 	lru_remove(table, &k2->entry);
    201      1.1  christos 	unit_assert( table->lru_start == NULL && table->lru_end == NULL);
    202      1.1  christos 	lock_quick_unlock(&table->lock);
    203      1.1  christos 	delkey(k);
    204      1.1  christos 	delkey(k2);
    205      1.1  christos }
    206      1.1  christos 
    207      1.1  christos /** test hashtable using short sequence */
    208      1.1  christos static void
    209      1.1  christos test_short_table(struct lruhash* table)
    210      1.1  christos {
    211  1.1.1.2  christos 	testkey_type* k = newkey(12);
    212  1.1.1.2  christos 	testkey_type* k2 = newkey(14);
    213  1.1.1.2  christos 	testdata_type* d = newdata(128);
    214  1.1.1.2  christos 	testdata_type* d2 = newdata(129);
    215      1.1  christos 
    216      1.1  christos 	k->entry.data = d;
    217      1.1  christos 	k2->entry.data = d2;
    218      1.1  christos 
    219      1.1  christos 	lruhash_insert(table, myhash(12), &k->entry, d, NULL);
    220      1.1  christos 	lruhash_insert(table, myhash(14), &k2->entry, d2, NULL);
    221      1.1  christos 
    222      1.1  christos 	unit_assert( lruhash_lookup(table, myhash(12), k, 0) == &k->entry);
    223      1.1  christos 	lock_rw_unlock( &k->entry.lock );
    224      1.1  christos 	unit_assert( lruhash_lookup(table, myhash(14), k2, 0) == &k2->entry);
    225      1.1  christos 	lock_rw_unlock( &k2->entry.lock );
    226      1.1  christos 	lruhash_remove(table, myhash(12), k);
    227      1.1  christos 	lruhash_remove(table, myhash(14), k2);
    228      1.1  christos }
    229      1.1  christos 
    230      1.1  christos /** number of hash test max */
    231      1.1  christos #define HASHTESTMAX 25
    232      1.1  christos 
    233      1.1  christos /** test adding a random element */
    234      1.1  christos static void
    235  1.1.1.2  christos testadd(struct lruhash* table, testdata_type* ref[])
    236      1.1  christos {
    237      1.1  christos 	int numtoadd = random() % HASHTESTMAX;
    238  1.1.1.2  christos 	testdata_type* data = newdata(numtoadd);
    239  1.1.1.2  christos 	testkey_type* key = newkey(numtoadd);
    240      1.1  christos 	key->entry.data = data;
    241      1.1  christos 	lruhash_insert(table, myhash(numtoadd), &key->entry, data, NULL);
    242      1.1  christos 	ref[numtoadd] = data;
    243      1.1  christos }
    244      1.1  christos 
    245      1.1  christos /** test adding a random element */
    246      1.1  christos static void
    247  1.1.1.2  christos testremove(struct lruhash* table, testdata_type* ref[])
    248      1.1  christos {
    249      1.1  christos 	int num = random() % HASHTESTMAX;
    250  1.1.1.2  christos 	testkey_type* key = newkey(num);
    251      1.1  christos 	lruhash_remove(table, myhash(num), key);
    252      1.1  christos 	ref[num] = NULL;
    253      1.1  christos 	delkey(key);
    254      1.1  christos }
    255      1.1  christos 
    256      1.1  christos /** test adding a random element */
    257      1.1  christos static void
    258  1.1.1.2  christos testlookup(struct lruhash* table, testdata_type* ref[])
    259      1.1  christos {
    260      1.1  christos 	int num = random() % HASHTESTMAX;
    261  1.1.1.2  christos 	testkey_type* key = newkey(num);
    262      1.1  christos 	struct lruhash_entry* en = lruhash_lookup(table, myhash(num), key, 0);
    263  1.1.1.2  christos 	testdata_type* data = en? (testdata_type*)en->data : NULL;
    264      1.1  christos 	if(en) {
    265      1.1  christos 		unit_assert(en->key);
    266      1.1  christos 		unit_assert(en->data);
    267      1.1  christos 	}
    268      1.1  christos 	if(0) log_info("lookup %d got %d, expect %d", num, en? data->data :-1,
    269      1.1  christos 		ref[num]? ref[num]->data : -1);
    270      1.1  christos 	unit_assert( data == ref[num] );
    271      1.1  christos 	if(en) { lock_rw_unlock(&en->lock); }
    272      1.1  christos 	delkey(key);
    273      1.1  christos }
    274      1.1  christos 
    275      1.1  christos /** check integrity of hash table */
    276      1.1  christos static void
    277      1.1  christos check_table(struct lruhash* table)
    278      1.1  christos {
    279      1.1  christos 	struct lruhash_entry* p;
    280      1.1  christos 	size_t c = 0;
    281      1.1  christos 	lock_quick_lock(&table->lock);
    282      1.1  christos 	unit_assert( table->num <= table->size);
    283      1.1  christos 	unit_assert( table->size_mask == (int)table->size-1 );
    284      1.1  christos 	unit_assert( (table->lru_start && table->lru_end) ||
    285      1.1  christos 		(!table->lru_start && !table->lru_end) );
    286      1.1  christos 	unit_assert( table->space_used <= table->space_max );
    287      1.1  christos 	/* check lru list integrity */
    288      1.1  christos 	if(table->lru_start)
    289      1.1  christos 		unit_assert(table->lru_start->lru_prev == NULL);
    290      1.1  christos 	if(table->lru_end)
    291      1.1  christos 		unit_assert(table->lru_end->lru_next == NULL);
    292      1.1  christos 	p = table->lru_start;
    293      1.1  christos 	while(p) {
    294      1.1  christos 		if(p->lru_prev) {
    295      1.1  christos 			unit_assert(p->lru_prev->lru_next == p);
    296      1.1  christos 		}
    297      1.1  christos 		if(p->lru_next) {
    298      1.1  christos 			unit_assert(p->lru_next->lru_prev == p);
    299      1.1  christos 		}
    300      1.1  christos 		c++;
    301      1.1  christos 		p = p->lru_next;
    302      1.1  christos 	}
    303      1.1  christos 	unit_assert(c == table->num);
    304      1.1  christos 
    305      1.1  christos 	/* this assertion is specific to the unit test */
    306      1.1  christos 	unit_assert( table->space_used ==
    307      1.1  christos 		table->num * test_slabhash_sizefunc(NULL, NULL) );
    308      1.1  christos 	lock_quick_unlock(&table->lock);
    309      1.1  christos }
    310      1.1  christos 
    311      1.1  christos /** test adding a random element (unlimited range) */
    312      1.1  christos static void
    313  1.1.1.2  christos testadd_unlim(struct lruhash* table, testdata_type** ref)
    314      1.1  christos {
    315      1.1  christos 	int numtoadd = random() % (HASHTESTMAX * 10);
    316  1.1.1.2  christos 	testdata_type* data = newdata(numtoadd);
    317  1.1.1.2  christos 	testkey_type* key = newkey(numtoadd);
    318      1.1  christos 	key->entry.data = data;
    319      1.1  christos 	lruhash_insert(table, myhash(numtoadd), &key->entry, data, NULL);
    320      1.1  christos 	if(ref)
    321      1.1  christos 		ref[numtoadd] = data;
    322      1.1  christos }
    323      1.1  christos 
    324      1.1  christos /** test adding a random element (unlimited range) */
    325      1.1  christos static void
    326  1.1.1.2  christos testremove_unlim(struct lruhash* table, testdata_type** ref)
    327      1.1  christos {
    328      1.1  christos 	int num = random() % (HASHTESTMAX*10);
    329  1.1.1.2  christos 	testkey_type* key = newkey(num);
    330      1.1  christos 	lruhash_remove(table, myhash(num), key);
    331      1.1  christos 	if(ref)
    332      1.1  christos 		ref[num] = NULL;
    333      1.1  christos 	delkey(key);
    334      1.1  christos }
    335      1.1  christos 
    336      1.1  christos /** test adding a random element (unlimited range) */
    337      1.1  christos static void
    338  1.1.1.2  christos testlookup_unlim(struct lruhash* table, testdata_type** ref)
    339      1.1  christos {
    340      1.1  christos 	int num = random() % (HASHTESTMAX*10);
    341  1.1.1.2  christos 	testkey_type* key = newkey(num);
    342      1.1  christos 	struct lruhash_entry* en = lruhash_lookup(table, myhash(num), key, 0);
    343  1.1.1.2  christos 	testdata_type* data = en? (testdata_type*)en->data : NULL;
    344      1.1  christos 	if(en) {
    345      1.1  christos 		unit_assert(en->key);
    346      1.1  christos 		unit_assert(en->data);
    347      1.1  christos 	}
    348      1.1  christos 	if(0 && ref) log_info("lookup unlim %d got %d, expect %d", num, en ?
    349      1.1  christos 		data->data :-1, ref[num] ? ref[num]->data : -1);
    350      1.1  christos 	if(data && ref) {
    351      1.1  christos 		/* its okay for !data, it fell off the lru */
    352      1.1  christos 		unit_assert( data == ref[num] );
    353      1.1  christos 	}
    354      1.1  christos 	if(en) { lock_rw_unlock(&en->lock); }
    355      1.1  christos 	delkey(key);
    356      1.1  christos }
    357      1.1  christos 
    358      1.1  christos /** test with long sequence of adds, removes and updates, and lookups */
    359      1.1  christos static void
    360      1.1  christos test_long_table(struct lruhash* table)
    361      1.1  christos {
    362      1.1  christos 	/* assuming it all fits in the hashtable, this check will work */
    363  1.1.1.2  christos 	testdata_type* ref[HASHTESTMAX * 100];
    364      1.1  christos 	size_t i;
    365      1.1  christos 	memset(ref, 0, sizeof(ref));
    366      1.1  christos 	/* test assumption */
    367      1.1  christos 	if(0) log_info(" size %d x %d < %d", (int)test_slabhash_sizefunc(NULL, NULL),
    368      1.1  christos 		(int)HASHTESTMAX, (int)table->space_max);
    369      1.1  christos 	unit_assert( test_slabhash_sizefunc(NULL, NULL)*HASHTESTMAX < table->space_max);
    370      1.1  christos 	if(0) lruhash_status(table, "unit test", 1);
    371      1.1  christos 	srandom(48);
    372      1.1  christos 	for(i=0; i<1000; i++) {
    373      1.1  christos 		/* what to do? */
    374      1.1  christos 		if(i == 500) {
    375      1.1  christos 			lruhash_clear(table);
    376      1.1  christos 			memset(ref, 0, sizeof(ref));
    377      1.1  christos 			continue;
    378      1.1  christos 		}
    379      1.1  christos 		switch(random() % 4) {
    380      1.1  christos 			case 0:
    381      1.1  christos 			case 3:
    382      1.1  christos 				testadd(table, ref);
    383      1.1  christos 				break;
    384      1.1  christos 			case 1:
    385      1.1  christos 				testremove(table, ref);
    386      1.1  christos 				break;
    387      1.1  christos 			case 2:
    388      1.1  christos 				testlookup(table, ref);
    389      1.1  christos 				break;
    390      1.1  christos 			default:
    391      1.1  christos 				unit_assert(0);
    392      1.1  christos 		}
    393      1.1  christos 		if(0) lruhash_status(table, "unit test", 1);
    394      1.1  christos 		check_table(table);
    395      1.1  christos 		unit_assert( table->num <= HASHTESTMAX );
    396      1.1  christos 	}
    397      1.1  christos 
    398      1.1  christos 	/* test more, but 'ref' assumption does not hold anymore */
    399      1.1  christos 	for(i=0; i<1000; i++) {
    400      1.1  christos 		/* what to do? */
    401      1.1  christos 		switch(random() % 4) {
    402      1.1  christos 			case 0:
    403      1.1  christos 			case 3:
    404      1.1  christos 				testadd_unlim(table, ref);
    405      1.1  christos 				break;
    406      1.1  christos 			case 1:
    407      1.1  christos 				testremove_unlim(table, ref);
    408      1.1  christos 				break;
    409      1.1  christos 			case 2:
    410      1.1  christos 				testlookup_unlim(table, ref);
    411      1.1  christos 				break;
    412      1.1  christos 			default:
    413      1.1  christos 				unit_assert(0);
    414      1.1  christos 		}
    415      1.1  christos 		if(0) lruhash_status(table, "unlim", 1);
    416      1.1  christos 		check_table(table);
    417      1.1  christos 	}
    418      1.1  christos }
    419      1.1  christos 
    420      1.1  christos /** structure to threaded test the lru hash table */
    421      1.1  christos struct test_thr {
    422      1.1  christos 	/** thread num, first entry. */
    423      1.1  christos 	int num;
    424      1.1  christos 	/** id */
    425  1.1.1.2  christos 	ub_thread_type id;
    426      1.1  christos 	/** hash table */
    427      1.1  christos 	struct lruhash* table;
    428      1.1  christos };
    429      1.1  christos 
    430      1.1  christos /** main routine for threaded hash table test */
    431      1.1  christos static void*
    432      1.1  christos test_thr_main(void* arg)
    433      1.1  christos {
    434      1.1  christos 	struct test_thr* t = (struct test_thr*)arg;
    435      1.1  christos 	int i;
    436      1.1  christos 	log_thread_set(&t->num);
    437      1.1  christos 	for(i=0; i<1000; i++) {
    438      1.1  christos 		switch(random() % 4) {
    439      1.1  christos 			case 0:
    440      1.1  christos 			case 3:
    441      1.1  christos 				testadd_unlim(t->table, NULL);
    442      1.1  christos 				break;
    443      1.1  christos 			case 1:
    444      1.1  christos 				testremove_unlim(t->table, NULL);
    445      1.1  christos 				break;
    446      1.1  christos 			case 2:
    447      1.1  christos 				testlookup_unlim(t->table, NULL);
    448      1.1  christos 				break;
    449      1.1  christos 			default:
    450      1.1  christos 				unit_assert(0);
    451      1.1  christos 		}
    452      1.1  christos 		if(0) lruhash_status(t->table, "hashtest", 1);
    453      1.1  christos 		if(i % 100 == 0) /* because of locking, not all the time */
    454      1.1  christos 			check_table(t->table);
    455      1.1  christos 	}
    456      1.1  christos 	check_table(t->table);
    457      1.1  christos 	return NULL;
    458      1.1  christos }
    459      1.1  christos 
    460      1.1  christos /** test hash table access by multiple threads */
    461      1.1  christos static void
    462      1.1  christos test_threaded_table(struct lruhash* table)
    463      1.1  christos {
    464      1.1  christos 	int numth = 10;
    465      1.1  christos 	struct test_thr t[100];
    466      1.1  christos 	int i;
    467      1.1  christos 
    468      1.1  christos 	for(i=1; i<numth; i++) {
    469      1.1  christos 		t[i].num = i;
    470      1.1  christos 		t[i].table = table;
    471      1.1  christos 		ub_thread_create(&t[i].id, test_thr_main, &t[i]);
    472      1.1  christos 	}
    473      1.1  christos 
    474      1.1  christos 	for(i=1; i<numth; i++) {
    475      1.1  christos 		ub_thread_join(t[i].id);
    476      1.1  christos 	}
    477      1.1  christos 	if(0) lruhash_status(table, "hashtest", 1);
    478      1.1  christos }
    479      1.1  christos 
    480      1.1  christos void lruhash_test(void)
    481      1.1  christos {
    482      1.1  christos 	/* start very very small array, so it can do lots of table_grow() */
    483      1.1  christos 	/* also small in size so that reclaim has to be done quickly. */
    484      1.1  christos 	struct lruhash* table ;
    485      1.1  christos 	unit_show_feature("lruhash");
    486      1.1  christos 	table = lruhash_create(2, 8192,
    487      1.1  christos 		test_slabhash_sizefunc, test_slabhash_compfunc,
    488      1.1  christos 		test_slabhash_delkey, test_slabhash_deldata, NULL);
    489      1.1  christos 	test_bin_find_entry(table);
    490      1.1  christos 	test_lru(table);
    491      1.1  christos 	test_short_table(table);
    492      1.1  christos 	test_long_table(table);
    493      1.1  christos 	lruhash_delete(table);
    494      1.1  christos 	table = lruhash_create(2, 8192,
    495      1.1  christos 		test_slabhash_sizefunc, test_slabhash_compfunc,
    496      1.1  christos 		test_slabhash_delkey, test_slabhash_deldata, NULL);
    497      1.1  christos 	test_threaded_table(table);
    498      1.1  christos 	lruhash_delete(table);
    499      1.1  christos }
    500