hash.c revision c1f859d4
1/** 2 * \file hash.c 3 * Generic hash table. 4 * 5 * Used for display lists, texture objects, vertex/fragment programs, 6 * buffer objects, etc. The hash functions are thread-safe. 7 * 8 * \note key=0 is illegal. 9 * 10 * \author Brian Paul 11 */ 12 13/* 14 * Mesa 3-D graphics library 15 * Version: 6.5.1 16 * 17 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved. 18 * 19 * Permission is hereby granted, free of charge, to any person obtaining a 20 * copy of this software and associated documentation files (the "Software"), 21 * to deal in the Software without restriction, including without limitation 22 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 23 * and/or sell copies of the Software, and to permit persons to whom the 24 * Software is furnished to do so, subject to the following conditions: 25 * 26 * The above copyright notice and this permission notice shall be included 27 * in all copies or substantial portions of the Software. 28 * 29 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 30 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 32 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 33 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 34 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 35 */ 36 37 38#include "glheader.h" 39#include "imports.h" 40#include "glapi/glthread.h" 41#include "hash.h" 42 43 44#define TABLE_SIZE 1023 /**< Size of lookup table/array */ 45 46#define HASH_FUNC(K) ((K) % TABLE_SIZE) 47 48 49/** 50 * An entry in the hash table. 51 */ 52struct HashEntry { 53 GLuint Key; /**< the entry's key */ 54 void *Data; /**< the entry's data */ 55 struct HashEntry *Next; /**< pointer to next entry */ 56}; 57 58 59/** 60 * The hash table data structure. 61 */ 62struct _mesa_HashTable { 63 struct HashEntry *Table[TABLE_SIZE]; /**< the lookup table */ 64 GLuint MaxKey; /**< highest key inserted so far */ 65 _glthread_Mutex Mutex; /**< mutual exclusion lock */ 66 _glthread_Mutex WalkMutex; /**< for _mesa_HashWalk() */ 67 GLboolean InDeleteAll; /**< Debug check */ 68}; 69 70 71 72/** 73 * Create a new hash table. 74 * 75 * \return pointer to a new, empty hash table. 76 */ 77struct _mesa_HashTable * 78_mesa_NewHashTable(void) 79{ 80 struct _mesa_HashTable *table = CALLOC_STRUCT(_mesa_HashTable); 81 if (table) { 82 _glthread_INIT_MUTEX(table->Mutex); 83 _glthread_INIT_MUTEX(table->WalkMutex); 84 } 85 return table; 86} 87 88 89 90/** 91 * Delete a hash table. 92 * Frees each entry on the hash table and then the hash table structure itself. 93 * Note that the caller should have already traversed the table and deleted 94 * the objects in the table (i.e. We don't free the entries' data pointer). 95 * 96 * \param table the hash table to delete. 97 */ 98void 99_mesa_DeleteHashTable(struct _mesa_HashTable *table) 100{ 101 GLuint pos; 102 assert(table); 103 for (pos = 0; pos < TABLE_SIZE; pos++) { 104 struct HashEntry *entry = table->Table[pos]; 105 while (entry) { 106 struct HashEntry *next = entry->Next; 107 if (entry->Data) { 108 _mesa_problem(NULL, 109 "In _mesa_DeleteHashTable, found non-freed data"); 110 } 111 _mesa_free(entry); 112 entry = next; 113 } 114 } 115 _glthread_DESTROY_MUTEX(table->Mutex); 116 _glthread_DESTROY_MUTEX(table->WalkMutex); 117 _mesa_free(table); 118} 119 120 121 122/** 123 * Lookup an entry in the hash table. 124 * 125 * \param table the hash table. 126 * \param key the key. 127 * 128 * \return pointer to user's data or NULL if key not in table 129 */ 130void * 131_mesa_HashLookup(const struct _mesa_HashTable *table, GLuint key) 132{ 133 GLuint pos; 134 const struct HashEntry *entry; 135 136 assert(table); 137 assert(key); 138 139 pos = HASH_FUNC(key); 140 entry = table->Table[pos]; 141 while (entry) { 142 if (entry->Key == key) { 143 return entry->Data; 144 } 145 entry = entry->Next; 146 } 147 return NULL; 148} 149 150 151 152/** 153 * Insert a key/pointer pair into the hash table. 154 * If an entry with this key already exists we'll replace the existing entry. 155 * 156 * \param table the hash table. 157 * \param key the key (not zero). 158 * \param data pointer to user data. 159 */ 160void 161_mesa_HashInsert(struct _mesa_HashTable *table, GLuint key, void *data) 162{ 163 /* search for existing entry with this key */ 164 GLuint pos; 165 struct HashEntry *entry; 166 167 assert(table); 168 assert(key); 169 170 _glthread_LOCK_MUTEX(table->Mutex); 171 172 if (key > table->MaxKey) 173 table->MaxKey = key; 174 175 pos = HASH_FUNC(key); 176 177 /* check if replacing an existing entry with same key */ 178 for (entry = table->Table[pos]; entry; entry = entry->Next) { 179 if (entry->Key == key) { 180 /* replace entry's data */ 181#if 0 /* not sure this check is always valid */ 182 if (entry->Data) { 183 _mesa_problem(NULL, "Memory leak detected in _mesa_HashInsert"); 184 } 185#endif 186 entry->Data = data; 187 _glthread_UNLOCK_MUTEX(table->Mutex); 188 return; 189 } 190 } 191 192 /* alloc and insert new table entry */ 193 entry = MALLOC_STRUCT(HashEntry); 194 entry->Key = key; 195 entry->Data = data; 196 entry->Next = table->Table[pos]; 197 table->Table[pos] = entry; 198 199 _glthread_UNLOCK_MUTEX(table->Mutex); 200} 201 202 203 204/** 205 * Remove an entry from the hash table. 206 * 207 * \param table the hash table. 208 * \param key key of entry to remove. 209 * 210 * While holding the hash table's lock, searches the entry with the matching 211 * key and unlinks it. 212 */ 213void 214_mesa_HashRemove(struct _mesa_HashTable *table, GLuint key) 215{ 216 GLuint pos; 217 struct HashEntry *entry, *prev; 218 219 assert(table); 220 assert(key); 221 222 /* have to check this outside of mutex lock */ 223 if (table->InDeleteAll) { 224 _mesa_problem(NULL, "_mesa_HashRemove illegally called from " 225 "_mesa_HashDeleteAll callback function"); 226 return; 227 } 228 229 _glthread_LOCK_MUTEX(table->Mutex); 230 231 pos = HASH_FUNC(key); 232 prev = NULL; 233 entry = table->Table[pos]; 234 while (entry) { 235 if (entry->Key == key) { 236 /* found it! */ 237 if (prev) { 238 prev->Next = entry->Next; 239 } 240 else { 241 table->Table[pos] = entry->Next; 242 } 243 _mesa_free(entry); 244 _glthread_UNLOCK_MUTEX(table->Mutex); 245 return; 246 } 247 prev = entry; 248 entry = entry->Next; 249 } 250 251 _glthread_UNLOCK_MUTEX(table->Mutex); 252} 253 254 255 256/** 257 * Delete all entries in a hash table, but don't delete the table itself. 258 * Invoke the given callback function for each table entry. 259 * 260 * \param table the hash table to delete 261 * \param callback the callback function 262 * \param userData arbitrary pointer to pass along to the callback 263 * (this is typically a GLcontext pointer) 264 */ 265void 266_mesa_HashDeleteAll(struct _mesa_HashTable *table, 267 void (*callback)(GLuint key, void *data, void *userData), 268 void *userData) 269{ 270 GLuint pos; 271 ASSERT(table); 272 ASSERT(callback); 273 _glthread_LOCK_MUTEX(table->Mutex); 274 table->InDeleteAll = GL_TRUE; 275 for (pos = 0; pos < TABLE_SIZE; pos++) { 276 struct HashEntry *entry, *next; 277 for (entry = table->Table[pos]; entry; entry = next) { 278 callback(entry->Key, entry->Data, userData); 279 next = entry->Next; 280 _mesa_free(entry); 281 } 282 table->Table[pos] = NULL; 283 } 284 table->InDeleteAll = GL_FALSE; 285 _glthread_UNLOCK_MUTEX(table->Mutex); 286} 287 288 289/** 290 * Walk over all entries in a hash table, calling callback function for each. 291 * Note: we use a separate mutex in this function to avoid a recursive 292 * locking deadlock (in case the callback calls _mesa_HashRemove()) and to 293 * prevent multiple threads/contexts from getting tangled up. 294 * A lock-less version of this function could be used when the table will 295 * not be modified. 296 * \param table the hash table to walk 297 * \param callback the callback function 298 * \param userData arbitrary pointer to pass along to the callback 299 * (this is typically a GLcontext pointer) 300 */ 301void 302_mesa_HashWalk(const struct _mesa_HashTable *table, 303 void (*callback)(GLuint key, void *data, void *userData), 304 void *userData) 305{ 306 /* cast-away const */ 307 struct _mesa_HashTable *table2 = (struct _mesa_HashTable *) table; 308 GLuint pos; 309 ASSERT(table); 310 ASSERT(callback); 311 _glthread_LOCK_MUTEX(table2->WalkMutex); 312 for (pos = 0; pos < TABLE_SIZE; pos++) { 313 struct HashEntry *entry, *next; 314 for (entry = table->Table[pos]; entry; entry = next) { 315 /* save 'next' pointer now in case the callback deletes the entry */ 316 next = entry->Next; 317 callback(entry->Key, entry->Data, userData); 318 } 319 } 320 _glthread_UNLOCK_MUTEX(table2->WalkMutex); 321} 322 323 324/** 325 * Return the key of the "first" entry in the hash table. 326 * While holding the lock, walks through all table positions until finding 327 * the first entry of the first non-empty one. 328 * 329 * \param table the hash table 330 * \return key for the "first" entry in the hash table. 331 */ 332GLuint 333_mesa_HashFirstEntry(struct _mesa_HashTable *table) 334{ 335 GLuint pos; 336 assert(table); 337 _glthread_LOCK_MUTEX(table->Mutex); 338 for (pos = 0; pos < TABLE_SIZE; pos++) { 339 if (table->Table[pos]) { 340 _glthread_UNLOCK_MUTEX(table->Mutex); 341 return table->Table[pos]->Key; 342 } 343 } 344 _glthread_UNLOCK_MUTEX(table->Mutex); 345 return 0; 346} 347 348 349/** 350 * Given a hash table key, return the next key. This is used to walk 351 * over all entries in the table. Note that the keys returned during 352 * walking won't be in any particular order. 353 * \return next hash key or 0 if end of table. 354 */ 355GLuint 356_mesa_HashNextEntry(const struct _mesa_HashTable *table, GLuint key) 357{ 358 const struct HashEntry *entry; 359 GLuint pos; 360 361 assert(table); 362 assert(key); 363 364 /* Find the entry with given key */ 365 pos = HASH_FUNC(key); 366 for (entry = table->Table[pos]; entry ; entry = entry->Next) { 367 if (entry->Key == key) { 368 break; 369 } 370 } 371 372 if (!entry) { 373 /* the given key was not found, so we can't find the next entry */ 374 return 0; 375 } 376 377 if (entry->Next) { 378 /* return next in linked list */ 379 return entry->Next->Key; 380 } 381 else { 382 /* look for next non-empty table slot */ 383 pos++; 384 while (pos < TABLE_SIZE) { 385 if (table->Table[pos]) { 386 return table->Table[pos]->Key; 387 } 388 pos++; 389 } 390 return 0; 391 } 392} 393 394 395/** 396 * Dump contents of hash table for debugging. 397 * 398 * \param table the hash table. 399 */ 400void 401_mesa_HashPrint(const struct _mesa_HashTable *table) 402{ 403 GLuint pos; 404 assert(table); 405 for (pos = 0; pos < TABLE_SIZE; pos++) { 406 const struct HashEntry *entry = table->Table[pos]; 407 while (entry) { 408 _mesa_debug(NULL, "%u %p\n", entry->Key, entry->Data); 409 entry = entry->Next; 410 } 411 } 412} 413 414 415 416/** 417 * Find a block of adjacent unused hash keys. 418 * 419 * \param table the hash table. 420 * \param numKeys number of keys needed. 421 * 422 * \return Starting key of free block or 0 if failure. 423 * 424 * If there are enough free keys between the maximum key existing in the table 425 * (_mesa_HashTable::MaxKey) and the maximum key possible, then simply return 426 * the adjacent key. Otherwise do a full search for a free key block in the 427 * allowable key range. 428 */ 429GLuint 430_mesa_HashFindFreeKeyBlock(struct _mesa_HashTable *table, GLuint numKeys) 431{ 432 const GLuint maxKey = ~((GLuint) 0); 433 _glthread_LOCK_MUTEX(table->Mutex); 434 if (maxKey - numKeys > table->MaxKey) { 435 /* the quick solution */ 436 _glthread_UNLOCK_MUTEX(table->Mutex); 437 return table->MaxKey + 1; 438 } 439 else { 440 /* the slow solution */ 441 GLuint freeCount = 0; 442 GLuint freeStart = 1; 443 GLuint key; 444 for (key = 1; key != maxKey; key++) { 445 if (_mesa_HashLookup(table, key)) { 446 /* darn, this key is already in use */ 447 freeCount = 0; 448 freeStart = key+1; 449 } 450 else { 451 /* this key not in use, check if we've found enough */ 452 freeCount++; 453 if (freeCount == numKeys) { 454 _glthread_UNLOCK_MUTEX(table->Mutex); 455 return freeStart; 456 } 457 } 458 } 459 /* cannot allocate a block of numKeys consecutive keys */ 460 _glthread_UNLOCK_MUTEX(table->Mutex); 461 return 0; 462 } 463} 464 465 466#if 0 /* debug only */ 467 468/** 469 * Test walking over all the entries in a hash table. 470 */ 471static void 472test_hash_walking(void) 473{ 474 struct _mesa_HashTable *t = _mesa_NewHashTable(); 475 const GLuint limit = 50000; 476 GLuint i; 477 478 /* create some entries */ 479 for (i = 0; i < limit; i++) { 480 GLuint dummy; 481 GLuint k = (rand() % (limit * 10)) + 1; 482 while (_mesa_HashLookup(t, k)) { 483 /* id already in use, try another */ 484 k = (rand() % (limit * 10)) + 1; 485 } 486 _mesa_HashInsert(t, k, &dummy); 487 } 488 489 /* walk over all entries */ 490 { 491 GLuint k = _mesa_HashFirstEntry(t); 492 GLuint count = 0; 493 while (k) { 494 GLuint knext = _mesa_HashNextEntry(t, k); 495 assert(knext != k); 496 _mesa_HashRemove(t, k); 497 count++; 498 k = knext; 499 } 500 assert(count == limit); 501 k = _mesa_HashFirstEntry(t); 502 assert(k==0); 503 } 504 505 _mesa_DeleteHashTable(t); 506} 507 508 509void 510_mesa_test_hash_functions(void) 511{ 512 int a, b, c; 513 struct _mesa_HashTable *t; 514 515 t = _mesa_NewHashTable(); 516 _mesa_HashInsert(t, 501, &a); 517 _mesa_HashInsert(t, 10, &c); 518 _mesa_HashInsert(t, 0xfffffff8, &b); 519 /*_mesa_HashPrint(t);*/ 520 521 assert(_mesa_HashLookup(t,501)); 522 assert(!_mesa_HashLookup(t,1313)); 523 assert(_mesa_HashFindFreeKeyBlock(t, 100)); 524 525 _mesa_DeleteHashTable(t); 526 527 test_hash_walking(); 528} 529 530#endif 531