1b7579f77SDag-Erling Smørgrav /* 2b7579f77SDag-Erling Smørgrav * util/storage/lruhash.c - hashtable, hash function, LRU keeping. 3b7579f77SDag-Erling Smørgrav * 4b7579f77SDag-Erling Smørgrav * Copyright (c) 2007, NLnet Labs. All rights reserved. 5b7579f77SDag-Erling Smørgrav * 6b7579f77SDag-Erling Smørgrav * This software is open source. 7b7579f77SDag-Erling Smørgrav * 8b7579f77SDag-Erling Smørgrav * Redistribution and use in source and binary forms, with or without 9b7579f77SDag-Erling Smørgrav * modification, are permitted provided that the following conditions 10b7579f77SDag-Erling Smørgrav * are met: 11b7579f77SDag-Erling Smørgrav * 12b7579f77SDag-Erling Smørgrav * Redistributions of source code must retain the above copyright notice, 13b7579f77SDag-Erling Smørgrav * this list of conditions and the following disclaimer. 14b7579f77SDag-Erling Smørgrav * 15b7579f77SDag-Erling Smørgrav * Redistributions in binary form must reproduce the above copyright notice, 16b7579f77SDag-Erling Smørgrav * this list of conditions and the following disclaimer in the documentation 17b7579f77SDag-Erling Smørgrav * and/or other materials provided with the distribution. 18b7579f77SDag-Erling Smørgrav * 19b7579f77SDag-Erling Smørgrav * Neither the name of the NLNET LABS nor the names of its contributors may 20b7579f77SDag-Erling Smørgrav * be used to endorse or promote products derived from this software without 21b7579f77SDag-Erling Smørgrav * specific prior written permission. 22b7579f77SDag-Erling Smørgrav * 23b7579f77SDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2417d15b25SDag-Erling Smørgrav * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2517d15b25SDag-Erling Smørgrav * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2617d15b25SDag-Erling Smørgrav * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2717d15b25SDag-Erling Smørgrav * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2817d15b25SDag-Erling Smørgrav * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 2917d15b25SDag-Erling Smørgrav * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 3017d15b25SDag-Erling Smørgrav * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 3117d15b25SDag-Erling Smørgrav * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 3217d15b25SDag-Erling Smørgrav * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 3317d15b25SDag-Erling Smørgrav * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34b7579f77SDag-Erling Smørgrav */ 35b7579f77SDag-Erling Smørgrav 36b7579f77SDag-Erling Smørgrav /** 37b7579f77SDag-Erling Smørgrav * \file 38b7579f77SDag-Erling Smørgrav * 39b7579f77SDag-Erling Smørgrav * This file contains a hashtable with LRU keeping of entries. 40b7579f77SDag-Erling Smørgrav * 41b7579f77SDag-Erling Smørgrav */ 42b7579f77SDag-Erling Smørgrav 43b7579f77SDag-Erling Smørgrav #include "config.h" 44b7579f77SDag-Erling Smørgrav #include "util/storage/lruhash.h" 45b7579f77SDag-Erling Smørgrav #include "util/fptr_wlist.h" 46b7579f77SDag-Erling Smørgrav 47b7579f77SDag-Erling Smørgrav void 48b7579f77SDag-Erling Smørgrav bin_init(struct lruhash_bin* array, size_t size) 49b7579f77SDag-Erling Smørgrav { 50b7579f77SDag-Erling Smørgrav size_t i; 51b7579f77SDag-Erling Smørgrav #ifdef THREADS_DISABLED 52b7579f77SDag-Erling Smørgrav (void)array; 53b7579f77SDag-Erling Smørgrav #endif 54b7579f77SDag-Erling Smørgrav for(i=0; i<size; i++) { 55b7579f77SDag-Erling Smørgrav lock_quick_init(&array[i].lock); 56b7579f77SDag-Erling Smørgrav lock_protect(&array[i].lock, &array[i], 57b7579f77SDag-Erling Smørgrav sizeof(struct lruhash_bin)); 58b7579f77SDag-Erling Smørgrav } 59b7579f77SDag-Erling Smørgrav } 60b7579f77SDag-Erling Smørgrav 61b7579f77SDag-Erling Smørgrav struct lruhash* 623005e0a3SDag-Erling Smørgrav lruhash_create(size_t start_size, size_t maxmem, 633005e0a3SDag-Erling Smørgrav lruhash_sizefunc_type sizefunc, lruhash_compfunc_type compfunc, 643005e0a3SDag-Erling Smørgrav lruhash_delkeyfunc_type delkeyfunc, 653005e0a3SDag-Erling Smørgrav lruhash_deldatafunc_type deldatafunc, void* arg) 66b7579f77SDag-Erling Smørgrav { 67b7579f77SDag-Erling Smørgrav struct lruhash* table = (struct lruhash*)calloc(1, 68b7579f77SDag-Erling Smørgrav sizeof(struct lruhash)); 69b7579f77SDag-Erling Smørgrav if(!table) 70b7579f77SDag-Erling Smørgrav return NULL; 71b7579f77SDag-Erling Smørgrav lock_quick_init(&table->lock); 72b7579f77SDag-Erling Smørgrav table->sizefunc = sizefunc; 73b7579f77SDag-Erling Smørgrav table->compfunc = compfunc; 74b7579f77SDag-Erling Smørgrav table->delkeyfunc = delkeyfunc; 75b7579f77SDag-Erling Smørgrav table->deldatafunc = deldatafunc; 76b7579f77SDag-Erling Smørgrav table->cb_arg = arg; 77b7579f77SDag-Erling Smørgrav table->size = start_size; 78b7579f77SDag-Erling Smørgrav table->size_mask = (int)(start_size-1); 79b7579f77SDag-Erling Smørgrav table->lru_start = NULL; 80b7579f77SDag-Erling Smørgrav table->lru_end = NULL; 81b7579f77SDag-Erling Smørgrav table->num = 0; 82b7579f77SDag-Erling Smørgrav table->space_used = 0; 83b7579f77SDag-Erling Smørgrav table->space_max = maxmem; 848f76bb7dSCy Schubert table->max_collisions = 0; 85b7579f77SDag-Erling Smørgrav table->array = calloc(table->size, sizeof(struct lruhash_bin)); 86b7579f77SDag-Erling Smørgrav if(!table->array) { 87b7579f77SDag-Erling Smørgrav lock_quick_destroy(&table->lock); 88b7579f77SDag-Erling Smørgrav free(table); 89b7579f77SDag-Erling Smørgrav return NULL; 90b7579f77SDag-Erling Smørgrav } 91b7579f77SDag-Erling Smørgrav bin_init(table->array, table->size); 92b7579f77SDag-Erling Smørgrav lock_protect(&table->lock, table, sizeof(*table)); 93b7579f77SDag-Erling Smørgrav lock_protect(&table->lock, table->array, 94b7579f77SDag-Erling Smørgrav table->size*sizeof(struct lruhash_bin)); 95b7579f77SDag-Erling Smørgrav return table; 96b7579f77SDag-Erling Smørgrav } 97b7579f77SDag-Erling Smørgrav 98b7579f77SDag-Erling Smørgrav void 99b7579f77SDag-Erling Smørgrav bin_delete(struct lruhash* table, struct lruhash_bin* bin) 100b7579f77SDag-Erling Smørgrav { 101b7579f77SDag-Erling Smørgrav struct lruhash_entry* p, *np; 102b7579f77SDag-Erling Smørgrav void *d; 103b7579f77SDag-Erling Smørgrav if(!bin) 104b7579f77SDag-Erling Smørgrav return; 105b7579f77SDag-Erling Smørgrav lock_quick_destroy(&bin->lock); 106b7579f77SDag-Erling Smørgrav p = bin->overflow_list; 107b7579f77SDag-Erling Smørgrav bin->overflow_list = NULL; 108b7579f77SDag-Erling Smørgrav while(p) { 109b7579f77SDag-Erling Smørgrav np = p->overflow_next; 110b7579f77SDag-Erling Smørgrav d = p->data; 111b7579f77SDag-Erling Smørgrav (*table->delkeyfunc)(p->key, table->cb_arg); 112b7579f77SDag-Erling Smørgrav (*table->deldatafunc)(d, table->cb_arg); 113b7579f77SDag-Erling Smørgrav p = np; 114b7579f77SDag-Erling Smørgrav } 115b7579f77SDag-Erling Smørgrav } 116b7579f77SDag-Erling Smørgrav 117b7579f77SDag-Erling Smørgrav void 118b7579f77SDag-Erling Smørgrav bin_split(struct lruhash* table, struct lruhash_bin* newa, 119b7579f77SDag-Erling Smørgrav int newmask) 120b7579f77SDag-Erling Smørgrav { 121b7579f77SDag-Erling Smørgrav size_t i; 122b7579f77SDag-Erling Smørgrav struct lruhash_entry *p, *np; 123b7579f77SDag-Erling Smørgrav struct lruhash_bin* newbin; 124b7579f77SDag-Erling Smørgrav /* move entries to new table. Notice that since hash x is mapped to 125b7579f77SDag-Erling Smørgrav * bin x & mask, and new mask uses one more bit, so all entries in 126b7579f77SDag-Erling Smørgrav * one bin will go into the old bin or bin | newbit */ 127b7579f77SDag-Erling Smørgrav #ifndef THREADS_DISABLED 128b7579f77SDag-Erling Smørgrav int newbit = newmask - table->size_mask; 129b7579f77SDag-Erling Smørgrav #endif 130b7579f77SDag-Erling Smørgrav /* so, really, this task could also be threaded, per bin. */ 131b7579f77SDag-Erling Smørgrav /* LRU list is not changed */ 132b7579f77SDag-Erling Smørgrav for(i=0; i<table->size; i++) 133b7579f77SDag-Erling Smørgrav { 134b7579f77SDag-Erling Smørgrav lock_quick_lock(&table->array[i].lock); 135b7579f77SDag-Erling Smørgrav p = table->array[i].overflow_list; 136b7579f77SDag-Erling Smørgrav /* lock both destination bins */ 137b7579f77SDag-Erling Smørgrav lock_quick_lock(&newa[i].lock); 138b7579f77SDag-Erling Smørgrav lock_quick_lock(&newa[newbit|i].lock); 139b7579f77SDag-Erling Smørgrav while(p) { 140b7579f77SDag-Erling Smørgrav np = p->overflow_next; 141b7579f77SDag-Erling Smørgrav /* link into correct new bin */ 142b7579f77SDag-Erling Smørgrav newbin = &newa[p->hash & newmask]; 143b7579f77SDag-Erling Smørgrav p->overflow_next = newbin->overflow_list; 144b7579f77SDag-Erling Smørgrav newbin->overflow_list = p; 145b7579f77SDag-Erling Smørgrav p=np; 146b7579f77SDag-Erling Smørgrav } 147b7579f77SDag-Erling Smørgrav lock_quick_unlock(&newa[i].lock); 148b7579f77SDag-Erling Smørgrav lock_quick_unlock(&newa[newbit|i].lock); 149b7579f77SDag-Erling Smørgrav lock_quick_unlock(&table->array[i].lock); 150b7579f77SDag-Erling Smørgrav } 151b7579f77SDag-Erling Smørgrav } 152b7579f77SDag-Erling Smørgrav 153b7579f77SDag-Erling Smørgrav void 154b7579f77SDag-Erling Smørgrav lruhash_delete(struct lruhash* table) 155b7579f77SDag-Erling Smørgrav { 156b7579f77SDag-Erling Smørgrav size_t i; 157b7579f77SDag-Erling Smørgrav if(!table) 158b7579f77SDag-Erling Smørgrav return; 159b7579f77SDag-Erling Smørgrav /* delete lock on hashtable to force check its OK */ 160b7579f77SDag-Erling Smørgrav lock_quick_destroy(&table->lock); 161b7579f77SDag-Erling Smørgrav for(i=0; i<table->size; i++) 162b7579f77SDag-Erling Smørgrav bin_delete(table, &table->array[i]); 163b7579f77SDag-Erling Smørgrav free(table->array); 164b7579f77SDag-Erling Smørgrav free(table); 165b7579f77SDag-Erling Smørgrav } 166b7579f77SDag-Erling Smørgrav 167b7579f77SDag-Erling Smørgrav void 168b7579f77SDag-Erling Smørgrav bin_overflow_remove(struct lruhash_bin* bin, struct lruhash_entry* entry) 169b7579f77SDag-Erling Smørgrav { 170b7579f77SDag-Erling Smørgrav struct lruhash_entry* p = bin->overflow_list; 171b7579f77SDag-Erling Smørgrav struct lruhash_entry** prevp = &bin->overflow_list; 172b7579f77SDag-Erling Smørgrav while(p) { 173b7579f77SDag-Erling Smørgrav if(p == entry) { 174b7579f77SDag-Erling Smørgrav *prevp = p->overflow_next; 175b7579f77SDag-Erling Smørgrav return; 176b7579f77SDag-Erling Smørgrav } 177b7579f77SDag-Erling Smørgrav prevp = &p->overflow_next; 178b7579f77SDag-Erling Smørgrav p = p->overflow_next; 179b7579f77SDag-Erling Smørgrav } 180b7579f77SDag-Erling Smørgrav } 181b7579f77SDag-Erling Smørgrav 182b7579f77SDag-Erling Smørgrav void 183b7579f77SDag-Erling Smørgrav reclaim_space(struct lruhash* table, struct lruhash_entry** list) 184b7579f77SDag-Erling Smørgrav { 185b7579f77SDag-Erling Smørgrav struct lruhash_entry* d; 186b7579f77SDag-Erling Smørgrav struct lruhash_bin* bin; 187b7579f77SDag-Erling Smørgrav log_assert(table); 188b7579f77SDag-Erling Smørgrav /* does not delete MRU entry, so table will not be empty. */ 189b7579f77SDag-Erling Smørgrav while(table->num > 1 && table->space_used > table->space_max) { 190b7579f77SDag-Erling Smørgrav /* notice that since we hold the hashtable lock, nobody 191b7579f77SDag-Erling Smørgrav can change the lru chain. So it cannot be deleted underneath 192b7579f77SDag-Erling Smørgrav us. We still need the hashbin and entry write lock to make 193b7579f77SDag-Erling Smørgrav sure we flush all users away from the entry. 194b7579f77SDag-Erling Smørgrav which is unlikely, since it is LRU, if someone got a rdlock 195b7579f77SDag-Erling Smørgrav it would be moved to front, but to be sure. */ 196b7579f77SDag-Erling Smørgrav d = table->lru_end; 197b7579f77SDag-Erling Smørgrav /* specialised, delete from end of double linked list, 198b7579f77SDag-Erling Smørgrav and we know num>1, so there is a previous lru entry. */ 199b7579f77SDag-Erling Smørgrav log_assert(d && d->lru_prev); 200b7579f77SDag-Erling Smørgrav table->lru_end = d->lru_prev; 201b7579f77SDag-Erling Smørgrav d->lru_prev->lru_next = NULL; 202b7579f77SDag-Erling Smørgrav /* schedule entry for deletion */ 203b7579f77SDag-Erling Smørgrav bin = &table->array[d->hash & table->size_mask]; 204b7579f77SDag-Erling Smørgrav table->num --; 205b7579f77SDag-Erling Smørgrav lock_quick_lock(&bin->lock); 206b7579f77SDag-Erling Smørgrav bin_overflow_remove(bin, d); 207b7579f77SDag-Erling Smørgrav d->overflow_next = *list; 208b7579f77SDag-Erling Smørgrav *list = d; 209b7579f77SDag-Erling Smørgrav lock_rw_wrlock(&d->lock); 210b7579f77SDag-Erling Smørgrav table->space_used -= table->sizefunc(d->key, d->data); 211b7579f77SDag-Erling Smørgrav if(table->markdelfunc) 212b7579f77SDag-Erling Smørgrav (*table->markdelfunc)(d->key); 213b7579f77SDag-Erling Smørgrav lock_rw_unlock(&d->lock); 214b7579f77SDag-Erling Smørgrav lock_quick_unlock(&bin->lock); 215b7579f77SDag-Erling Smørgrav } 216b7579f77SDag-Erling Smørgrav } 217b7579f77SDag-Erling Smørgrav 218b7579f77SDag-Erling Smørgrav struct lruhash_entry* 219b7579f77SDag-Erling Smørgrav bin_find_entry(struct lruhash* table, 2208f76bb7dSCy Schubert struct lruhash_bin* bin, hashvalue_type hash, void* key, size_t* collisions) 221b7579f77SDag-Erling Smørgrav { 2228f76bb7dSCy Schubert size_t c = 0; 223b7579f77SDag-Erling Smørgrav struct lruhash_entry* p = bin->overflow_list; 224b7579f77SDag-Erling Smørgrav while(p) { 225b7579f77SDag-Erling Smørgrav if(p->hash == hash && table->compfunc(p->key, key) == 0) 2268f76bb7dSCy Schubert break; 2278f76bb7dSCy Schubert c++; 228b7579f77SDag-Erling Smørgrav p = p->overflow_next; 229b7579f77SDag-Erling Smørgrav } 2308f76bb7dSCy Schubert if (collisions != NULL) 2318f76bb7dSCy Schubert *collisions = c; 2328f76bb7dSCy Schubert return p; 233b7579f77SDag-Erling Smørgrav } 234b7579f77SDag-Erling Smørgrav 235b7579f77SDag-Erling Smørgrav void 236b7579f77SDag-Erling Smørgrav table_grow(struct lruhash* table) 237b7579f77SDag-Erling Smørgrav { 238b7579f77SDag-Erling Smørgrav struct lruhash_bin* newa; 239b7579f77SDag-Erling Smørgrav int newmask; 240b7579f77SDag-Erling Smørgrav size_t i; 241b7579f77SDag-Erling Smørgrav if(table->size_mask == (int)(((size_t)-1)>>1)) { 242b7579f77SDag-Erling Smørgrav log_err("hash array malloc: size_t too small"); 243b7579f77SDag-Erling Smørgrav return; 244b7579f77SDag-Erling Smørgrav } 245b7579f77SDag-Erling Smørgrav /* try to allocate new array, if not fail */ 246b7579f77SDag-Erling Smørgrav newa = calloc(table->size*2, sizeof(struct lruhash_bin)); 247b7579f77SDag-Erling Smørgrav if(!newa) { 248b7579f77SDag-Erling Smørgrav log_err("hash grow: malloc failed"); 249b7579f77SDag-Erling Smørgrav /* continue with smaller array. Though its slower. */ 250b7579f77SDag-Erling Smørgrav return; 251b7579f77SDag-Erling Smørgrav } 252b7579f77SDag-Erling Smørgrav bin_init(newa, table->size*2); 253b7579f77SDag-Erling Smørgrav newmask = (table->size_mask << 1) | 1; 254b7579f77SDag-Erling Smørgrav bin_split(table, newa, newmask); 255b7579f77SDag-Erling Smørgrav /* delete the old bins */ 256b7579f77SDag-Erling Smørgrav lock_unprotect(&table->lock, table->array); 257b7579f77SDag-Erling Smørgrav for(i=0; i<table->size; i++) { 258b7579f77SDag-Erling Smørgrav lock_quick_destroy(&table->array[i].lock); 259b7579f77SDag-Erling Smørgrav } 260b7579f77SDag-Erling Smørgrav free(table->array); 261b7579f77SDag-Erling Smørgrav 262b7579f77SDag-Erling Smørgrav table->size *= 2; 263b7579f77SDag-Erling Smørgrav table->size_mask = newmask; 264b7579f77SDag-Erling Smørgrav table->array = newa; 265b7579f77SDag-Erling Smørgrav lock_protect(&table->lock, table->array, 266b7579f77SDag-Erling Smørgrav table->size*sizeof(struct lruhash_bin)); 267b7579f77SDag-Erling Smørgrav return; 268b7579f77SDag-Erling Smørgrav } 269b7579f77SDag-Erling Smørgrav 270b7579f77SDag-Erling Smørgrav void 271b7579f77SDag-Erling Smørgrav lru_front(struct lruhash* table, struct lruhash_entry* entry) 272b7579f77SDag-Erling Smørgrav { 273b7579f77SDag-Erling Smørgrav entry->lru_prev = NULL; 274b7579f77SDag-Erling Smørgrav entry->lru_next = table->lru_start; 275b7579f77SDag-Erling Smørgrav if(!table->lru_start) 276b7579f77SDag-Erling Smørgrav table->lru_end = entry; 277b7579f77SDag-Erling Smørgrav else table->lru_start->lru_prev = entry; 278b7579f77SDag-Erling Smørgrav table->lru_start = entry; 279b7579f77SDag-Erling Smørgrav } 280b7579f77SDag-Erling Smørgrav 281b7579f77SDag-Erling Smørgrav void 282b7579f77SDag-Erling Smørgrav lru_remove(struct lruhash* table, struct lruhash_entry* entry) 283b7579f77SDag-Erling Smørgrav { 284b7579f77SDag-Erling Smørgrav if(entry->lru_prev) 285b7579f77SDag-Erling Smørgrav entry->lru_prev->lru_next = entry->lru_next; 286b7579f77SDag-Erling Smørgrav else table->lru_start = entry->lru_next; 287b7579f77SDag-Erling Smørgrav if(entry->lru_next) 288b7579f77SDag-Erling Smørgrav entry->lru_next->lru_prev = entry->lru_prev; 289b7579f77SDag-Erling Smørgrav else table->lru_end = entry->lru_prev; 290b7579f77SDag-Erling Smørgrav } 291b7579f77SDag-Erling Smørgrav 292b7579f77SDag-Erling Smørgrav void 293b7579f77SDag-Erling Smørgrav lru_touch(struct lruhash* table, struct lruhash_entry* entry) 294b7579f77SDag-Erling Smørgrav { 295b7579f77SDag-Erling Smørgrav log_assert(table && entry); 296b7579f77SDag-Erling Smørgrav if(entry == table->lru_start) 297b7579f77SDag-Erling Smørgrav return; /* nothing to do */ 298b7579f77SDag-Erling Smørgrav /* remove from current lru position */ 299b7579f77SDag-Erling Smørgrav lru_remove(table, entry); 300b7579f77SDag-Erling Smørgrav /* add at front */ 301b7579f77SDag-Erling Smørgrav lru_front(table, entry); 302b7579f77SDag-Erling Smørgrav } 303b7579f77SDag-Erling Smørgrav 304b7579f77SDag-Erling Smørgrav void 3053005e0a3SDag-Erling Smørgrav lruhash_insert(struct lruhash* table, hashvalue_type hash, 306b7579f77SDag-Erling Smørgrav struct lruhash_entry* entry, void* data, void* cb_arg) 307b7579f77SDag-Erling Smørgrav { 308b7579f77SDag-Erling Smørgrav struct lruhash_bin* bin; 309b7579f77SDag-Erling Smørgrav struct lruhash_entry* found, *reclaimlist=NULL; 310b7579f77SDag-Erling Smørgrav size_t need_size; 3118f76bb7dSCy Schubert size_t collisions; 312b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc)); 313b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc)); 314b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc)); 315b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc)); 316b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc)); 317b7579f77SDag-Erling Smørgrav need_size = table->sizefunc(entry->key, data); 318b7579f77SDag-Erling Smørgrav if(cb_arg == NULL) cb_arg = table->cb_arg; 319b7579f77SDag-Erling Smørgrav 320b7579f77SDag-Erling Smørgrav /* find bin */ 321b7579f77SDag-Erling Smørgrav lock_quick_lock(&table->lock); 322b7579f77SDag-Erling Smørgrav bin = &table->array[hash & table->size_mask]; 323b7579f77SDag-Erling Smørgrav lock_quick_lock(&bin->lock); 324b7579f77SDag-Erling Smørgrav 325b7579f77SDag-Erling Smørgrav /* see if entry exists already */ 3268f76bb7dSCy Schubert if(!(found=bin_find_entry(table, bin, hash, entry->key, &collisions))) { 327b7579f77SDag-Erling Smørgrav /* if not: add to bin */ 328b7579f77SDag-Erling Smørgrav entry->overflow_next = bin->overflow_list; 329b7579f77SDag-Erling Smørgrav bin->overflow_list = entry; 330b7579f77SDag-Erling Smørgrav lru_front(table, entry); 331b7579f77SDag-Erling Smørgrav table->num++; 3328f76bb7dSCy Schubert if (table->max_collisions < collisions) 3338f76bb7dSCy Schubert table->max_collisions = collisions; 334b7579f77SDag-Erling Smørgrav table->space_used += need_size; 335b7579f77SDag-Erling Smørgrav } else { 336b7579f77SDag-Erling Smørgrav /* if so: update data - needs a writelock */ 337b7579f77SDag-Erling Smørgrav table->space_used += need_size - 338b7579f77SDag-Erling Smørgrav (*table->sizefunc)(found->key, found->data); 339b7579f77SDag-Erling Smørgrav (*table->delkeyfunc)(entry->key, cb_arg); 340b7579f77SDag-Erling Smørgrav lru_touch(table, found); 341b7579f77SDag-Erling Smørgrav lock_rw_wrlock(&found->lock); 342b7579f77SDag-Erling Smørgrav (*table->deldatafunc)(found->data, cb_arg); 343b7579f77SDag-Erling Smørgrav found->data = data; 344b7579f77SDag-Erling Smørgrav lock_rw_unlock(&found->lock); 345b7579f77SDag-Erling Smørgrav } 346b7579f77SDag-Erling Smørgrav lock_quick_unlock(&bin->lock); 347b7579f77SDag-Erling Smørgrav if(table->space_used > table->space_max) 348b7579f77SDag-Erling Smørgrav reclaim_space(table, &reclaimlist); 349b7579f77SDag-Erling Smørgrav if(table->num >= table->size) 350b7579f77SDag-Erling Smørgrav table_grow(table); 351b7579f77SDag-Erling Smørgrav lock_quick_unlock(&table->lock); 352b7579f77SDag-Erling Smørgrav 353b7579f77SDag-Erling Smørgrav /* finish reclaim if any (outside of critical region) */ 354b7579f77SDag-Erling Smørgrav while(reclaimlist) { 355b7579f77SDag-Erling Smørgrav struct lruhash_entry* n = reclaimlist->overflow_next; 356b7579f77SDag-Erling Smørgrav void* d = reclaimlist->data; 357b7579f77SDag-Erling Smørgrav (*table->delkeyfunc)(reclaimlist->key, cb_arg); 358b7579f77SDag-Erling Smørgrav (*table->deldatafunc)(d, cb_arg); 359b7579f77SDag-Erling Smørgrav reclaimlist = n; 360b7579f77SDag-Erling Smørgrav } 361b7579f77SDag-Erling Smørgrav } 362b7579f77SDag-Erling Smørgrav 363b7579f77SDag-Erling Smørgrav struct lruhash_entry* 3643005e0a3SDag-Erling Smørgrav lruhash_lookup(struct lruhash* table, hashvalue_type hash, void* key, int wr) 365b7579f77SDag-Erling Smørgrav { 366b7579f77SDag-Erling Smørgrav struct lruhash_entry* entry; 367b7579f77SDag-Erling Smørgrav struct lruhash_bin* bin; 368b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc)); 369b7579f77SDag-Erling Smørgrav 370b7579f77SDag-Erling Smørgrav lock_quick_lock(&table->lock); 371b7579f77SDag-Erling Smørgrav bin = &table->array[hash & table->size_mask]; 372b7579f77SDag-Erling Smørgrav lock_quick_lock(&bin->lock); 3738f76bb7dSCy Schubert if((entry=bin_find_entry(table, bin, hash, key, NULL))) 374b7579f77SDag-Erling Smørgrav lru_touch(table, entry); 375b7579f77SDag-Erling Smørgrav lock_quick_unlock(&table->lock); 376b7579f77SDag-Erling Smørgrav 377b7579f77SDag-Erling Smørgrav if(entry) { 378b7579f77SDag-Erling Smørgrav if(wr) { lock_rw_wrlock(&entry->lock); } 379b7579f77SDag-Erling Smørgrav else { lock_rw_rdlock(&entry->lock); } 380b7579f77SDag-Erling Smørgrav } 381b7579f77SDag-Erling Smørgrav lock_quick_unlock(&bin->lock); 382b7579f77SDag-Erling Smørgrav return entry; 383b7579f77SDag-Erling Smørgrav } 384b7579f77SDag-Erling Smørgrav 385b7579f77SDag-Erling Smørgrav void 3863005e0a3SDag-Erling Smørgrav lruhash_remove(struct lruhash* table, hashvalue_type hash, void* key) 387b7579f77SDag-Erling Smørgrav { 388b7579f77SDag-Erling Smørgrav struct lruhash_entry* entry; 389b7579f77SDag-Erling Smørgrav struct lruhash_bin* bin; 390b7579f77SDag-Erling Smørgrav void *d; 391b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc)); 392b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc)); 393b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc)); 394b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc)); 395b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc)); 396b7579f77SDag-Erling Smørgrav 397b7579f77SDag-Erling Smørgrav lock_quick_lock(&table->lock); 398b7579f77SDag-Erling Smørgrav bin = &table->array[hash & table->size_mask]; 399b7579f77SDag-Erling Smørgrav lock_quick_lock(&bin->lock); 4008f76bb7dSCy Schubert if((entry=bin_find_entry(table, bin, hash, key, NULL))) { 401b7579f77SDag-Erling Smørgrav bin_overflow_remove(bin, entry); 402b7579f77SDag-Erling Smørgrav lru_remove(table, entry); 403b7579f77SDag-Erling Smørgrav } else { 404b7579f77SDag-Erling Smørgrav lock_quick_unlock(&table->lock); 405b7579f77SDag-Erling Smørgrav lock_quick_unlock(&bin->lock); 406b7579f77SDag-Erling Smørgrav return; 407b7579f77SDag-Erling Smørgrav } 408b7579f77SDag-Erling Smørgrav table->num--; 409b7579f77SDag-Erling Smørgrav table->space_used -= (*table->sizefunc)(entry->key, entry->data); 410b7579f77SDag-Erling Smørgrav lock_rw_wrlock(&entry->lock); 411b7579f77SDag-Erling Smørgrav if(table->markdelfunc) 412b7579f77SDag-Erling Smørgrav (*table->markdelfunc)(entry->key); 413b7579f77SDag-Erling Smørgrav lock_rw_unlock(&entry->lock); 414b7579f77SDag-Erling Smørgrav lock_quick_unlock(&bin->lock); 415f44e67d1SCy Schubert lock_quick_unlock(&table->lock); 416b7579f77SDag-Erling Smørgrav /* finish removal */ 417b7579f77SDag-Erling Smørgrav d = entry->data; 418b7579f77SDag-Erling Smørgrav (*table->delkeyfunc)(entry->key, table->cb_arg); 419b7579f77SDag-Erling Smørgrav (*table->deldatafunc)(d, table->cb_arg); 420b7579f77SDag-Erling Smørgrav } 421b7579f77SDag-Erling Smørgrav 422b7579f77SDag-Erling Smørgrav /** clear bin, respecting locks, does not do space, LRU */ 423b7579f77SDag-Erling Smørgrav static void 424b7579f77SDag-Erling Smørgrav bin_clear(struct lruhash* table, struct lruhash_bin* bin) 425b7579f77SDag-Erling Smørgrav { 426b7579f77SDag-Erling Smørgrav struct lruhash_entry* p, *np; 427b7579f77SDag-Erling Smørgrav void *d; 428b7579f77SDag-Erling Smørgrav lock_quick_lock(&bin->lock); 429b7579f77SDag-Erling Smørgrav p = bin->overflow_list; 430b7579f77SDag-Erling Smørgrav while(p) { 431b7579f77SDag-Erling Smørgrav lock_rw_wrlock(&p->lock); 432b7579f77SDag-Erling Smørgrav np = p->overflow_next; 433b7579f77SDag-Erling Smørgrav d = p->data; 434b7579f77SDag-Erling Smørgrav if(table->markdelfunc) 435b7579f77SDag-Erling Smørgrav (*table->markdelfunc)(p->key); 436b7579f77SDag-Erling Smørgrav lock_rw_unlock(&p->lock); 437b7579f77SDag-Erling Smørgrav (*table->delkeyfunc)(p->key, table->cb_arg); 438b7579f77SDag-Erling Smørgrav (*table->deldatafunc)(d, table->cb_arg); 439b7579f77SDag-Erling Smørgrav p = np; 440b7579f77SDag-Erling Smørgrav } 441b7579f77SDag-Erling Smørgrav bin->overflow_list = NULL; 442b7579f77SDag-Erling Smørgrav lock_quick_unlock(&bin->lock); 443b7579f77SDag-Erling Smørgrav } 444b7579f77SDag-Erling Smørgrav 445b7579f77SDag-Erling Smørgrav void 446b7579f77SDag-Erling Smørgrav lruhash_clear(struct lruhash* table) 447b7579f77SDag-Erling Smørgrav { 448b7579f77SDag-Erling Smørgrav size_t i; 449b7579f77SDag-Erling Smørgrav if(!table) 450b7579f77SDag-Erling Smørgrav return; 451b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc)); 452b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc)); 453b7579f77SDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc)); 454b7579f77SDag-Erling Smørgrav 455b7579f77SDag-Erling Smørgrav lock_quick_lock(&table->lock); 456b7579f77SDag-Erling Smørgrav for(i=0; i<table->size; i++) { 457b7579f77SDag-Erling Smørgrav bin_clear(table, &table->array[i]); 458b7579f77SDag-Erling Smørgrav } 459b7579f77SDag-Erling Smørgrav table->lru_start = NULL; 460b7579f77SDag-Erling Smørgrav table->lru_end = NULL; 461b7579f77SDag-Erling Smørgrav table->num = 0; 462b7579f77SDag-Erling Smørgrav table->space_used = 0; 463b7579f77SDag-Erling Smørgrav lock_quick_unlock(&table->lock); 464b7579f77SDag-Erling Smørgrav } 465b7579f77SDag-Erling Smørgrav 466b7579f77SDag-Erling Smørgrav void 467b7579f77SDag-Erling Smørgrav lruhash_status(struct lruhash* table, const char* id, int extended) 468b7579f77SDag-Erling Smørgrav { 469b7579f77SDag-Erling Smørgrav lock_quick_lock(&table->lock); 470b7579f77SDag-Erling Smørgrav log_info("%s: %u entries, memory %u / %u", 471b7579f77SDag-Erling Smørgrav id, (unsigned)table->num, (unsigned)table->space_used, 472b7579f77SDag-Erling Smørgrav (unsigned)table->space_max); 473b7579f77SDag-Erling Smørgrav log_info(" itemsize %u, array %u, mask %d", 474b7579f77SDag-Erling Smørgrav (unsigned)(table->num? table->space_used/table->num : 0), 475b7579f77SDag-Erling Smørgrav (unsigned)table->size, table->size_mask); 476b7579f77SDag-Erling Smørgrav if(extended) { 477b7579f77SDag-Erling Smørgrav size_t i; 478b7579f77SDag-Erling Smørgrav int min=(int)table->size*2, max=-2; 479b7579f77SDag-Erling Smørgrav for(i=0; i<table->size; i++) { 480b7579f77SDag-Erling Smørgrav int here = 0; 481b7579f77SDag-Erling Smørgrav struct lruhash_entry *en; 482b7579f77SDag-Erling Smørgrav lock_quick_lock(&table->array[i].lock); 483b7579f77SDag-Erling Smørgrav en = table->array[i].overflow_list; 484b7579f77SDag-Erling Smørgrav while(en) { 485b7579f77SDag-Erling Smørgrav here ++; 486b7579f77SDag-Erling Smørgrav en = en->overflow_next; 487b7579f77SDag-Erling Smørgrav } 488b7579f77SDag-Erling Smørgrav lock_quick_unlock(&table->array[i].lock); 489b7579f77SDag-Erling Smørgrav if(extended >= 2) 490b7579f77SDag-Erling Smørgrav log_info("bin[%d] %d", (int)i, here); 491b7579f77SDag-Erling Smørgrav if(here > max) max = here; 492b7579f77SDag-Erling Smørgrav if(here < min) min = here; 493b7579f77SDag-Erling Smørgrav } 494b7579f77SDag-Erling Smørgrav log_info(" bin min %d, avg %.2lf, max %d", min, 495b7579f77SDag-Erling Smørgrav (double)table->num/(double)table->size, max); 496b7579f77SDag-Erling Smørgrav } 497b7579f77SDag-Erling Smørgrav lock_quick_unlock(&table->lock); 498b7579f77SDag-Erling Smørgrav } 499b7579f77SDag-Erling Smørgrav 500b7579f77SDag-Erling Smørgrav size_t 501b7579f77SDag-Erling Smørgrav lruhash_get_mem(struct lruhash* table) 502b7579f77SDag-Erling Smørgrav { 503b7579f77SDag-Erling Smørgrav size_t s; 504b7579f77SDag-Erling Smørgrav lock_quick_lock(&table->lock); 505b7579f77SDag-Erling Smørgrav s = sizeof(struct lruhash) + table->space_used; 506b7579f77SDag-Erling Smørgrav #ifdef USE_THREAD_DEBUG 507b7579f77SDag-Erling Smørgrav if(table->size != 0) { 508b7579f77SDag-Erling Smørgrav size_t i; 509b7579f77SDag-Erling Smørgrav for(i=0; i<table->size; i++) 510b7579f77SDag-Erling Smørgrav s += sizeof(struct lruhash_bin) + 511b7579f77SDag-Erling Smørgrav lock_get_mem(&table->array[i].lock); 512b7579f77SDag-Erling Smørgrav } 513b7579f77SDag-Erling Smørgrav #else /* no THREAD_DEBUG */ 514b7579f77SDag-Erling Smørgrav if(table->size != 0) 515b7579f77SDag-Erling Smørgrav s += (table->size)*(sizeof(struct lruhash_bin) + 516b7579f77SDag-Erling Smørgrav lock_get_mem(&table->array[0].lock)); 517b7579f77SDag-Erling Smørgrav #endif 518b7579f77SDag-Erling Smørgrav lock_quick_unlock(&table->lock); 519b7579f77SDag-Erling Smørgrav s += lock_get_mem(&table->lock); 520b7579f77SDag-Erling Smørgrav return s; 521b7579f77SDag-Erling Smørgrav } 522b7579f77SDag-Erling Smørgrav 523b7579f77SDag-Erling Smørgrav void 5243005e0a3SDag-Erling Smørgrav lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_type md) 525b7579f77SDag-Erling Smørgrav { 526b7579f77SDag-Erling Smørgrav lock_quick_lock(&table->lock); 527b7579f77SDag-Erling Smørgrav table->markdelfunc = md; 528b7579f77SDag-Erling Smørgrav lock_quick_unlock(&table->lock); 529b7579f77SDag-Erling Smørgrav } 530b7579f77SDag-Erling Smørgrav 531b7579f77SDag-Erling Smørgrav void 532335c7cdaSCy Schubert lruhash_update_space_used(struct lruhash* table, void* cb_arg, int diff_size) 533335c7cdaSCy Schubert { 534335c7cdaSCy Schubert struct lruhash_entry *reclaimlist = NULL; 535335c7cdaSCy Schubert 536335c7cdaSCy Schubert fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc)); 537335c7cdaSCy Schubert fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc)); 538335c7cdaSCy Schubert fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc)); 539335c7cdaSCy Schubert fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc)); 540335c7cdaSCy Schubert 541335c7cdaSCy Schubert if(cb_arg == NULL) cb_arg = table->cb_arg; 542335c7cdaSCy Schubert 543335c7cdaSCy Schubert /* update space used */ 544335c7cdaSCy Schubert lock_quick_lock(&table->lock); 545335c7cdaSCy Schubert 546335c7cdaSCy Schubert if((int)table->space_used + diff_size < 0) 547335c7cdaSCy Schubert table->space_used = 0; 548335c7cdaSCy Schubert else table->space_used = (size_t)((int)table->space_used + diff_size); 549335c7cdaSCy Schubert 550335c7cdaSCy Schubert if(table->space_used > table->space_max) 551335c7cdaSCy Schubert reclaim_space(table, &reclaimlist); 552335c7cdaSCy Schubert 553335c7cdaSCy Schubert lock_quick_unlock(&table->lock); 554335c7cdaSCy Schubert 555335c7cdaSCy Schubert /* finish reclaim if any (outside of critical region) */ 556335c7cdaSCy Schubert while(reclaimlist) { 557335c7cdaSCy Schubert struct lruhash_entry* n = reclaimlist->overflow_next; 558335c7cdaSCy Schubert void* d = reclaimlist->data; 559335c7cdaSCy Schubert (*table->delkeyfunc)(reclaimlist->key, cb_arg); 560335c7cdaSCy Schubert (*table->deldatafunc)(d, cb_arg); 561335c7cdaSCy Schubert reclaimlist = n; 562335c7cdaSCy Schubert } 563335c7cdaSCy Schubert } 564335c7cdaSCy Schubert 565*be771a7bSCy Schubert void lruhash_update_space_max(struct lruhash* table, void* cb_arg, size_t max) 566*be771a7bSCy Schubert { 567*be771a7bSCy Schubert struct lruhash_entry *reclaimlist = NULL; 568*be771a7bSCy Schubert 569*be771a7bSCy Schubert fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc)); 570*be771a7bSCy Schubert fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc)); 571*be771a7bSCy Schubert fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc)); 572*be771a7bSCy Schubert fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc)); 573*be771a7bSCy Schubert 574*be771a7bSCy Schubert if(cb_arg == NULL) cb_arg = table->cb_arg; 575*be771a7bSCy Schubert 576*be771a7bSCy Schubert /* update space max */ 577*be771a7bSCy Schubert lock_quick_lock(&table->lock); 578*be771a7bSCy Schubert table->space_max = max; 579*be771a7bSCy Schubert 580*be771a7bSCy Schubert if(table->space_used > table->space_max) 581*be771a7bSCy Schubert reclaim_space(table, &reclaimlist); 582*be771a7bSCy Schubert 583*be771a7bSCy Schubert lock_quick_unlock(&table->lock); 584*be771a7bSCy Schubert 585*be771a7bSCy Schubert /* finish reclaim if any (outside of critical region) */ 586*be771a7bSCy Schubert while(reclaimlist) { 587*be771a7bSCy Schubert struct lruhash_entry* n = reclaimlist->overflow_next; 588*be771a7bSCy Schubert void* d = reclaimlist->data; 589*be771a7bSCy Schubert (*table->delkeyfunc)(reclaimlist->key, cb_arg); 590*be771a7bSCy Schubert (*table->deldatafunc)(d, cb_arg); 591*be771a7bSCy Schubert reclaimlist = n; 592*be771a7bSCy Schubert } 593*be771a7bSCy Schubert } 594*be771a7bSCy Schubert 595335c7cdaSCy Schubert void 596b7579f77SDag-Erling Smørgrav lruhash_traverse(struct lruhash* h, int wr, 597b7579f77SDag-Erling Smørgrav void (*func)(struct lruhash_entry*, void*), void* arg) 598b7579f77SDag-Erling Smørgrav { 599b7579f77SDag-Erling Smørgrav size_t i; 600b7579f77SDag-Erling Smørgrav struct lruhash_entry* e; 601b7579f77SDag-Erling Smørgrav 602b7579f77SDag-Erling Smørgrav lock_quick_lock(&h->lock); 603b7579f77SDag-Erling Smørgrav for(i=0; i<h->size; i++) { 604b7579f77SDag-Erling Smørgrav lock_quick_lock(&h->array[i].lock); 605b7579f77SDag-Erling Smørgrav for(e = h->array[i].overflow_list; e; e = e->overflow_next) { 606b7579f77SDag-Erling Smørgrav if(wr) { 607b7579f77SDag-Erling Smørgrav lock_rw_wrlock(&e->lock); 608b7579f77SDag-Erling Smørgrav } else { 609b7579f77SDag-Erling Smørgrav lock_rw_rdlock(&e->lock); 610b7579f77SDag-Erling Smørgrav } 611b7579f77SDag-Erling Smørgrav (*func)(e, arg); 612b7579f77SDag-Erling Smørgrav lock_rw_unlock(&e->lock); 613b7579f77SDag-Erling Smørgrav } 614b7579f77SDag-Erling Smørgrav lock_quick_unlock(&h->array[i].lock); 615b7579f77SDag-Erling Smørgrav } 616b7579f77SDag-Erling Smørgrav lock_quick_unlock(&h->lock); 617b7579f77SDag-Erling Smørgrav } 61865b390aaSDag-Erling Smørgrav 61965b390aaSDag-Erling Smørgrav /* 62065b390aaSDag-Erling Smørgrav * Demote: the opposite of touch, move an entry to the bottom 62165b390aaSDag-Erling Smørgrav * of the LRU pile. 62265b390aaSDag-Erling Smørgrav */ 62365b390aaSDag-Erling Smørgrav 62465b390aaSDag-Erling Smørgrav void 62565b390aaSDag-Erling Smørgrav lru_demote(struct lruhash* table, struct lruhash_entry* entry) 62665b390aaSDag-Erling Smørgrav { 62765b390aaSDag-Erling Smørgrav log_assert(table && entry); 62865b390aaSDag-Erling Smørgrav if (entry == table->lru_end) 62965b390aaSDag-Erling Smørgrav return; /* nothing to do */ 63065b390aaSDag-Erling Smørgrav /* remove from current lru position */ 63165b390aaSDag-Erling Smørgrav lru_remove(table, entry); 63265b390aaSDag-Erling Smørgrav /* add at end */ 63365b390aaSDag-Erling Smørgrav entry->lru_next = NULL; 63465b390aaSDag-Erling Smørgrav entry->lru_prev = table->lru_end; 63565b390aaSDag-Erling Smørgrav 63665b390aaSDag-Erling Smørgrav if (table->lru_end == NULL) 63765b390aaSDag-Erling Smørgrav { 63865b390aaSDag-Erling Smørgrav table->lru_start = entry; 63965b390aaSDag-Erling Smørgrav } 64065b390aaSDag-Erling Smørgrav else 64165b390aaSDag-Erling Smørgrav { 64265b390aaSDag-Erling Smørgrav table->lru_end->lru_next = entry; 64365b390aaSDag-Erling Smørgrav } 64465b390aaSDag-Erling Smørgrav table->lru_end = entry; 64565b390aaSDag-Erling Smørgrav } 64665b390aaSDag-Erling Smørgrav 64765b390aaSDag-Erling Smørgrav struct lruhash_entry* 64865b390aaSDag-Erling Smørgrav lruhash_insert_or_retrieve(struct lruhash* table, hashvalue_type hash, 64965b390aaSDag-Erling Smørgrav struct lruhash_entry* entry, void* data, void* cb_arg) 65065b390aaSDag-Erling Smørgrav { 65165b390aaSDag-Erling Smørgrav struct lruhash_bin* bin; 65265b390aaSDag-Erling Smørgrav struct lruhash_entry* found, *reclaimlist = NULL; 65365b390aaSDag-Erling Smørgrav size_t need_size; 6548f76bb7dSCy Schubert size_t collisions; 65565b390aaSDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc)); 65665b390aaSDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc)); 65765b390aaSDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc)); 65865b390aaSDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc)); 65965b390aaSDag-Erling Smørgrav fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc)); 66065b390aaSDag-Erling Smørgrav need_size = table->sizefunc(entry->key, data); 66165b390aaSDag-Erling Smørgrav if (cb_arg == NULL) cb_arg = table->cb_arg; 66265b390aaSDag-Erling Smørgrav 66365b390aaSDag-Erling Smørgrav /* find bin */ 66465b390aaSDag-Erling Smørgrav lock_quick_lock(&table->lock); 66565b390aaSDag-Erling Smørgrav bin = &table->array[hash & table->size_mask]; 66665b390aaSDag-Erling Smørgrav lock_quick_lock(&bin->lock); 66765b390aaSDag-Erling Smørgrav 66865b390aaSDag-Erling Smørgrav /* see if entry exists already */ 6698f76bb7dSCy Schubert if ((found = bin_find_entry(table, bin, hash, entry->key, &collisions)) != NULL) { 67065b390aaSDag-Erling Smørgrav /* if so: keep the existing data - acquire a writelock */ 67165b390aaSDag-Erling Smørgrav lock_rw_wrlock(&found->lock); 67265b390aaSDag-Erling Smørgrav } 67365b390aaSDag-Erling Smørgrav else 67465b390aaSDag-Erling Smørgrav { 67565b390aaSDag-Erling Smørgrav /* if not: add to bin */ 67665b390aaSDag-Erling Smørgrav entry->overflow_next = bin->overflow_list; 67765b390aaSDag-Erling Smørgrav bin->overflow_list = entry; 67865b390aaSDag-Erling Smørgrav lru_front(table, entry); 67965b390aaSDag-Erling Smørgrav table->num++; 6808f76bb7dSCy Schubert if (table->max_collisions < collisions) 6818f76bb7dSCy Schubert table->max_collisions = collisions; 68265b390aaSDag-Erling Smørgrav table->space_used += need_size; 68365b390aaSDag-Erling Smørgrav /* return the entry that was presented, and lock it */ 68465b390aaSDag-Erling Smørgrav found = entry; 68565b390aaSDag-Erling Smørgrav lock_rw_wrlock(&found->lock); 68665b390aaSDag-Erling Smørgrav } 68765b390aaSDag-Erling Smørgrav lock_quick_unlock(&bin->lock); 68865b390aaSDag-Erling Smørgrav if (table->space_used > table->space_max) 68965b390aaSDag-Erling Smørgrav reclaim_space(table, &reclaimlist); 69065b390aaSDag-Erling Smørgrav if (table->num >= table->size) 69165b390aaSDag-Erling Smørgrav table_grow(table); 69265b390aaSDag-Erling Smørgrav lock_quick_unlock(&table->lock); 69365b390aaSDag-Erling Smørgrav 69465b390aaSDag-Erling Smørgrav /* finish reclaim if any (outside of critical region) */ 69565b390aaSDag-Erling Smørgrav while (reclaimlist) { 69665b390aaSDag-Erling Smørgrav struct lruhash_entry* n = reclaimlist->overflow_next; 69765b390aaSDag-Erling Smørgrav void* d = reclaimlist->data; 69865b390aaSDag-Erling Smørgrav (*table->delkeyfunc)(reclaimlist->key, cb_arg); 69965b390aaSDag-Erling Smørgrav (*table->deldatafunc)(d, cb_arg); 70065b390aaSDag-Erling Smørgrav reclaimlist = n; 70165b390aaSDag-Erling Smørgrav } 70265b390aaSDag-Erling Smørgrav 70365b390aaSDag-Erling Smørgrav /* return the entry that was selected */ 70465b390aaSDag-Erling Smørgrav return found; 70565b390aaSDag-Erling Smørgrav } 70665b390aaSDag-Erling Smørgrav 707