2 * util/storage/lruhash.c - hashtable, hash function, LRU keeping.
4 * Copyright (c) 2007, NLnet Labs. All rights reserved.
6 * This software is open source.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 * This file contains a hashtable with LRU keeping of entries.
44 #include "util/storage/lruhash.h"
45 #include "util/fptr_wlist.h"
48 bin_init(struct lruhash_bin
* array
, size_t size
)
51 #ifdef THREADS_DISABLED
54 for(i
=0; i
<size
; i
++) {
55 lock_quick_init(&array
[i
].lock
);
56 lock_protect(&array
[i
].lock
, &array
[i
],
57 sizeof(struct lruhash_bin
));
62 lruhash_create(size_t start_size
, size_t maxmem
, lruhash_sizefunc_t sizefunc
,
63 lruhash_compfunc_t compfunc
, lruhash_delkeyfunc_t delkeyfunc
,
64 lruhash_deldatafunc_t deldatafunc
, void* arg
)
66 struct lruhash
* table
= (struct lruhash
*)calloc(1,
67 sizeof(struct lruhash
));
70 lock_quick_init(&table
->lock
);
71 table
->sizefunc
= sizefunc
;
72 table
->compfunc
= compfunc
;
73 table
->delkeyfunc
= delkeyfunc
;
74 table
->deldatafunc
= deldatafunc
;
76 table
->size
= start_size
;
77 table
->size_mask
= (int)(start_size
-1);
78 table
->lru_start
= NULL
;
79 table
->lru_end
= NULL
;
81 table
->space_used
= 0;
82 table
->space_max
= maxmem
;
83 table
->array
= calloc(table
->size
, sizeof(struct lruhash_bin
));
85 lock_quick_destroy(&table
->lock
);
89 bin_init(table
->array
, table
->size
);
90 lock_protect(&table
->lock
, table
, sizeof(*table
));
91 lock_protect(&table
->lock
, table
->array
,
92 table
->size
*sizeof(struct lruhash_bin
));
97 bin_delete(struct lruhash
* table
, struct lruhash_bin
* bin
)
99 struct lruhash_entry
* p
, *np
;
103 lock_quick_destroy(&bin
->lock
);
104 p
= bin
->overflow_list
;
105 bin
->overflow_list
= NULL
;
107 np
= p
->overflow_next
;
109 (*table
->delkeyfunc
)(p
->key
, table
->cb_arg
);
110 (*table
->deldatafunc
)(d
, table
->cb_arg
);
116 bin_split(struct lruhash
* table
, struct lruhash_bin
* newa
,
120 struct lruhash_entry
*p
, *np
;
121 struct lruhash_bin
* newbin
;
122 /* move entries to new table. Notice that since hash x is mapped to
123 * bin x & mask, and new mask uses one more bit, so all entries in
124 * one bin will go into the old bin or bin | newbit */
125 #ifndef THREADS_DISABLED
126 int newbit
= newmask
- table
->size_mask
;
128 /* so, really, this task could also be threaded, per bin. */
129 /* LRU list is not changed */
130 for(i
=0; i
<table
->size
; i
++)
132 lock_quick_lock(&table
->array
[i
].lock
);
133 p
= table
->array
[i
].overflow_list
;
134 /* lock both destination bins */
135 lock_quick_lock(&newa
[i
].lock
);
136 lock_quick_lock(&newa
[newbit
|i
].lock
);
138 np
= p
->overflow_next
;
139 /* link into correct new bin */
140 newbin
= &newa
[p
->hash
& newmask
];
141 p
->overflow_next
= newbin
->overflow_list
;
142 newbin
->overflow_list
= p
;
145 lock_quick_unlock(&newa
[i
].lock
);
146 lock_quick_unlock(&newa
[newbit
|i
].lock
);
147 lock_quick_unlock(&table
->array
[i
].lock
);
152 lruhash_delete(struct lruhash
* table
)
157 /* delete lock on hashtable to force check its OK */
158 lock_quick_destroy(&table
->lock
);
159 for(i
=0; i
<table
->size
; i
++)
160 bin_delete(table
, &table
->array
[i
]);
166 bin_overflow_remove(struct lruhash_bin
* bin
, struct lruhash_entry
* entry
)
168 struct lruhash_entry
* p
= bin
->overflow_list
;
169 struct lruhash_entry
** prevp
= &bin
->overflow_list
;
172 *prevp
= p
->overflow_next
;
175 prevp
= &p
->overflow_next
;
176 p
= p
->overflow_next
;
181 reclaim_space(struct lruhash
* table
, struct lruhash_entry
** list
)
183 struct lruhash_entry
* d
;
184 struct lruhash_bin
* bin
;
186 /* does not delete MRU entry, so table will not be empty. */
187 while(table
->num
> 1 && table
->space_used
> table
->space_max
) {
188 /* notice that since we hold the hashtable lock, nobody
189 can change the lru chain. So it cannot be deleted underneath
190 us. We still need the hashbin and entry write lock to make
191 sure we flush all users away from the entry.
192 which is unlikely, since it is LRU, if someone got a rdlock
193 it would be moved to front, but to be sure. */
195 /* specialised, delete from end of double linked list,
196 and we know num>1, so there is a previous lru entry. */
197 log_assert(d
&& d
->lru_prev
);
198 table
->lru_end
= d
->lru_prev
;
199 d
->lru_prev
->lru_next
= NULL
;
200 /* schedule entry for deletion */
201 bin
= &table
->array
[d
->hash
& table
->size_mask
];
203 lock_quick_lock(&bin
->lock
);
204 bin_overflow_remove(bin
, d
);
205 d
->overflow_next
= *list
;
207 lock_rw_wrlock(&d
->lock
);
208 table
->space_used
-= table
->sizefunc(d
->key
, d
->data
);
209 if(table
->markdelfunc
)
210 (*table
->markdelfunc
)(d
->key
);
211 lock_rw_unlock(&d
->lock
);
212 lock_quick_unlock(&bin
->lock
);
216 struct lruhash_entry
*
217 bin_find_entry(struct lruhash
* table
,
218 struct lruhash_bin
* bin
, hashvalue_t hash
, void* key
)
220 struct lruhash_entry
* p
= bin
->overflow_list
;
222 if(p
->hash
== hash
&& table
->compfunc(p
->key
, key
) == 0)
224 p
= p
->overflow_next
;
230 table_grow(struct lruhash
* table
)
232 struct lruhash_bin
* newa
;
235 if(table
->size_mask
== (int)(((size_t)-1)>>1)) {
236 log_err("hash array malloc: size_t too small");
239 /* try to allocate new array, if not fail */
240 newa
= calloc(table
->size
*2, sizeof(struct lruhash_bin
));
242 log_err("hash grow: malloc failed");
243 /* continue with smaller array. Though its slower. */
246 bin_init(newa
, table
->size
*2);
247 newmask
= (table
->size_mask
<< 1) | 1;
248 bin_split(table
, newa
, newmask
);
249 /* delete the old bins */
250 lock_unprotect(&table
->lock
, table
->array
);
251 for(i
=0; i
<table
->size
; i
++) {
252 lock_quick_destroy(&table
->array
[i
].lock
);
257 table
->size_mask
= newmask
;
259 lock_protect(&table
->lock
, table
->array
,
260 table
->size
*sizeof(struct lruhash_bin
));
265 lru_front(struct lruhash
* table
, struct lruhash_entry
* entry
)
267 entry
->lru_prev
= NULL
;
268 entry
->lru_next
= table
->lru_start
;
269 if(!table
->lru_start
)
270 table
->lru_end
= entry
;
271 else table
->lru_start
->lru_prev
= entry
;
272 table
->lru_start
= entry
;
276 lru_remove(struct lruhash
* table
, struct lruhash_entry
* entry
)
279 entry
->lru_prev
->lru_next
= entry
->lru_next
;
280 else table
->lru_start
= entry
->lru_next
;
282 entry
->lru_next
->lru_prev
= entry
->lru_prev
;
283 else table
->lru_end
= entry
->lru_prev
;
287 lru_touch(struct lruhash
* table
, struct lruhash_entry
* entry
)
289 log_assert(table
&& entry
);
290 if(entry
== table
->lru_start
)
291 return; /* nothing to do */
292 /* remove from current lru position */
293 lru_remove(table
, entry
);
295 lru_front(table
, entry
);
299 lruhash_insert(struct lruhash
* table
, hashvalue_t hash
,
300 struct lruhash_entry
* entry
, void* data
, void* cb_arg
)
302 struct lruhash_bin
* bin
;
303 struct lruhash_entry
* found
, *reclaimlist
=NULL
;
305 fptr_ok(fptr_whitelist_hash_sizefunc(table
->sizefunc
));
306 fptr_ok(fptr_whitelist_hash_delkeyfunc(table
->delkeyfunc
));
307 fptr_ok(fptr_whitelist_hash_deldatafunc(table
->deldatafunc
));
308 fptr_ok(fptr_whitelist_hash_compfunc(table
->compfunc
));
309 fptr_ok(fptr_whitelist_hash_markdelfunc(table
->markdelfunc
));
310 need_size
= table
->sizefunc(entry
->key
, data
);
311 if(cb_arg
== NULL
) cb_arg
= table
->cb_arg
;
314 lock_quick_lock(&table
->lock
);
315 bin
= &table
->array
[hash
& table
->size_mask
];
316 lock_quick_lock(&bin
->lock
);
318 /* see if entry exists already */
319 if(!(found
=bin_find_entry(table
, bin
, hash
, entry
->key
))) {
320 /* if not: add to bin */
321 entry
->overflow_next
= bin
->overflow_list
;
322 bin
->overflow_list
= entry
;
323 lru_front(table
, entry
);
325 table
->space_used
+= need_size
;
327 /* if so: update data - needs a writelock */
328 table
->space_used
+= need_size
-
329 (*table
->sizefunc
)(found
->key
, found
->data
);
330 (*table
->delkeyfunc
)(entry
->key
, cb_arg
);
331 lru_touch(table
, found
);
332 lock_rw_wrlock(&found
->lock
);
333 (*table
->deldatafunc
)(found
->data
, cb_arg
);
335 lock_rw_unlock(&found
->lock
);
337 lock_quick_unlock(&bin
->lock
);
338 if(table
->space_used
> table
->space_max
)
339 reclaim_space(table
, &reclaimlist
);
340 if(table
->num
>= table
->size
)
342 lock_quick_unlock(&table
->lock
);
344 /* finish reclaim if any (outside of critical region) */
346 struct lruhash_entry
* n
= reclaimlist
->overflow_next
;
347 void* d
= reclaimlist
->data
;
348 (*table
->delkeyfunc
)(reclaimlist
->key
, cb_arg
);
349 (*table
->deldatafunc
)(d
, cb_arg
);
354 struct lruhash_entry
*
355 lruhash_lookup(struct lruhash
* table
, hashvalue_t hash
, void* key
, int wr
)
357 struct lruhash_entry
* entry
;
358 struct lruhash_bin
* bin
;
359 fptr_ok(fptr_whitelist_hash_compfunc(table
->compfunc
));
361 lock_quick_lock(&table
->lock
);
362 bin
= &table
->array
[hash
& table
->size_mask
];
363 lock_quick_lock(&bin
->lock
);
364 if((entry
=bin_find_entry(table
, bin
, hash
, key
)))
365 lru_touch(table
, entry
);
366 lock_quick_unlock(&table
->lock
);
369 if(wr
) { lock_rw_wrlock(&entry
->lock
); }
370 else { lock_rw_rdlock(&entry
->lock
); }
372 lock_quick_unlock(&bin
->lock
);
377 lruhash_remove(struct lruhash
* table
, hashvalue_t hash
, void* key
)
379 struct lruhash_entry
* entry
;
380 struct lruhash_bin
* bin
;
382 fptr_ok(fptr_whitelist_hash_sizefunc(table
->sizefunc
));
383 fptr_ok(fptr_whitelist_hash_delkeyfunc(table
->delkeyfunc
));
384 fptr_ok(fptr_whitelist_hash_deldatafunc(table
->deldatafunc
));
385 fptr_ok(fptr_whitelist_hash_compfunc(table
->compfunc
));
386 fptr_ok(fptr_whitelist_hash_markdelfunc(table
->markdelfunc
));
388 lock_quick_lock(&table
->lock
);
389 bin
= &table
->array
[hash
& table
->size_mask
];
390 lock_quick_lock(&bin
->lock
);
391 if((entry
=bin_find_entry(table
, bin
, hash
, key
))) {
392 bin_overflow_remove(bin
, entry
);
393 lru_remove(table
, entry
);
395 lock_quick_unlock(&table
->lock
);
396 lock_quick_unlock(&bin
->lock
);
400 table
->space_used
-= (*table
->sizefunc
)(entry
->key
, entry
->data
);
401 lock_quick_unlock(&table
->lock
);
402 lock_rw_wrlock(&entry
->lock
);
403 if(table
->markdelfunc
)
404 (*table
->markdelfunc
)(entry
->key
);
405 lock_rw_unlock(&entry
->lock
);
406 lock_quick_unlock(&bin
->lock
);
409 (*table
->delkeyfunc
)(entry
->key
, table
->cb_arg
);
410 (*table
->deldatafunc
)(d
, table
->cb_arg
);
413 /** clear bin, respecting locks, does not do space, LRU */
415 bin_clear(struct lruhash
* table
, struct lruhash_bin
* bin
)
417 struct lruhash_entry
* p
, *np
;
419 lock_quick_lock(&bin
->lock
);
420 p
= bin
->overflow_list
;
422 lock_rw_wrlock(&p
->lock
);
423 np
= p
->overflow_next
;
425 if(table
->markdelfunc
)
426 (*table
->markdelfunc
)(p
->key
);
427 lock_rw_unlock(&p
->lock
);
428 (*table
->delkeyfunc
)(p
->key
, table
->cb_arg
);
429 (*table
->deldatafunc
)(d
, table
->cb_arg
);
432 bin
->overflow_list
= NULL
;
433 lock_quick_unlock(&bin
->lock
);
437 lruhash_clear(struct lruhash
* table
)
442 fptr_ok(fptr_whitelist_hash_delkeyfunc(table
->delkeyfunc
));
443 fptr_ok(fptr_whitelist_hash_deldatafunc(table
->deldatafunc
));
444 fptr_ok(fptr_whitelist_hash_markdelfunc(table
->markdelfunc
));
446 lock_quick_lock(&table
->lock
);
447 for(i
=0; i
<table
->size
; i
++) {
448 bin_clear(table
, &table
->array
[i
]);
450 table
->lru_start
= NULL
;
451 table
->lru_end
= NULL
;
453 table
->space_used
= 0;
454 lock_quick_unlock(&table
->lock
);
458 lruhash_status(struct lruhash
* table
, const char* id
, int extended
)
460 lock_quick_lock(&table
->lock
);
461 log_info("%s: %u entries, memory %u / %u",
462 id
, (unsigned)table
->num
, (unsigned)table
->space_used
,
463 (unsigned)table
->space_max
);
464 log_info(" itemsize %u, array %u, mask %d",
465 (unsigned)(table
->num
? table
->space_used
/table
->num
: 0),
466 (unsigned)table
->size
, table
->size_mask
);
469 int min
=(int)table
->size
*2, max
=-2;
470 for(i
=0; i
<table
->size
; i
++) {
472 struct lruhash_entry
*en
;
473 lock_quick_lock(&table
->array
[i
].lock
);
474 en
= table
->array
[i
].overflow_list
;
477 en
= en
->overflow_next
;
479 lock_quick_unlock(&table
->array
[i
].lock
);
481 log_info("bin[%d] %d", (int)i
, here
);
482 if(here
> max
) max
= here
;
483 if(here
< min
) min
= here
;
485 log_info(" bin min %d, avg %.2lf, max %d", min
,
486 (double)table
->num
/(double)table
->size
, max
);
488 lock_quick_unlock(&table
->lock
);
492 lruhash_get_mem(struct lruhash
* table
)
495 lock_quick_lock(&table
->lock
);
496 s
= sizeof(struct lruhash
) + table
->space_used
;
497 #ifdef USE_THREAD_DEBUG
498 if(table
->size
!= 0) {
500 for(i
=0; i
<table
->size
; i
++)
501 s
+= sizeof(struct lruhash_bin
) +
502 lock_get_mem(&table
->array
[i
].lock
);
504 #else /* no THREAD_DEBUG */
506 s
+= (table
->size
)*(sizeof(struct lruhash_bin
) +
507 lock_get_mem(&table
->array
[0].lock
));
509 lock_quick_unlock(&table
->lock
);
510 s
+= lock_get_mem(&table
->lock
);
515 lruhash_setmarkdel(struct lruhash
* table
, lruhash_markdelfunc_t md
)
517 lock_quick_lock(&table
->lock
);
518 table
->markdelfunc
= md
;
519 lock_quick_unlock(&table
->lock
);
523 lruhash_traverse(struct lruhash
* h
, int wr
,
524 void (*func
)(struct lruhash_entry
*, void*), void* arg
)
527 struct lruhash_entry
* e
;
529 lock_quick_lock(&h
->lock
);
530 for(i
=0; i
<h
->size
; i
++) {
531 lock_quick_lock(&h
->array
[i
].lock
);
532 for(e
= h
->array
[i
].overflow_list
; e
; e
= e
->overflow_next
) {
534 lock_rw_wrlock(&e
->lock
);
536 lock_rw_rdlock(&e
->lock
);
539 lock_rw_unlock(&e
->lock
);
541 lock_quick_unlock(&h
->array
[i
].lock
);
543 lock_quick_unlock(&h
->lock
);