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 */ 52 struct 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 */ 62 struct _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 */ 77 struct _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 */ 98 void 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 free(entry); 112 entry = next; 113 } 114 } 115 _glthread_DESTROY_MUTEX(table->Mutex); 116 _glthread_DESTROY_MUTEX(table->WalkMutex); 117 free(table); 118 } 119 120 121 122 /** 123 * Lookup an entry in the hash table, without locking. 124 * \sa _mesa_HashLookup 125 */ 126 static inline void * 127 _mesa_HashLookup_unlocked(struct _mesa_HashTable *table, GLuint key) 128 { 129 GLuint pos; 130 const struct HashEntry *entry; 131 132 assert(table); 133 assert(key); 134 135 pos = HASH_FUNC(key); 136 entry = table->Table[pos]; 137 while (entry) { 138 if (entry->Key == key) { 139 return entry->Data; 140 } 141 entry = entry->Next; 142 } 143 return NULL; 144 } 145 146 147 /** 148 * Lookup an entry in the hash table. 149 * 150 * \param table the hash table. 151 * \param key the key. 152 * 153 * \return pointer to user's data or NULL if key not in table 154 */ 155 void * 156 _mesa_HashLookup(struct _mesa_HashTable *table, GLuint key) 157 { 158 void *res; 159 assert(table); 160 _glthread_LOCK_MUTEX(table->Mutex); 161 res = _mesa_HashLookup_unlocked(table, key); 162 _glthread_UNLOCK_MUTEX(table->Mutex); 163 return res; 164 } 165 166 167 /** 168 * Insert a key/pointer pair into the hash table. 169 * If an entry with this key already exists we'll replace the existing entry. 170 * 171 * \param table the hash table. 172 * \param key the key (not zero). 173 * \param data pointer to user data. 174 */ 175 void 176 _mesa_HashInsert(struct _mesa_HashTable *table, GLuint key, void *data) 177 { 178 /* search for existing entry with this key */ 179 GLuint pos; 180 struct HashEntry *entry; 181 182 assert(table); 183 assert(key); 184 185 _glthread_LOCK_MUTEX(table->Mutex); 186 187 if (key > table->MaxKey) 188 table->MaxKey = key; 189 190 pos = HASH_FUNC(key); 191 192 /* check if replacing an existing entry with same key */ 193 for (entry = table->Table[pos]; entry; entry = entry->Next) { 194 if (entry->Key == key) { 195 /* replace entry's data */ 196 #if 0 /* not sure this check is always valid */ 197 if (entry->Data) { 198 _mesa_problem(NULL, "Memory leak detected in _mesa_HashInsert"); 199 } 200 #endif 201 entry->Data = data; 202 _glthread_UNLOCK_MUTEX(table->Mutex); 203 return; 204 } 205 } 206 207 /* alloc and insert new table entry */ 208 entry = MALLOC_STRUCT(HashEntry); 209 if (entry) { 210 entry->Key = key; 211 entry->Data = data; 212 entry->Next = table->Table[pos]; 213 table->Table[pos] = entry; 214 } 215 216 _glthread_UNLOCK_MUTEX(table->Mutex); 217 } 218 219 220 221 /** 222 * Remove an entry from the hash table. 223 * 224 * \param table the hash table. 225 * \param key key of entry to remove. 226 * 227 * While holding the hash table's lock, searches the entry with the matching 228 * key and unlinks it. 229 */ 230 void 231 _mesa_HashRemove(struct _mesa_HashTable *table, GLuint key) 232 { 233 GLuint pos; 234 struct HashEntry *entry, *prev; 235 236 assert(table); 237 assert(key); 238 239 /* have to check this outside of mutex lock */ 240 if (table->InDeleteAll) { 241 _mesa_problem(NULL, "_mesa_HashRemove illegally called from " 242 "_mesa_HashDeleteAll callback function"); 243 return; 244 } 245 246 _glthread_LOCK_MUTEX(table->Mutex); 247 248 pos = HASH_FUNC(key); 249 prev = NULL; 250 entry = table->Table[pos]; 251 while (entry) { 252 if (entry->Key == key) { 253 /* found it! */ 254 if (prev) { 255 prev->Next = entry->Next; 256 } 257 else { 258 table->Table[pos] = entry->Next; 259 } 260 free(entry); 261 _glthread_UNLOCK_MUTEX(table->Mutex); 262 return; 263 } 264 prev = entry; 265 entry = entry->Next; 266 } 267 268 _glthread_UNLOCK_MUTEX(table->Mutex); 269 } 270 271 272 273 /** 274 * Delete all entries in a hash table, but don't delete the table itself. 275 * Invoke the given callback function for each table entry. 276 * 277 * \param table the hash table to delete 278 * \param callback the callback function 279 * \param userData arbitrary pointer to pass along to the callback 280 * (this is typically a struct gl_context pointer) 281 */ 282 void 283 _mesa_HashDeleteAll(struct _mesa_HashTable *table, 284 void (*callback)(GLuint key, void *data, void *userData), 285 void *userData) 286 { 287 GLuint pos; 288 ASSERT(table); 289 ASSERT(callback); 290 _glthread_LOCK_MUTEX(table->Mutex); 291 table->InDeleteAll = GL_TRUE; 292 for (pos = 0; pos < TABLE_SIZE; pos++) { 293 struct HashEntry *entry, *next; 294 for (entry = table->Table[pos]; entry; entry = next) { 295 callback(entry->Key, entry->Data, userData); 296 next = entry->Next; 297 free(entry); 298 } 299 table->Table[pos] = NULL; 300 } 301 table->InDeleteAll = GL_FALSE; 302 _glthread_UNLOCK_MUTEX(table->Mutex); 303 } 304 305 306 /** 307 * Walk over all entries in a hash table, calling callback function for each. 308 * Note: we use a separate mutex in this function to avoid a recursive 309 * locking deadlock (in case the callback calls _mesa_HashRemove()) and to 310 * prevent multiple threads/contexts from getting tangled up. 311 * A lock-less version of this function could be used when the table will 312 * not be modified. 313 * \param table the hash table to walk 314 * \param callback the callback function 315 * \param userData arbitrary pointer to pass along to the callback 316 * (this is typically a struct gl_context pointer) 317 */ 318 void 319 _mesa_HashWalk(const struct _mesa_HashTable *table, 320 void (*callback)(GLuint key, void *data, void *userData), 321 void *userData) 322 { 323 /* cast-away const */ 324 struct _mesa_HashTable *table2 = (struct _mesa_HashTable *) table; 325 GLuint pos; 326 ASSERT(table); 327 ASSERT(callback); 328 _glthread_LOCK_MUTEX(table2->WalkMutex); 329 for (pos = 0; pos < TABLE_SIZE; pos++) { 330 struct HashEntry *entry, *next; 331 for (entry = table->Table[pos]; entry; entry = next) { 332 /* save 'next' pointer now in case the callback deletes the entry */ 333 next = entry->Next; 334 callback(entry->Key, entry->Data, userData); 335 } 336 } 337 _glthread_UNLOCK_MUTEX(table2->WalkMutex); 338 } 339 340 341 /** 342 * Return the key of the "first" entry in the hash table. 343 * While holding the lock, walks through all table positions until finding 344 * the first entry of the first non-empty one. 345 * 346 * \param table the hash table 347 * \return key for the "first" entry in the hash table. 348 */ 349 GLuint 350 _mesa_HashFirstEntry(struct _mesa_HashTable *table) 351 { 352 GLuint pos; 353 assert(table); 354 _glthread_LOCK_MUTEX(table->Mutex); 355 for (pos = 0; pos < TABLE_SIZE; pos++) { 356 if (table->Table[pos]) { 357 _glthread_UNLOCK_MUTEX(table->Mutex); 358 return table->Table[pos]->Key; 359 } 360 } 361 _glthread_UNLOCK_MUTEX(table->Mutex); 362 return 0; 363 } 364 365 366 /** 367 * Given a hash table key, return the next key. This is used to walk 368 * over all entries in the table. Note that the keys returned during 369 * walking won't be in any particular order. 370 * \return next hash key or 0 if end of table. 371 */ 372 GLuint 373 _mesa_HashNextEntry(const struct _mesa_HashTable *table, GLuint key) 374 { 375 const struct HashEntry *entry; 376 GLuint pos; 377 378 assert(table); 379 assert(key); 380 381 /* Find the entry with given key */ 382 pos = HASH_FUNC(key); 383 for (entry = table->Table[pos]; entry ; entry = entry->Next) { 384 if (entry->Key == key) { 385 break; 386 } 387 } 388 389 if (!entry) { 390 /* the given key was not found, so we can't find the next entry */ 391 return 0; 392 } 393 394 if (entry->Next) { 395 /* return next in linked list */ 396 return entry->Next->Key; 397 } 398 else { 399 /* look for next non-empty table slot */ 400 pos++; 401 while (pos < TABLE_SIZE) { 402 if (table->Table[pos]) { 403 return table->Table[pos]->Key; 404 } 405 pos++; 406 } 407 return 0; 408 } 409 } 410 411 412 /** 413 * Dump contents of hash table for debugging. 414 * 415 * \param table the hash table. 416 */ 417 void 418 _mesa_HashPrint(const struct _mesa_HashTable *table) 419 { 420 GLuint pos; 421 assert(table); 422 for (pos = 0; pos < TABLE_SIZE; pos++) { 423 const struct HashEntry *entry = table->Table[pos]; 424 while (entry) { 425 _mesa_debug(NULL, "%u %p\n", entry->Key, entry->Data); 426 entry = entry->Next; 427 } 428 } 429 } 430 431 432 433 /** 434 * Find a block of adjacent unused hash keys. 435 * 436 * \param table the hash table. 437 * \param numKeys number of keys needed. 438 * 439 * \return Starting key of free block or 0 if failure. 440 * 441 * If there are enough free keys between the maximum key existing in the table 442 * (_mesa_HashTable::MaxKey) and the maximum key possible, then simply return 443 * the adjacent key. Otherwise do a full search for a free key block in the 444 * allowable key range. 445 */ 446 GLuint 447 _mesa_HashFindFreeKeyBlock(struct _mesa_HashTable *table, GLuint numKeys) 448 { 449 const GLuint maxKey = ~((GLuint) 0); 450 _glthread_LOCK_MUTEX(table->Mutex); 451 if (maxKey - numKeys > table->MaxKey) { 452 /* the quick solution */ 453 _glthread_UNLOCK_MUTEX(table->Mutex); 454 return table->MaxKey + 1; 455 } 456 else { 457 /* the slow solution */ 458 GLuint freeCount = 0; 459 GLuint freeStart = 1; 460 GLuint key; 461 for (key = 1; key != maxKey; key++) { 462 if (_mesa_HashLookup_unlocked(table, key)) { 463 /* darn, this key is already in use */ 464 freeCount = 0; 465 freeStart = key+1; 466 } 467 else { 468 /* this key not in use, check if we've found enough */ 469 freeCount++; 470 if (freeCount == numKeys) { 471 _glthread_UNLOCK_MUTEX(table->Mutex); 472 return freeStart; 473 } 474 } 475 } 476 /* cannot allocate a block of numKeys consecutive keys */ 477 _glthread_UNLOCK_MUTEX(table->Mutex); 478 return 0; 479 } 480 } 481 482 483 /** 484 * Return the number of entries in the hash table. 485 */ 486 GLuint 487 _mesa_HashNumEntries(const struct _mesa_HashTable *table) 488 { 489 GLuint pos, count = 0; 490 491 for (pos = 0; pos < TABLE_SIZE; pos++) { 492 const struct HashEntry *entry; 493 for (entry = table->Table[pos]; entry; entry = entry->Next) { 494 count++; 495 } 496 } 497 498 return count; 499 } 500 501 502 503 #if 0 /* debug only */ 504 505 /** 506 * Test walking over all the entries in a hash table. 507 */ 508 static void 509 test_hash_walking(void) 510 { 511 struct _mesa_HashTable *t = _mesa_NewHashTable(); 512 const GLuint limit = 50000; 513 GLuint i; 514 515 /* create some entries */ 516 for (i = 0; i < limit; i++) { 517 GLuint dummy; 518 GLuint k = (rand() % (limit * 10)) + 1; 519 while (_mesa_HashLookup(t, k)) { 520 /* id already in use, try another */ 521 k = (rand() % (limit * 10)) + 1; 522 } 523 _mesa_HashInsert(t, k, &dummy); 524 } 525 526 /* walk over all entries */ 527 { 528 GLuint k = _mesa_HashFirstEntry(t); 529 GLuint count = 0; 530 while (k) { 531 GLuint knext = _mesa_HashNextEntry(t, k); 532 assert(knext != k); 533 _mesa_HashRemove(t, k); 534 count++; 535 k = knext; 536 } 537 assert(count == limit); 538 k = _mesa_HashFirstEntry(t); 539 assert(k==0); 540 } 541 542 _mesa_DeleteHashTable(t); 543 } 544 545 546 void 547 _mesa_test_hash_functions(void) 548 { 549 int a, b, c; 550 struct _mesa_HashTable *t; 551 552 t = _mesa_NewHashTable(); 553 _mesa_HashInsert(t, 501, &a); 554 _mesa_HashInsert(t, 10, &c); 555 _mesa_HashInsert(t, 0xfffffff8, &b); 556 /*_mesa_HashPrint(t);*/ 557 558 assert(_mesa_HashLookup(t,501)); 559 assert(!_mesa_HashLookup(t,1313)); 560 assert(_mesa_HashFindFreeKeyBlock(t, 100)); 561 562 _mesa_DeleteHashTable(t); 563 564 test_hash_walking(); 565 } 566 567 #endif 568