1ae8c6e27Sflorian /*
2ae8c6e27Sflorian * util/storage/lruhash.c - hashtable, hash function, LRU keeping.
3ae8c6e27Sflorian *
4ae8c6e27Sflorian * Copyright (c) 2007, NLnet Labs. All rights reserved.
5ae8c6e27Sflorian *
6ae8c6e27Sflorian * This software is open source.
7ae8c6e27Sflorian *
8ae8c6e27Sflorian * Redistribution and use in source and binary forms, with or without
9ae8c6e27Sflorian * modification, are permitted provided that the following conditions
10ae8c6e27Sflorian * are met:
11ae8c6e27Sflorian *
12ae8c6e27Sflorian * Redistributions of source code must retain the above copyright notice,
13ae8c6e27Sflorian * this list of conditions and the following disclaimer.
14ae8c6e27Sflorian *
15ae8c6e27Sflorian * Redistributions in binary form must reproduce the above copyright notice,
16ae8c6e27Sflorian * this list of conditions and the following disclaimer in the documentation
17ae8c6e27Sflorian * and/or other materials provided with the distribution.
18ae8c6e27Sflorian *
19ae8c6e27Sflorian * Neither the name of the NLNET LABS nor the names of its contributors may
20ae8c6e27Sflorian * be used to endorse or promote products derived from this software without
21ae8c6e27Sflorian * specific prior written permission.
22ae8c6e27Sflorian *
23ae8c6e27Sflorian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24ae8c6e27Sflorian * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25ae8c6e27Sflorian * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26ae8c6e27Sflorian * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27ae8c6e27Sflorian * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28ae8c6e27Sflorian * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29ae8c6e27Sflorian * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30ae8c6e27Sflorian * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31ae8c6e27Sflorian * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32ae8c6e27Sflorian * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33ae8c6e27Sflorian * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34ae8c6e27Sflorian */
35ae8c6e27Sflorian
36ae8c6e27Sflorian /**
37ae8c6e27Sflorian * \file
38ae8c6e27Sflorian *
39ae8c6e27Sflorian * This file contains a hashtable with LRU keeping of entries.
40ae8c6e27Sflorian *
41ae8c6e27Sflorian */
42ae8c6e27Sflorian
43ae8c6e27Sflorian #include "config.h"
44ae8c6e27Sflorian #include "util/storage/lruhash.h"
45ae8c6e27Sflorian #include "util/fptr_wlist.h"
46ae8c6e27Sflorian
47ae8c6e27Sflorian void
bin_init(struct lruhash_bin * array,size_t size)48ae8c6e27Sflorian bin_init(struct lruhash_bin* array, size_t size)
49ae8c6e27Sflorian {
50ae8c6e27Sflorian size_t i;
51ae8c6e27Sflorian #ifdef THREADS_DISABLED
52ae8c6e27Sflorian (void)array;
53ae8c6e27Sflorian #endif
54ae8c6e27Sflorian for(i=0; i<size; i++) {
55ae8c6e27Sflorian lock_quick_init(&array[i].lock);
56ae8c6e27Sflorian lock_protect(&array[i].lock, &array[i],
57ae8c6e27Sflorian sizeof(struct lruhash_bin));
58ae8c6e27Sflorian }
59ae8c6e27Sflorian }
60ae8c6e27Sflorian
61ae8c6e27Sflorian struct lruhash*
lruhash_create(size_t start_size,size_t maxmem,lruhash_sizefunc_type sizefunc,lruhash_compfunc_type compfunc,lruhash_delkeyfunc_type delkeyfunc,lruhash_deldatafunc_type deldatafunc,void * arg)62ae8c6e27Sflorian lruhash_create(size_t start_size, size_t maxmem,
63ae8c6e27Sflorian lruhash_sizefunc_type sizefunc, lruhash_compfunc_type compfunc,
64ae8c6e27Sflorian lruhash_delkeyfunc_type delkeyfunc,
65ae8c6e27Sflorian lruhash_deldatafunc_type deldatafunc, void* arg)
66ae8c6e27Sflorian {
67ae8c6e27Sflorian struct lruhash* table = (struct lruhash*)calloc(1,
68ae8c6e27Sflorian sizeof(struct lruhash));
69ae8c6e27Sflorian if(!table)
70ae8c6e27Sflorian return NULL;
71ae8c6e27Sflorian lock_quick_init(&table->lock);
72ae8c6e27Sflorian table->sizefunc = sizefunc;
73ae8c6e27Sflorian table->compfunc = compfunc;
74ae8c6e27Sflorian table->delkeyfunc = delkeyfunc;
75ae8c6e27Sflorian table->deldatafunc = deldatafunc;
76ae8c6e27Sflorian table->cb_arg = arg;
77ae8c6e27Sflorian table->size = start_size;
78ae8c6e27Sflorian table->size_mask = (int)(start_size-1);
79ae8c6e27Sflorian table->lru_start = NULL;
80ae8c6e27Sflorian table->lru_end = NULL;
81ae8c6e27Sflorian table->num = 0;
82ae8c6e27Sflorian table->space_used = 0;
83ae8c6e27Sflorian table->space_max = maxmem;
84d500c338Sflorian table->max_collisions = 0;
85ae8c6e27Sflorian table->array = calloc(table->size, sizeof(struct lruhash_bin));
86ae8c6e27Sflorian if(!table->array) {
87ae8c6e27Sflorian lock_quick_destroy(&table->lock);
88ae8c6e27Sflorian free(table);
89ae8c6e27Sflorian return NULL;
90ae8c6e27Sflorian }
91ae8c6e27Sflorian bin_init(table->array, table->size);
92ae8c6e27Sflorian lock_protect(&table->lock, table, sizeof(*table));
93ae8c6e27Sflorian lock_protect(&table->lock, table->array,
94ae8c6e27Sflorian table->size*sizeof(struct lruhash_bin));
95ae8c6e27Sflorian return table;
96ae8c6e27Sflorian }
97ae8c6e27Sflorian
98ae8c6e27Sflorian void
bin_delete(struct lruhash * table,struct lruhash_bin * bin)99ae8c6e27Sflorian bin_delete(struct lruhash* table, struct lruhash_bin* bin)
100ae8c6e27Sflorian {
101ae8c6e27Sflorian struct lruhash_entry* p, *np;
102ae8c6e27Sflorian void *d;
103ae8c6e27Sflorian if(!bin)
104ae8c6e27Sflorian return;
105ae8c6e27Sflorian lock_quick_destroy(&bin->lock);
106ae8c6e27Sflorian p = bin->overflow_list;
107ae8c6e27Sflorian bin->overflow_list = NULL;
108ae8c6e27Sflorian while(p) {
109ae8c6e27Sflorian np = p->overflow_next;
110ae8c6e27Sflorian d = p->data;
111ae8c6e27Sflorian (*table->delkeyfunc)(p->key, table->cb_arg);
112ae8c6e27Sflorian (*table->deldatafunc)(d, table->cb_arg);
113ae8c6e27Sflorian p = np;
114ae8c6e27Sflorian }
115ae8c6e27Sflorian }
116ae8c6e27Sflorian
117ae8c6e27Sflorian void
bin_split(struct lruhash * table,struct lruhash_bin * newa,int newmask)118ae8c6e27Sflorian bin_split(struct lruhash* table, struct lruhash_bin* newa,
119ae8c6e27Sflorian int newmask)
120ae8c6e27Sflorian {
121ae8c6e27Sflorian size_t i;
122ae8c6e27Sflorian struct lruhash_entry *p, *np;
123ae8c6e27Sflorian struct lruhash_bin* newbin;
124ae8c6e27Sflorian /* move entries to new table. Notice that since hash x is mapped to
125ae8c6e27Sflorian * bin x & mask, and new mask uses one more bit, so all entries in
126ae8c6e27Sflorian * one bin will go into the old bin or bin | newbit */
127ae8c6e27Sflorian #ifndef THREADS_DISABLED
128ae8c6e27Sflorian int newbit = newmask - table->size_mask;
129ae8c6e27Sflorian #endif
130ae8c6e27Sflorian /* so, really, this task could also be threaded, per bin. */
131ae8c6e27Sflorian /* LRU list is not changed */
132ae8c6e27Sflorian for(i=0; i<table->size; i++)
133ae8c6e27Sflorian {
134ae8c6e27Sflorian lock_quick_lock(&table->array[i].lock);
135ae8c6e27Sflorian p = table->array[i].overflow_list;
136ae8c6e27Sflorian /* lock both destination bins */
137ae8c6e27Sflorian lock_quick_lock(&newa[i].lock);
138ae8c6e27Sflorian lock_quick_lock(&newa[newbit|i].lock);
139ae8c6e27Sflorian while(p) {
140ae8c6e27Sflorian np = p->overflow_next;
141ae8c6e27Sflorian /* link into correct new bin */
142ae8c6e27Sflorian newbin = &newa[p->hash & newmask];
143ae8c6e27Sflorian p->overflow_next = newbin->overflow_list;
144ae8c6e27Sflorian newbin->overflow_list = p;
145ae8c6e27Sflorian p=np;
146ae8c6e27Sflorian }
147ae8c6e27Sflorian lock_quick_unlock(&newa[i].lock);
148ae8c6e27Sflorian lock_quick_unlock(&newa[newbit|i].lock);
149ae8c6e27Sflorian lock_quick_unlock(&table->array[i].lock);
150ae8c6e27Sflorian }
151ae8c6e27Sflorian }
152ae8c6e27Sflorian
153ae8c6e27Sflorian void
lruhash_delete(struct lruhash * table)154ae8c6e27Sflorian lruhash_delete(struct lruhash* table)
155ae8c6e27Sflorian {
156ae8c6e27Sflorian size_t i;
157ae8c6e27Sflorian if(!table)
158ae8c6e27Sflorian return;
159ae8c6e27Sflorian /* delete lock on hashtable to force check its OK */
160ae8c6e27Sflorian lock_quick_destroy(&table->lock);
161ae8c6e27Sflorian for(i=0; i<table->size; i++)
162ae8c6e27Sflorian bin_delete(table, &table->array[i]);
163ae8c6e27Sflorian free(table->array);
164ae8c6e27Sflorian free(table);
165ae8c6e27Sflorian }
166ae8c6e27Sflorian
167ae8c6e27Sflorian void
bin_overflow_remove(struct lruhash_bin * bin,struct lruhash_entry * entry)168ae8c6e27Sflorian bin_overflow_remove(struct lruhash_bin* bin, struct lruhash_entry* entry)
169ae8c6e27Sflorian {
170ae8c6e27Sflorian struct lruhash_entry* p = bin->overflow_list;
171ae8c6e27Sflorian struct lruhash_entry** prevp = &bin->overflow_list;
172ae8c6e27Sflorian while(p) {
173ae8c6e27Sflorian if(p == entry) {
174ae8c6e27Sflorian *prevp = p->overflow_next;
175ae8c6e27Sflorian return;
176ae8c6e27Sflorian }
177ae8c6e27Sflorian prevp = &p->overflow_next;
178ae8c6e27Sflorian p = p->overflow_next;
179ae8c6e27Sflorian }
180ae8c6e27Sflorian }
181ae8c6e27Sflorian
182ae8c6e27Sflorian void
reclaim_space(struct lruhash * table,struct lruhash_entry ** list)183ae8c6e27Sflorian reclaim_space(struct lruhash* table, struct lruhash_entry** list)
184ae8c6e27Sflorian {
185ae8c6e27Sflorian struct lruhash_entry* d;
186ae8c6e27Sflorian struct lruhash_bin* bin;
187ae8c6e27Sflorian log_assert(table);
188ae8c6e27Sflorian /* does not delete MRU entry, so table will not be empty. */
189ae8c6e27Sflorian while(table->num > 1 && table->space_used > table->space_max) {
190ae8c6e27Sflorian /* notice that since we hold the hashtable lock, nobody
191ae8c6e27Sflorian can change the lru chain. So it cannot be deleted underneath
192ae8c6e27Sflorian us. We still need the hashbin and entry write lock to make
193ae8c6e27Sflorian sure we flush all users away from the entry.
194ae8c6e27Sflorian which is unlikely, since it is LRU, if someone got a rdlock
195ae8c6e27Sflorian it would be moved to front, but to be sure. */
196ae8c6e27Sflorian d = table->lru_end;
197ae8c6e27Sflorian /* specialised, delete from end of double linked list,
198ae8c6e27Sflorian and we know num>1, so there is a previous lru entry. */
199ae8c6e27Sflorian log_assert(d && d->lru_prev);
200ae8c6e27Sflorian table->lru_end = d->lru_prev;
201ae8c6e27Sflorian d->lru_prev->lru_next = NULL;
202ae8c6e27Sflorian /* schedule entry for deletion */
203ae8c6e27Sflorian bin = &table->array[d->hash & table->size_mask];
204ae8c6e27Sflorian table->num --;
205ae8c6e27Sflorian lock_quick_lock(&bin->lock);
206ae8c6e27Sflorian bin_overflow_remove(bin, d);
207ae8c6e27Sflorian d->overflow_next = *list;
208ae8c6e27Sflorian *list = d;
209ae8c6e27Sflorian lock_rw_wrlock(&d->lock);
210ae8c6e27Sflorian table->space_used -= table->sizefunc(d->key, d->data);
211ae8c6e27Sflorian if(table->markdelfunc)
212ae8c6e27Sflorian (*table->markdelfunc)(d->key);
213ae8c6e27Sflorian lock_rw_unlock(&d->lock);
214ae8c6e27Sflorian lock_quick_unlock(&bin->lock);
215ae8c6e27Sflorian }
216ae8c6e27Sflorian }
217ae8c6e27Sflorian
218ae8c6e27Sflorian struct lruhash_entry*
bin_find_entry(struct lruhash * table,struct lruhash_bin * bin,hashvalue_type hash,void * key,size_t * collisions)219ae8c6e27Sflorian bin_find_entry(struct lruhash* table,
220d500c338Sflorian struct lruhash_bin* bin, hashvalue_type hash, void* key, size_t* collisions)
221ae8c6e27Sflorian {
222d500c338Sflorian size_t c = 0;
223ae8c6e27Sflorian struct lruhash_entry* p = bin->overflow_list;
224ae8c6e27Sflorian while(p) {
225ae8c6e27Sflorian if(p->hash == hash && table->compfunc(p->key, key) == 0)
226d500c338Sflorian break;
227d500c338Sflorian c++;
228ae8c6e27Sflorian p = p->overflow_next;
229ae8c6e27Sflorian }
230d500c338Sflorian if (collisions != NULL)
231d500c338Sflorian *collisions = c;
232d500c338Sflorian return p;
233ae8c6e27Sflorian }
234ae8c6e27Sflorian
235ae8c6e27Sflorian void
table_grow(struct lruhash * table)236ae8c6e27Sflorian table_grow(struct lruhash* table)
237ae8c6e27Sflorian {
238ae8c6e27Sflorian struct lruhash_bin* newa;
239ae8c6e27Sflorian int newmask;
240ae8c6e27Sflorian size_t i;
241ae8c6e27Sflorian if(table->size_mask == (int)(((size_t)-1)>>1)) {
242ae8c6e27Sflorian log_err("hash array malloc: size_t too small");
243ae8c6e27Sflorian return;
244ae8c6e27Sflorian }
245ae8c6e27Sflorian /* try to allocate new array, if not fail */
246ae8c6e27Sflorian newa = calloc(table->size*2, sizeof(struct lruhash_bin));
247ae8c6e27Sflorian if(!newa) {
248ae8c6e27Sflorian log_err("hash grow: malloc failed");
249ae8c6e27Sflorian /* continue with smaller array. Though its slower. */
250ae8c6e27Sflorian return;
251ae8c6e27Sflorian }
252ae8c6e27Sflorian bin_init(newa, table->size*2);
253ae8c6e27Sflorian newmask = (table->size_mask << 1) | 1;
254ae8c6e27Sflorian bin_split(table, newa, newmask);
255ae8c6e27Sflorian /* delete the old bins */
256ae8c6e27Sflorian lock_unprotect(&table->lock, table->array);
257ae8c6e27Sflorian for(i=0; i<table->size; i++) {
258ae8c6e27Sflorian lock_quick_destroy(&table->array[i].lock);
259ae8c6e27Sflorian }
260ae8c6e27Sflorian free(table->array);
261ae8c6e27Sflorian
262ae8c6e27Sflorian table->size *= 2;
263ae8c6e27Sflorian table->size_mask = newmask;
264ae8c6e27Sflorian table->array = newa;
265ae8c6e27Sflorian lock_protect(&table->lock, table->array,
266ae8c6e27Sflorian table->size*sizeof(struct lruhash_bin));
267ae8c6e27Sflorian return;
268ae8c6e27Sflorian }
269ae8c6e27Sflorian
270ae8c6e27Sflorian void
lru_front(struct lruhash * table,struct lruhash_entry * entry)271ae8c6e27Sflorian lru_front(struct lruhash* table, struct lruhash_entry* entry)
272ae8c6e27Sflorian {
273ae8c6e27Sflorian entry->lru_prev = NULL;
274ae8c6e27Sflorian entry->lru_next = table->lru_start;
275ae8c6e27Sflorian if(!table->lru_start)
276ae8c6e27Sflorian table->lru_end = entry;
277ae8c6e27Sflorian else table->lru_start->lru_prev = entry;
278ae8c6e27Sflorian table->lru_start = entry;
279ae8c6e27Sflorian }
280ae8c6e27Sflorian
281ae8c6e27Sflorian void
lru_remove(struct lruhash * table,struct lruhash_entry * entry)282ae8c6e27Sflorian lru_remove(struct lruhash* table, struct lruhash_entry* entry)
283ae8c6e27Sflorian {
284ae8c6e27Sflorian if(entry->lru_prev)
285ae8c6e27Sflorian entry->lru_prev->lru_next = entry->lru_next;
286ae8c6e27Sflorian else table->lru_start = entry->lru_next;
287ae8c6e27Sflorian if(entry->lru_next)
288ae8c6e27Sflorian entry->lru_next->lru_prev = entry->lru_prev;
289ae8c6e27Sflorian else table->lru_end = entry->lru_prev;
290ae8c6e27Sflorian }
291ae8c6e27Sflorian
292ae8c6e27Sflorian void
lru_touch(struct lruhash * table,struct lruhash_entry * entry)293ae8c6e27Sflorian lru_touch(struct lruhash* table, struct lruhash_entry* entry)
294ae8c6e27Sflorian {
295ae8c6e27Sflorian log_assert(table && entry);
296ae8c6e27Sflorian if(entry == table->lru_start)
297ae8c6e27Sflorian return; /* nothing to do */
298ae8c6e27Sflorian /* remove from current lru position */
299ae8c6e27Sflorian lru_remove(table, entry);
300ae8c6e27Sflorian /* add at front */
301ae8c6e27Sflorian lru_front(table, entry);
302ae8c6e27Sflorian }
303ae8c6e27Sflorian
304ae8c6e27Sflorian void
lruhash_insert(struct lruhash * table,hashvalue_type hash,struct lruhash_entry * entry,void * data,void * cb_arg)305ae8c6e27Sflorian lruhash_insert(struct lruhash* table, hashvalue_type hash,
306ae8c6e27Sflorian struct lruhash_entry* entry, void* data, void* cb_arg)
307ae8c6e27Sflorian {
308ae8c6e27Sflorian struct lruhash_bin* bin;
309ae8c6e27Sflorian struct lruhash_entry* found, *reclaimlist=NULL;
310ae8c6e27Sflorian size_t need_size;
311d500c338Sflorian size_t collisions;
312ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
313ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
314ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
315ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc));
316ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
317ae8c6e27Sflorian need_size = table->sizefunc(entry->key, data);
318ae8c6e27Sflorian if(cb_arg == NULL) cb_arg = table->cb_arg;
319ae8c6e27Sflorian
320ae8c6e27Sflorian /* find bin */
321ae8c6e27Sflorian lock_quick_lock(&table->lock);
322ae8c6e27Sflorian bin = &table->array[hash & table->size_mask];
323ae8c6e27Sflorian lock_quick_lock(&bin->lock);
324ae8c6e27Sflorian
325ae8c6e27Sflorian /* see if entry exists already */
326d500c338Sflorian if(!(found=bin_find_entry(table, bin, hash, entry->key, &collisions))) {
327ae8c6e27Sflorian /* if not: add to bin */
328ae8c6e27Sflorian entry->overflow_next = bin->overflow_list;
329ae8c6e27Sflorian bin->overflow_list = entry;
330ae8c6e27Sflorian lru_front(table, entry);
331ae8c6e27Sflorian table->num++;
332d500c338Sflorian if (table->max_collisions < collisions)
333d500c338Sflorian table->max_collisions = collisions;
334ae8c6e27Sflorian table->space_used += need_size;
335ae8c6e27Sflorian } else {
336ae8c6e27Sflorian /* if so: update data - needs a writelock */
337ae8c6e27Sflorian table->space_used += need_size -
338ae8c6e27Sflorian (*table->sizefunc)(found->key, found->data);
339ae8c6e27Sflorian (*table->delkeyfunc)(entry->key, cb_arg);
340ae8c6e27Sflorian lru_touch(table, found);
341ae8c6e27Sflorian lock_rw_wrlock(&found->lock);
342ae8c6e27Sflorian (*table->deldatafunc)(found->data, cb_arg);
343ae8c6e27Sflorian found->data = data;
344ae8c6e27Sflorian lock_rw_unlock(&found->lock);
345ae8c6e27Sflorian }
346ae8c6e27Sflorian lock_quick_unlock(&bin->lock);
347ae8c6e27Sflorian if(table->space_used > table->space_max)
348ae8c6e27Sflorian reclaim_space(table, &reclaimlist);
349ae8c6e27Sflorian if(table->num >= table->size)
350ae8c6e27Sflorian table_grow(table);
351ae8c6e27Sflorian lock_quick_unlock(&table->lock);
352ae8c6e27Sflorian
353ae8c6e27Sflorian /* finish reclaim if any (outside of critical region) */
354ae8c6e27Sflorian while(reclaimlist) {
355ae8c6e27Sflorian struct lruhash_entry* n = reclaimlist->overflow_next;
356ae8c6e27Sflorian void* d = reclaimlist->data;
357ae8c6e27Sflorian (*table->delkeyfunc)(reclaimlist->key, cb_arg);
358ae8c6e27Sflorian (*table->deldatafunc)(d, cb_arg);
359ae8c6e27Sflorian reclaimlist = n;
360ae8c6e27Sflorian }
361ae8c6e27Sflorian }
362ae8c6e27Sflorian
363ae8c6e27Sflorian struct lruhash_entry*
lruhash_lookup(struct lruhash * table,hashvalue_type hash,void * key,int wr)364ae8c6e27Sflorian lruhash_lookup(struct lruhash* table, hashvalue_type hash, void* key, int wr)
365ae8c6e27Sflorian {
366ae8c6e27Sflorian struct lruhash_entry* entry;
367ae8c6e27Sflorian struct lruhash_bin* bin;
368ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc));
369ae8c6e27Sflorian
370ae8c6e27Sflorian lock_quick_lock(&table->lock);
371ae8c6e27Sflorian bin = &table->array[hash & table->size_mask];
372ae8c6e27Sflorian lock_quick_lock(&bin->lock);
373d500c338Sflorian if((entry=bin_find_entry(table, bin, hash, key, NULL)))
374ae8c6e27Sflorian lru_touch(table, entry);
375ae8c6e27Sflorian lock_quick_unlock(&table->lock);
376ae8c6e27Sflorian
377ae8c6e27Sflorian if(entry) {
378ae8c6e27Sflorian if(wr) { lock_rw_wrlock(&entry->lock); }
379ae8c6e27Sflorian else { lock_rw_rdlock(&entry->lock); }
380ae8c6e27Sflorian }
381ae8c6e27Sflorian lock_quick_unlock(&bin->lock);
382ae8c6e27Sflorian return entry;
383ae8c6e27Sflorian }
384ae8c6e27Sflorian
385ae8c6e27Sflorian void
lruhash_remove(struct lruhash * table,hashvalue_type hash,void * key)386ae8c6e27Sflorian lruhash_remove(struct lruhash* table, hashvalue_type hash, void* key)
387ae8c6e27Sflorian {
388ae8c6e27Sflorian struct lruhash_entry* entry;
389ae8c6e27Sflorian struct lruhash_bin* bin;
390ae8c6e27Sflorian void *d;
391ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
392ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
393ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
394ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc));
395ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
396ae8c6e27Sflorian
397ae8c6e27Sflorian lock_quick_lock(&table->lock);
398ae8c6e27Sflorian bin = &table->array[hash & table->size_mask];
399ae8c6e27Sflorian lock_quick_lock(&bin->lock);
400d500c338Sflorian if((entry=bin_find_entry(table, bin, hash, key, NULL))) {
401ae8c6e27Sflorian bin_overflow_remove(bin, entry);
402ae8c6e27Sflorian lru_remove(table, entry);
403ae8c6e27Sflorian } else {
404ae8c6e27Sflorian lock_quick_unlock(&table->lock);
405ae8c6e27Sflorian lock_quick_unlock(&bin->lock);
406ae8c6e27Sflorian return;
407ae8c6e27Sflorian }
408ae8c6e27Sflorian table->num--;
409ae8c6e27Sflorian table->space_used -= (*table->sizefunc)(entry->key, entry->data);
410ae8c6e27Sflorian lock_rw_wrlock(&entry->lock);
411ae8c6e27Sflorian if(table->markdelfunc)
412ae8c6e27Sflorian (*table->markdelfunc)(entry->key);
413ae8c6e27Sflorian lock_rw_unlock(&entry->lock);
414ae8c6e27Sflorian lock_quick_unlock(&bin->lock);
415a8eaceedSflorian lock_quick_unlock(&table->lock);
416ae8c6e27Sflorian /* finish removal */
417ae8c6e27Sflorian d = entry->data;
418ae8c6e27Sflorian (*table->delkeyfunc)(entry->key, table->cb_arg);
419ae8c6e27Sflorian (*table->deldatafunc)(d, table->cb_arg);
420ae8c6e27Sflorian }
421ae8c6e27Sflorian
422ae8c6e27Sflorian /** clear bin, respecting locks, does not do space, LRU */
423ae8c6e27Sflorian static void
bin_clear(struct lruhash * table,struct lruhash_bin * bin)424ae8c6e27Sflorian bin_clear(struct lruhash* table, struct lruhash_bin* bin)
425ae8c6e27Sflorian {
426ae8c6e27Sflorian struct lruhash_entry* p, *np;
427ae8c6e27Sflorian void *d;
428ae8c6e27Sflorian lock_quick_lock(&bin->lock);
429ae8c6e27Sflorian p = bin->overflow_list;
430ae8c6e27Sflorian while(p) {
431ae8c6e27Sflorian lock_rw_wrlock(&p->lock);
432ae8c6e27Sflorian np = p->overflow_next;
433ae8c6e27Sflorian d = p->data;
434ae8c6e27Sflorian if(table->markdelfunc)
435ae8c6e27Sflorian (*table->markdelfunc)(p->key);
436ae8c6e27Sflorian lock_rw_unlock(&p->lock);
437ae8c6e27Sflorian (*table->delkeyfunc)(p->key, table->cb_arg);
438ae8c6e27Sflorian (*table->deldatafunc)(d, table->cb_arg);
439ae8c6e27Sflorian p = np;
440ae8c6e27Sflorian }
441ae8c6e27Sflorian bin->overflow_list = NULL;
442ae8c6e27Sflorian lock_quick_unlock(&bin->lock);
443ae8c6e27Sflorian }
444ae8c6e27Sflorian
445ae8c6e27Sflorian void
lruhash_clear(struct lruhash * table)446ae8c6e27Sflorian lruhash_clear(struct lruhash* table)
447ae8c6e27Sflorian {
448ae8c6e27Sflorian size_t i;
449ae8c6e27Sflorian if(!table)
450ae8c6e27Sflorian return;
451ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
452ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
453ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
454ae8c6e27Sflorian
455ae8c6e27Sflorian lock_quick_lock(&table->lock);
456ae8c6e27Sflorian for(i=0; i<table->size; i++) {
457ae8c6e27Sflorian bin_clear(table, &table->array[i]);
458ae8c6e27Sflorian }
459ae8c6e27Sflorian table->lru_start = NULL;
460ae8c6e27Sflorian table->lru_end = NULL;
461ae8c6e27Sflorian table->num = 0;
462ae8c6e27Sflorian table->space_used = 0;
463ae8c6e27Sflorian lock_quick_unlock(&table->lock);
464ae8c6e27Sflorian }
465ae8c6e27Sflorian
466ae8c6e27Sflorian void
lruhash_status(struct lruhash * table,const char * id,int extended)467ae8c6e27Sflorian lruhash_status(struct lruhash* table, const char* id, int extended)
468ae8c6e27Sflorian {
469ae8c6e27Sflorian lock_quick_lock(&table->lock);
470ae8c6e27Sflorian log_info("%s: %u entries, memory %u / %u",
471ae8c6e27Sflorian id, (unsigned)table->num, (unsigned)table->space_used,
472ae8c6e27Sflorian (unsigned)table->space_max);
473ae8c6e27Sflorian log_info(" itemsize %u, array %u, mask %d",
474ae8c6e27Sflorian (unsigned)(table->num? table->space_used/table->num : 0),
475ae8c6e27Sflorian (unsigned)table->size, table->size_mask);
476ae8c6e27Sflorian if(extended) {
477ae8c6e27Sflorian size_t i;
478ae8c6e27Sflorian int min=(int)table->size*2, max=-2;
479ae8c6e27Sflorian for(i=0; i<table->size; i++) {
480ae8c6e27Sflorian int here = 0;
481ae8c6e27Sflorian struct lruhash_entry *en;
482ae8c6e27Sflorian lock_quick_lock(&table->array[i].lock);
483ae8c6e27Sflorian en = table->array[i].overflow_list;
484ae8c6e27Sflorian while(en) {
485ae8c6e27Sflorian here ++;
486ae8c6e27Sflorian en = en->overflow_next;
487ae8c6e27Sflorian }
488ae8c6e27Sflorian lock_quick_unlock(&table->array[i].lock);
489ae8c6e27Sflorian if(extended >= 2)
490ae8c6e27Sflorian log_info("bin[%d] %d", (int)i, here);
491ae8c6e27Sflorian if(here > max) max = here;
492ae8c6e27Sflorian if(here < min) min = here;
493ae8c6e27Sflorian }
494ae8c6e27Sflorian log_info(" bin min %d, avg %.2lf, max %d", min,
495ae8c6e27Sflorian (double)table->num/(double)table->size, max);
496ae8c6e27Sflorian }
497ae8c6e27Sflorian lock_quick_unlock(&table->lock);
498ae8c6e27Sflorian }
499ae8c6e27Sflorian
500ae8c6e27Sflorian size_t
lruhash_get_mem(struct lruhash * table)501ae8c6e27Sflorian lruhash_get_mem(struct lruhash* table)
502ae8c6e27Sflorian {
503ae8c6e27Sflorian size_t s;
504ae8c6e27Sflorian lock_quick_lock(&table->lock);
505ae8c6e27Sflorian s = sizeof(struct lruhash) + table->space_used;
506ae8c6e27Sflorian #ifdef USE_THREAD_DEBUG
507ae8c6e27Sflorian if(table->size != 0) {
508ae8c6e27Sflorian size_t i;
509ae8c6e27Sflorian for(i=0; i<table->size; i++)
510ae8c6e27Sflorian s += sizeof(struct lruhash_bin) +
511ae8c6e27Sflorian lock_get_mem(&table->array[i].lock);
512ae8c6e27Sflorian }
513ae8c6e27Sflorian #else /* no THREAD_DEBUG */
514ae8c6e27Sflorian if(table->size != 0)
515ae8c6e27Sflorian s += (table->size)*(sizeof(struct lruhash_bin) +
516ae8c6e27Sflorian lock_get_mem(&table->array[0].lock));
517ae8c6e27Sflorian #endif
518ae8c6e27Sflorian lock_quick_unlock(&table->lock);
519ae8c6e27Sflorian s += lock_get_mem(&table->lock);
520ae8c6e27Sflorian return s;
521ae8c6e27Sflorian }
522ae8c6e27Sflorian
523ae8c6e27Sflorian void
lruhash_setmarkdel(struct lruhash * table,lruhash_markdelfunc_type md)524ae8c6e27Sflorian lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_type md)
525ae8c6e27Sflorian {
526ae8c6e27Sflorian lock_quick_lock(&table->lock);
527ae8c6e27Sflorian table->markdelfunc = md;
528ae8c6e27Sflorian lock_quick_unlock(&table->lock);
529ae8c6e27Sflorian }
530ae8c6e27Sflorian
531ae8c6e27Sflorian void
lruhash_update_space_used(struct lruhash * table,void * cb_arg,int diff_size)532*096314feSflorian lruhash_update_space_used(struct lruhash* table, void* cb_arg, int diff_size)
533*096314feSflorian {
534*096314feSflorian struct lruhash_entry *reclaimlist = NULL;
535*096314feSflorian
536*096314feSflorian fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
537*096314feSflorian fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
538*096314feSflorian fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
539*096314feSflorian fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
540*096314feSflorian
541*096314feSflorian if(cb_arg == NULL) cb_arg = table->cb_arg;
542*096314feSflorian
543*096314feSflorian /* update space used */
544*096314feSflorian lock_quick_lock(&table->lock);
545*096314feSflorian
546*096314feSflorian if((int)table->space_used + diff_size < 0)
547*096314feSflorian table->space_used = 0;
548*096314feSflorian else table->space_used = (size_t)((int)table->space_used + diff_size);
549*096314feSflorian
550*096314feSflorian if(table->space_used > table->space_max)
551*096314feSflorian reclaim_space(table, &reclaimlist);
552*096314feSflorian
553*096314feSflorian lock_quick_unlock(&table->lock);
554*096314feSflorian
555*096314feSflorian /* finish reclaim if any (outside of critical region) */
556*096314feSflorian while(reclaimlist) {
557*096314feSflorian struct lruhash_entry* n = reclaimlist->overflow_next;
558*096314feSflorian void* d = reclaimlist->data;
559*096314feSflorian (*table->delkeyfunc)(reclaimlist->key, cb_arg);
560*096314feSflorian (*table->deldatafunc)(d, cb_arg);
561*096314feSflorian reclaimlist = n;
562*096314feSflorian }
563*096314feSflorian }
564*096314feSflorian
565*096314feSflorian void
lruhash_traverse(struct lruhash * h,int wr,void (* func)(struct lruhash_entry *,void *),void * arg)566ae8c6e27Sflorian lruhash_traverse(struct lruhash* h, int wr,
567ae8c6e27Sflorian void (*func)(struct lruhash_entry*, void*), void* arg)
568ae8c6e27Sflorian {
569ae8c6e27Sflorian size_t i;
570ae8c6e27Sflorian struct lruhash_entry* e;
571ae8c6e27Sflorian
572ae8c6e27Sflorian lock_quick_lock(&h->lock);
573ae8c6e27Sflorian for(i=0; i<h->size; i++) {
574ae8c6e27Sflorian lock_quick_lock(&h->array[i].lock);
575ae8c6e27Sflorian for(e = h->array[i].overflow_list; e; e = e->overflow_next) {
576ae8c6e27Sflorian if(wr) {
577ae8c6e27Sflorian lock_rw_wrlock(&e->lock);
578ae8c6e27Sflorian } else {
579ae8c6e27Sflorian lock_rw_rdlock(&e->lock);
580ae8c6e27Sflorian }
581ae8c6e27Sflorian (*func)(e, arg);
582ae8c6e27Sflorian lock_rw_unlock(&e->lock);
583ae8c6e27Sflorian }
584ae8c6e27Sflorian lock_quick_unlock(&h->array[i].lock);
585ae8c6e27Sflorian }
586ae8c6e27Sflorian lock_quick_unlock(&h->lock);
587ae8c6e27Sflorian }
588ae8c6e27Sflorian
589ae8c6e27Sflorian /*
590ae8c6e27Sflorian * Demote: the opposite of touch, move an entry to the bottom
591ae8c6e27Sflorian * of the LRU pile.
592ae8c6e27Sflorian */
593ae8c6e27Sflorian
594ae8c6e27Sflorian void
lru_demote(struct lruhash * table,struct lruhash_entry * entry)595ae8c6e27Sflorian lru_demote(struct lruhash* table, struct lruhash_entry* entry)
596ae8c6e27Sflorian {
597ae8c6e27Sflorian log_assert(table && entry);
598ae8c6e27Sflorian if (entry == table->lru_end)
599ae8c6e27Sflorian return; /* nothing to do */
600ae8c6e27Sflorian /* remove from current lru position */
601ae8c6e27Sflorian lru_remove(table, entry);
602ae8c6e27Sflorian /* add at end */
603ae8c6e27Sflorian entry->lru_next = NULL;
604ae8c6e27Sflorian entry->lru_prev = table->lru_end;
605ae8c6e27Sflorian
606ae8c6e27Sflorian if (table->lru_end == NULL)
607ae8c6e27Sflorian {
608ae8c6e27Sflorian table->lru_start = entry;
609ae8c6e27Sflorian }
610ae8c6e27Sflorian else
611ae8c6e27Sflorian {
612ae8c6e27Sflorian table->lru_end->lru_next = entry;
613ae8c6e27Sflorian }
614ae8c6e27Sflorian table->lru_end = entry;
615ae8c6e27Sflorian }
616ae8c6e27Sflorian
617ae8c6e27Sflorian struct lruhash_entry*
lruhash_insert_or_retrieve(struct lruhash * table,hashvalue_type hash,struct lruhash_entry * entry,void * data,void * cb_arg)618ae8c6e27Sflorian lruhash_insert_or_retrieve(struct lruhash* table, hashvalue_type hash,
619ae8c6e27Sflorian struct lruhash_entry* entry, void* data, void* cb_arg)
620ae8c6e27Sflorian {
621ae8c6e27Sflorian struct lruhash_bin* bin;
622ae8c6e27Sflorian struct lruhash_entry* found, *reclaimlist = NULL;
623ae8c6e27Sflorian size_t need_size;
624d500c338Sflorian size_t collisions;
625ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
626ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
627ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
628ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc));
629ae8c6e27Sflorian fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
630ae8c6e27Sflorian need_size = table->sizefunc(entry->key, data);
631ae8c6e27Sflorian if (cb_arg == NULL) cb_arg = table->cb_arg;
632ae8c6e27Sflorian
633ae8c6e27Sflorian /* find bin */
634ae8c6e27Sflorian lock_quick_lock(&table->lock);
635ae8c6e27Sflorian bin = &table->array[hash & table->size_mask];
636ae8c6e27Sflorian lock_quick_lock(&bin->lock);
637ae8c6e27Sflorian
638ae8c6e27Sflorian /* see if entry exists already */
639d500c338Sflorian if ((found = bin_find_entry(table, bin, hash, entry->key, &collisions)) != NULL) {
640ae8c6e27Sflorian /* if so: keep the existing data - acquire a writelock */
641ae8c6e27Sflorian lock_rw_wrlock(&found->lock);
642ae8c6e27Sflorian }
643ae8c6e27Sflorian else
644ae8c6e27Sflorian {
645ae8c6e27Sflorian /* if not: add to bin */
646ae8c6e27Sflorian entry->overflow_next = bin->overflow_list;
647ae8c6e27Sflorian bin->overflow_list = entry;
648ae8c6e27Sflorian lru_front(table, entry);
649ae8c6e27Sflorian table->num++;
650d500c338Sflorian if (table->max_collisions < collisions)
651d500c338Sflorian table->max_collisions = collisions;
652ae8c6e27Sflorian table->space_used += need_size;
653ae8c6e27Sflorian /* return the entry that was presented, and lock it */
654ae8c6e27Sflorian found = entry;
655ae8c6e27Sflorian lock_rw_wrlock(&found->lock);
656ae8c6e27Sflorian }
657ae8c6e27Sflorian lock_quick_unlock(&bin->lock);
658ae8c6e27Sflorian if (table->space_used > table->space_max)
659ae8c6e27Sflorian reclaim_space(table, &reclaimlist);
660ae8c6e27Sflorian if (table->num >= table->size)
661ae8c6e27Sflorian table_grow(table);
662ae8c6e27Sflorian lock_quick_unlock(&table->lock);
663ae8c6e27Sflorian
664ae8c6e27Sflorian /* finish reclaim if any (outside of critical region) */
665ae8c6e27Sflorian while (reclaimlist) {
666ae8c6e27Sflorian struct lruhash_entry* n = reclaimlist->overflow_next;
667ae8c6e27Sflorian void* d = reclaimlist->data;
668ae8c6e27Sflorian (*table->delkeyfunc)(reclaimlist->key, cb_arg);
669ae8c6e27Sflorian (*table->deldatafunc)(d, cb_arg);
670ae8c6e27Sflorian reclaimlist = n;
671ae8c6e27Sflorian }
672ae8c6e27Sflorian
673ae8c6e27Sflorian /* return the entry that was selected */
674ae8c6e27Sflorian return found;
675ae8c6e27Sflorian }
676ae8c6e27Sflorian
677