nfs-ganesha 1.4
|
00001 /* 00002 * vim:expandtab:shiftwidth=8:tabstop=8: 00003 * 00004 * Copyright CEA/DAM/DIF (2008) 00005 * contributeur : Philippe DENIEL philippe.deniel@cea.fr 00006 * Thomas LEIBOVICI thomas.leibovici@cea.fr 00007 * 00008 * 00009 * This program is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Lesser General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 3 of the License, or (at your option) any later version. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 * Lesser General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU Lesser General Public 00020 * License along with this library; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00022 * 02110-1301 USA 00023 * 00024 * --------------------------------------- 00025 */ 00026 00039 #ifdef HAVE_CONFIG_H 00040 #include "config.h" 00041 #endif 00042 00043 #include <stdio.h> 00044 #include <string.h> 00045 #include <pthread.h> 00046 #include "RW_Lock.h" 00047 #include "HashTable.h" 00048 #include "log.h" 00049 #include <assert.h> 00050 00051 #ifndef TRUE 00052 #define TRUE 1 00053 #endif 00054 00055 #ifndef FALSE 00056 #define FALSE 0 00057 #endif 00058 00069 static inline size_t 00070 CACHE_PAGE_SIZE(const hash_table_t *ht) 00071 { 00072 return ((ht->parameter.cache_entry_count) * 00073 sizeof(struct rbt_node*)); 00074 } 00075 00088 static inline 00089 int cache_offsetof(struct hash_table *ht, uint64_t rbthash) 00090 { 00091 return (rbthash % ht->parameter.cache_entry_count); 00092 } 00093 00109 const char * 00110 hash_table_err_to_str(hash_error_t err) 00111 { 00112 switch(err) { 00113 case HASHTABLE_SUCCESS: 00114 return "HASHTABLE_SUCCESS"; 00115 case HASHTABLE_UNKNOWN_HASH_TYPE: 00116 return "HASHTABLE_UNKNOWN_HASH_TYPE"; 00117 case HASHTABLE_INSERT_MALLOC_ERROR: 00118 return "HASHTABLE_INSERT_MALLOC_ERROR"; 00119 case HASHTABLE_ERROR_NO_SUCH_KEY: 00120 return "HASHTABLE_ERROR_NO_SUCH_KEY"; 00121 case HASHTABLE_ERROR_KEY_ALREADY_EXISTS: 00122 return "HASHTABLE_ERROR_KEY_ALREADY_EXISTS"; 00123 case HASHTABLE_ERROR_INVALID_ARGUMENT: 00124 return "HASHTABLE_ERROR_INVALID_ARGUMENT"; 00125 case HASHTABLE_ERROR_DELALL_FAIL: 00126 return "HASHTABLE_ERROR_DELALL_FAIL"; 00127 case HASHTABLE_NOT_DELETED: 00128 return "HASHTABLE_NOT_DELETED"; 00129 case HASHTABLE_OVERWRITTEN: 00130 return "HASHTABLE_OVERWRITTEN"; 00131 } 00132 00133 return "UNKNOWN HASH TABLE ERROR"; 00134 } 00135 00152 static hash_error_t 00153 Key_Locate(struct hash_table *ht, 00154 struct hash_buff *key, 00155 uint32_t index, 00156 uint64_t rbthash, 00157 struct rbt_node **node) 00158 { 00159 /* The current partition */ 00160 struct hash_partition *partition = &(ht->partitions[index]); 00161 00162 /* The root of the red black tree matching this index */ 00163 struct rbt_head *root = NULL; 00164 00165 /* A pair of buffer descriptors locating key and value for this 00166 entry*/ 00167 struct hash_data *data = NULL; 00168 00169 /* The node in the red-black tree currently being traversed */ 00170 struct rbt_node *cursor = NULL; 00171 00172 /* TRUE if we have located the key */ 00173 int found = FALSE; 00174 00175 *node = NULL; 00176 00177 if (partition->cache) { 00178 cursor = partition->cache[cache_offsetof(ht, rbthash)]; 00179 LogFullDebug(COMPONENT_HASHTABLE_CACHE, 00180 "hash %s index %"PRIu32" slot %d\n", 00181 (cursor) ? "hit" : "miss", 00182 index, cache_offsetof(ht, rbthash)); 00183 if (cursor) { 00184 data = RBT_OPAQ(cursor); 00185 if (ht->parameter.compare_key(key, 00186 &(data->buffkey)) == 0) { 00187 goto out; 00188 } 00189 } 00190 } 00191 00192 root = &(ht->partitions[index].rbt); 00193 00194 /* The lefmost occurrence of the value is the one from which we 00195 may start iteration to visit all nodes containing a value. */ 00196 RBT_FIND_LEFT(root, cursor, rbthash); 00197 00198 if (cursor == NULL) { 00199 if(isFullDebug(COMPONENT_HASHTABLE) && 00200 isFullDebug(ht->parameter.ht_log_component)) 00201 LogFullDebug(ht->parameter.ht_log_component, 00202 "Key not found: rbthash = %"PRIu64, 00203 rbthash); 00204 return HASHTABLE_ERROR_NO_SUCH_KEY; 00205 } 00206 00207 while ((cursor != NULL) && (RBT_VALUE(cursor) == rbthash)) { 00208 data = RBT_OPAQ(cursor); 00209 if (ht->parameter.compare_key(key, 00210 &(data->buffkey)) == 0) { 00211 if (partition->cache) { 00212 partition->cache[cache_offsetof(ht, rbthash)] = cursor; 00213 } 00214 found = TRUE; 00215 break; 00216 } 00217 RBT_INCREMENT(cursor); 00218 } 00219 00220 if (!found) { 00221 if(isFullDebug(COMPONENT_HASHTABLE) && 00222 isFullDebug(ht->parameter.ht_log_component)) 00223 LogFullDebug(ht->parameter.ht_log_component, 00224 "Matching hash found, but no matching key."); 00225 return HASHTABLE_ERROR_NO_SUCH_KEY; 00226 } 00227 00228 out: 00229 *node = cursor; 00230 00231 return HASHTABLE_SUCCESS; 00232 } /* Key_Locate */ 00233 00250 static inline hash_error_t 00251 compute(struct hash_table *ht, struct hash_buff *key, 00252 uint32_t *index, uint64_t *rbt_hash) 00253 { 00254 /* Compute the partition index and red-black tree hash */ 00255 if (ht->parameter.hash_func_both) { 00256 if (!(*(ht->parameter.hash_func_both))(&ht->parameter, 00257 key, index, 00258 rbt_hash)) 00259 return HASHTABLE_ERROR_INVALID_ARGUMENT; 00260 } else { 00261 *index = (*(ht->parameter.hash_func_key))(&ht->parameter, 00262 key); 00263 *rbt_hash = (*(ht->parameter.hash_func_rbt))(&ht->parameter, 00264 key); 00265 } 00266 00267 /* At the suggestion of Jim Lieb, die if a hash function sends 00268 us off the end of the array. */ 00269 00270 assert(*index < ht->parameter.index_size); 00271 00272 return HASHTABLE_SUCCESS; 00273 } 00274 00275 /*}@ */ 00276 00282 /* The following are the hash table primitives implementing the 00283 actual functionality. */ 00284 00297 struct hash_table * 00298 HashTable_Init(struct hash_param *hparam) 00299 { 00300 /* The hash table being constructed */ 00301 struct hash_table *ht = NULL; 00302 /* The index for initializing each partition */ 00303 uint32_t index = 0; 00304 /* Read-Write Lock attributes, to prevent write starvation under 00305 GLIBC */ 00306 pthread_rwlockattr_t rwlockattr; 00307 /* Hash partition */ 00308 struct hash_partition *partition = NULL; 00309 /* The number of fully initialized partitions */ 00310 uint32_t completed = 0; 00311 00312 if (pthread_rwlockattr_init(&rwlockattr) != 0) { 00313 return NULL; 00314 } 00315 00316 /* At some point factor this out into the OS directory. it is 00317 necessary to prevent writer starvation under GLIBC. */ 00318 #ifdef GLIBC 00319 if ((pthread_rwlockattr_setkind_np( 00320 &rwlockattrs, 00321 PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP)) != 0) { 00322 LogCrit(COMPONENT_HASHTABLE, 00323 "Unable to set writer-preference on lock attribute."); 00324 goto deconstruct; 00325 } 00326 #endif /* GLIBC */ 00327 00328 if ((ht = gsh_calloc(1, sizeof(struct hash_table) + 00329 (sizeof(struct hash_partition) * 00330 hparam->index_size))) == NULL) { 00331 goto deconstruct; 00332 } 00333 00334 /* Fixup entry size */ 00335 if (hparam->flags & HT_FLAG_CACHE) { 00336 if (! hparam->cache_entry_count) 00337 /* works fine with a good hash algo */ 00338 hparam->cache_entry_count = 32767; 00339 } 00340 00341 /* We need to save copy of the parameters in the table. */ 00342 ht->parameter = *hparam; 00343 for (index = 0; index < hparam->index_size; ++index) { 00344 partition = (&ht->partitions[index]); 00345 RBT_HEAD_INIT(&(partition->rbt)); 00346 00347 if (pthread_rwlock_init(&partition->lock, &rwlockattr) != 0) { 00348 LogCrit(COMPONENT_HASHTABLE, 00349 "Unable to initialize lock in hash table."); 00350 goto deconstruct; 00351 } 00352 00353 /* Allocate a cache if requested */ 00354 if (hparam->flags & HT_FLAG_CACHE) { 00355 partition->cache = gsh_calloc(1, CACHE_PAGE_SIZE(ht)); 00356 if (!(partition->cache)) { 00357 pthread_rwlock_destroy(&partition->lock); 00358 goto deconstruct; 00359 } 00360 } 00361 completed++; 00362 } 00363 00364 ht->node_pool = pool_init(NULL, sizeof(rbt_node_t), 00365 pool_basic_substrate, 00366 NULL, NULL, NULL); 00367 if (!(ht->node_pool)) { 00368 goto deconstruct; 00369 } 00370 ht->data_pool = pool_init(NULL, sizeof(hash_data_t), 00371 pool_basic_substrate, 00372 NULL, NULL, NULL); 00373 if (!(ht->data_pool)) 00374 goto deconstruct; 00375 00376 pthread_rwlockattr_destroy(&rwlockattr); 00377 return ht; 00378 00379 deconstruct: 00380 00381 while (completed != 0) { 00382 if (hparam->flags & HT_FLAG_CACHE) 00383 gsh_free(ht->partitions[completed - 1].cache); 00384 00385 pthread_rwlock_destroy( 00386 &(ht->partitions[completed - 1].lock)); 00387 completed--; 00388 } 00389 pool_destroy(ht->node_pool); 00390 pool_destroy(ht->data_pool); 00391 00392 gsh_free(ht); 00393 return (ht = NULL); 00394 } /* HashTable_Init */ 00395 00410 hash_error_t 00411 HashTable_Destroy(struct hash_table *ht, 00412 int (*free_func)(struct hash_buff, 00413 struct hash_buff)) 00414 { 00415 size_t index = 0; 00416 hash_error_t hrc = HASHTABLE_SUCCESS; 00417 00418 hrc = HashTable_Delall(ht, free_func); 00419 if (hrc != HASHTABLE_SUCCESS) { 00420 goto out; 00421 } 00422 00423 for (index = 0; index < ht->parameter.index_size; ++index) { 00424 if (ht->partitions[index].cache) { 00425 gsh_free(ht->partitions[index].cache); 00426 ht->partitions[index].cache = NULL; 00427 } 00428 00429 pthread_rwlock_destroy(&(ht->partitions[index].lock)); 00430 } 00431 pool_destroy(ht->node_pool); 00432 pool_destroy(ht->data_pool); 00433 gsh_free(ht); 00434 00435 out: 00436 return hrc; 00437 } 00438 00461 hash_error_t 00462 HashTable_GetLatch(struct hash_table *ht, 00463 struct hash_buff *key, 00464 struct hash_buff *val, 00465 int may_write, 00466 struct hash_latch *latch) 00467 { 00468 /* The index specifying the partition to search */ 00469 uint32_t index = 0; 00470 /* The node found for the key */ 00471 struct rbt_node *locator = NULL; 00472 /* The buffer descritpros for the key and value for the found entry */ 00473 struct hash_data *data = NULL; 00474 /* The hash value to be searched for within the Red-Black tree */ 00475 uint64_t rbt_hash = 0; 00476 /* Stored error return */ 00477 hash_error_t rc = HASHTABLE_SUCCESS; 00478 00479 /* This combination of options makes no sense ever */ 00480 assert(!(may_write && !latch)); 00481 00482 if ((rc = compute(ht, key, &index, &rbt_hash)) 00483 != HASHTABLE_SUCCESS) { 00484 return rc; 00485 } 00486 00487 if(isDebug(COMPONENT_HASHTABLE) && 00488 isFullDebug(ht->parameter.ht_log_component)) { 00489 char dispkey[HASHTABLE_DISPLAY_STRLEN]; 00490 00491 if(ht->parameter.key_to_str != NULL) 00492 ht->parameter.key_to_str(key, dispkey); 00493 else 00494 dispkey[0] = '\0'; 00495 00496 LogFullDebug(ht->parameter.ht_log_component, 00497 "Get %s Key=%p {%s} index=%"PRIu32" rbt_hash=%"PRIu64" latch=%p", 00498 ht->parameter.ht_name, 00499 key->pdata, dispkey, 00500 index, rbt_hash, latch); 00501 } 00502 00503 /* Acquire mutex */ 00504 if (may_write) { 00505 pthread_rwlock_wrlock(&(ht->partitions[index].lock)); 00506 } else { 00507 pthread_rwlock_rdlock(&(ht->partitions[index].lock)); 00508 } 00509 00510 rc = Key_Locate(ht, key, index, rbt_hash, &locator); 00511 00512 if (rc == HASHTABLE_SUCCESS) { 00513 /* Key was found */ 00514 data = RBT_OPAQ(locator); 00515 if (val) { 00516 val->pdata = data->buffval.pdata; 00517 val->len = data->buffval.len; 00518 } 00519 00520 if(isDebug(COMPONENT_HASHTABLE) && 00521 isFullDebug(ht->parameter.ht_log_component)) { 00522 char dispval[HASHTABLE_DISPLAY_STRLEN]; 00523 00524 if(ht->parameter.val_to_str != NULL) 00525 ht->parameter.val_to_str(&data->buffval, dispval); 00526 else 00527 dispval[0] = '\0'; 00528 00529 LogFullDebug(ht->parameter.ht_log_component, 00530 "Get %s returning Value=%p {%s}", 00531 ht->parameter.ht_name, 00532 data->buffval.pdata, dispval); 00533 } 00534 00535 } 00536 00537 if (((rc == HASHTABLE_SUCCESS) || 00538 (rc == HASHTABLE_ERROR_NO_SUCH_KEY)) && 00539 (latch != NULL)) { 00540 latch->index = index; 00541 latch->rbt_hash = rbt_hash; 00542 latch->locator = locator; 00543 } else { 00544 pthread_rwlock_unlock(&ht->partitions[index].lock); 00545 } 00546 00547 if(rc != HASHTABLE_SUCCESS && 00548 isDebug(COMPONENT_HASHTABLE) && 00549 isFullDebug(ht->parameter.ht_log_component)) 00550 LogFullDebug(ht->parameter.ht_log_component, 00551 "Get %s returning failure %s", 00552 ht->parameter.ht_name, 00553 hash_table_err_to_str(rc)); 00554 00555 return rc; 00556 } /* HashTable_GetLatch */ 00557 00572 void 00573 HashTable_ReleaseLatched(struct hash_table *ht, 00574 struct hash_latch *latch) 00575 { 00576 if (latch) { 00577 pthread_rwlock_unlock(&ht->partitions[latch->index].lock); 00578 memset(latch, 0, sizeof(struct hash_latch)); 00579 } 00580 } /* HashTable_ReleaseLatched */ 00581 00608 hash_error_t 00609 HashTable_SetLatched(struct hash_table *ht, 00610 struct hash_buff *key, 00611 struct hash_buff *val, 00612 struct hash_latch *latch, 00613 int overwrite, 00614 struct hash_buff *stored_key, 00615 struct hash_buff *stored_val) 00616 { 00617 /* Stored error return */ 00618 hash_error_t rc = HASHTABLE_SUCCESS; 00619 /* The pair of buffer descriptors locating both key and value 00620 for this object, what actually gets stored. */ 00621 hash_data_t *descriptors = NULL; 00622 /* Node giving the location to insert new node */ 00623 struct rbt_node *locator = NULL; 00624 /* New node for the case of non-overwrite */ 00625 struct rbt_node *mutator = NULL; 00626 00627 if(isDebug(COMPONENT_HASHTABLE) && 00628 isFullDebug(ht->parameter.ht_log_component)) { 00629 char dispkey[HASHTABLE_DISPLAY_STRLEN]; 00630 char dispval[HASHTABLE_DISPLAY_STRLEN]; 00631 00632 if(ht->parameter.key_to_str != NULL) 00633 ht->parameter.key_to_str(key, dispkey); 00634 else 00635 dispkey[0] = '\0'; 00636 00637 if(ht->parameter.val_to_str != NULL) 00638 ht->parameter.val_to_str(val, dispval); 00639 else 00640 dispval[0] = '\0'; 00641 00642 LogFullDebug(ht->parameter.ht_log_component, 00643 "Set %s Key=%p {%s} Value=%p {%s} index=%"PRIu32" rbt_hash=%"PRIu64, 00644 ht->parameter.ht_name, 00645 key->pdata, dispkey, 00646 val->pdata, dispval, 00647 latch->index, latch->rbt_hash); 00648 } 00649 00650 /* In the case of collision */ 00651 if (latch->locator) { 00652 if (!overwrite) { 00653 rc = HASHTABLE_ERROR_KEY_ALREADY_EXISTS; 00654 goto out; 00655 } 00656 00657 descriptors = RBT_OPAQ(latch->locator); 00658 00659 if(isDebug(COMPONENT_HASHTABLE) && 00660 isFullDebug(ht->parameter.ht_log_component)) { 00661 char dispkey[HASHTABLE_DISPLAY_STRLEN]; 00662 char dispval[HASHTABLE_DISPLAY_STRLEN]; 00663 00664 if(ht->parameter.key_to_str != NULL) 00665 ht->parameter.key_to_str(&descriptors->buffkey, dispkey); 00666 else 00667 dispkey[0] = '\0'; 00668 00669 if(ht->parameter.val_to_str != NULL) 00670 ht->parameter.val_to_str(&descriptors->buffval, dispval); 00671 else 00672 dispval[0] = '\0'; 00673 00674 LogFullDebug(ht->parameter.ht_log_component, 00675 "Set %s Key=%p {%s} Value=%p {%s} index=%"PRIu32" rbt_hash=%"PRIu64" was replaced", 00676 ht->parameter.ht_name, 00677 descriptors->buffkey.pdata, dispkey, 00678 descriptors->buffval.pdata, dispval, 00679 latch->index, latch->rbt_hash); 00680 } 00681 00682 if (stored_key) { 00683 *stored_key = descriptors->buffkey; 00684 } 00685 if (stored_val) { 00686 *stored_val = descriptors->buffval; 00687 } 00688 descriptors->buffkey = *key; 00689 descriptors->buffval = *val; 00690 rc = HASHTABLE_OVERWRITTEN; 00691 goto out; 00692 } 00693 00694 /* We have no collision, so go about creating and inserting a new 00695 node. */ 00696 00697 RBT_FIND(&ht->partitions[latch->index].rbt, 00698 locator, latch->rbt_hash); 00699 00700 mutator = pool_alloc(ht->node_pool, NULL); 00701 if (mutator == NULL) { 00702 rc = HASHTABLE_INSERT_MALLOC_ERROR; 00703 goto out; 00704 } 00705 00706 descriptors = pool_alloc(ht->data_pool, NULL); 00707 if (descriptors == NULL) { 00708 pool_free(ht->node_pool, mutator); 00709 rc = HASHTABLE_INSERT_MALLOC_ERROR; 00710 goto out; 00711 } 00712 00713 RBT_OPAQ(mutator) = descriptors; 00714 RBT_VALUE(mutator) = latch->rbt_hash; 00715 RBT_INSERT(&ht->partitions[latch->index].rbt, mutator, locator); 00716 00717 descriptors->buffkey.pdata = key->pdata; 00718 descriptors->buffkey.len = key->len; 00719 00720 descriptors->buffval.pdata = val->pdata; 00721 descriptors->buffval.len = val->len; 00722 00723 /* Only in the non-overwrite case */ 00724 ++ht->partitions[latch->index].count; 00725 00726 rc = HASHTABLE_SUCCESS; 00727 00728 out: 00729 HashTable_ReleaseLatched(ht, latch); 00730 00731 if(rc != HASHTABLE_SUCCESS && 00732 isDebug(COMPONENT_HASHTABLE) && 00733 isFullDebug(ht->parameter.ht_log_component)) 00734 LogFullDebug(ht->parameter.ht_log_component, 00735 "Set %s returning failure %s", 00736 ht->parameter.ht_name, 00737 hash_table_err_to_str(rc)); 00738 00739 return rc; 00740 } /* HashTable_SetLatched */ 00741 00762 hash_error_t 00763 HashTable_DeleteLatched(struct hash_table *ht, 00764 struct hash_buff *key, 00765 struct hash_latch *latch, 00766 struct hash_buff *stored_key, 00767 struct hash_buff *stored_val) 00768 { 00769 /* The pair of buffer descriptors comprising the stored entry */ 00770 struct hash_data *data = NULL; 00771 /* Its partition */ 00772 struct hash_partition *partition = &ht->partitions[latch->index]; 00773 00774 if (!latch->locator) { 00775 HashTable_ReleaseLatched(ht, latch); 00776 return HASHTABLE_SUCCESS; 00777 } 00778 00779 data = RBT_OPAQ(latch->locator); 00780 00781 if(isDebug(COMPONENT_HASHTABLE) && 00782 isFullDebug(ht->parameter.ht_log_component)) { 00783 char dispkey[HASHTABLE_DISPLAY_STRLEN]; 00784 char dispval[HASHTABLE_DISPLAY_STRLEN]; 00785 00786 if(ht->parameter.key_to_str != NULL) 00787 ht->parameter.key_to_str(&data->buffkey, dispkey); 00788 else 00789 dispkey[0] = '\0'; 00790 00791 if(ht->parameter.val_to_str != NULL) 00792 ht->parameter.val_to_str(&data->buffval, dispval); 00793 else 00794 dispval[0] = '\0'; 00795 00796 LogFullDebug(ht->parameter.ht_log_component, 00797 "Delete %s Key=%p {%s} Value=%p {%s} index=%"PRIu32" rbt_hash=%"PRIu64" was removed", 00798 ht->parameter.ht_name, 00799 data->buffkey.pdata, dispkey, 00800 data->buffval.pdata, dispval, 00801 latch->index, latch->rbt_hash); 00802 } 00803 00804 if (stored_key) { 00805 *stored_key = data->buffkey; 00806 } 00807 00808 if (stored_val) { 00809 *stored_val = data->buffval; 00810 } 00811 00812 /* Clear cache */ 00813 if(partition->cache) { 00814 uint32_t offset = cache_offsetof(ht, latch->rbt_hash); 00815 struct rbt_node *cnode = partition->cache[offset]; 00816 if (cnode) { 00817 #if COMPARE_BEFORE_CLEAR_CACHE 00818 struct hash_data *data1 = RBT_OPAQ(cnode); 00819 struct hash_data *data2 = RBT_OPAQ(latch->locator); 00820 if (ht->parameter.compare_key(&(data1->buffkey), 00821 &(data2->buffkey)) == 0) { 00822 LogFullDebug(COMPONENT_HASHTABLE_CACHE, 00823 "hash clear index %d slot %"PRIu64"\n", 00824 latch->index, offset); 00825 partition->cache[offset] = NULL; 00826 } 00827 #else 00828 LogFullDebug(COMPONENT_HASHTABLE_CACHE, 00829 "hash clear slot %d\n", offset); 00830 partition->cache[offset] = NULL; 00831 #endif 00832 } 00833 } 00834 00835 /* Now remove the entry */ 00836 RBT_UNLINK(&partition->rbt, latch->locator); 00837 pool_free(ht->data_pool, data); 00838 pool_free(ht->node_pool, latch->locator); 00839 00840 HashTable_ReleaseLatched(ht, latch); 00841 return HASHTABLE_SUCCESS; 00842 } /* HashTable_DeleteLatched */ 00843 00856 hash_error_t 00857 HashTable_Delall(struct hash_table *ht, 00858 int (*free_func)(struct hash_buff, 00859 struct hash_buff)) 00860 { 00861 /* Successive partition numbers */ 00862 uint32_t index = 0; 00863 00864 for (index = 0; index < ht->parameter.index_size; index++) { 00865 /* The root of each successive partition */ 00866 struct rbt_head *root = &ht->partitions[index].rbt; 00867 /* Pointer to node in tree for removal */ 00868 struct rbt_node *cursor = NULL; 00869 00870 pthread_rwlock_wrlock(&ht->partitions[index].lock); 00871 00872 /* Continue until there are no more entries in the red-black 00873 tree */ 00874 while ((cursor = RBT_LEFTMOST(root)) != NULL) { 00875 /* Pointer to the key and value descriptors for each successive 00876 entry */ 00877 hash_data_t *data = NULL; 00878 /* Aliased poitner to node, for freeing buffers after 00879 removal from tree */ 00880 struct rbt_node *holder = cursor; 00881 /* Buffer descriptor for key, as stored */ 00882 hash_buffer_t key; 00883 /* Buffer descriptor for value, as stored */ 00884 hash_buffer_t val; 00885 /* Return code from the free function. Zero on failure */ 00886 int rc = 0; 00887 00888 RBT_UNLINK(root, cursor); 00889 data = RBT_OPAQ(holder); 00890 00891 key = data->buffkey; 00892 val = data->buffval; 00893 00894 pool_free(ht->data_pool, data); 00895 pool_free(ht->node_pool, holder); 00896 --ht->partitions[index].count; 00897 rc = free_func(key, val); 00898 00899 if (rc == 0) { 00900 pthread_rwlock_unlock(&ht->partitions[index].lock); 00901 return HASHTABLE_ERROR_DELALL_FAIL; 00902 } 00903 } 00904 pthread_rwlock_unlock(&ht->partitions[index].lock); 00905 } 00906 00907 return HASHTABLE_SUCCESS; 00908 } /* HashTable_Delall */ 00909 00920 void 00921 HashTable_GetStats(struct hash_table *ht, 00922 struct hash_stat *hstat) 00923 { 00924 size_t i = 0; 00925 00926 /* Then compute the other values */ 00927 00928 /* A min value hash to be initialized with a huge value */ 00929 hstat->min_rbt_num_node = 1 << 31; 00930 00931 /* A max value is initialized with 0 */ 00932 hstat->max_rbt_num_node = 0; 00933 /* And so does the average value */ 00934 hstat->average_rbt_num_node = 0; 00935 00936 hstat->entries = 0; 00937 00938 for (i = 0; i < ht->parameter.index_size; i++) { 00939 if (ht->partitions[i].rbt.rbt_num_node > hstat->max_rbt_num_node) 00940 hstat->max_rbt_num_node 00941 = ht->partitions[i].rbt.rbt_num_node; 00942 00943 if (ht->partitions[i].rbt.rbt_num_node < hstat->min_rbt_num_node) 00944 hstat->min_rbt_num_node 00945 = ht->partitions[i].rbt.rbt_num_node; 00946 00947 hstat->average_rbt_num_node 00948 += ht->partitions[i].rbt.rbt_num_node; 00949 00950 hstat->entries += ht->partitions[i].count; 00951 } 00952 00953 hstat->average_rbt_num_node /= ht->parameter.index_size; 00954 } /* Hashtable_GetStats */ 00955 00965 size_t 00966 HashTable_GetSize(struct hash_table *ht) 00967 { 00968 uint32_t i = 0; 00969 size_t nb_entries = 0; 00970 00971 for (i = 0; i < ht->parameter.index_size; i++) 00972 nb_entries += ht->partitions[i].count; 00973 00974 return nb_entries; 00975 } /* HashTable_GetSize */ 00976 00988 void 00989 HashTable_Log(log_components_t component, 00990 struct hash_table *ht) 00991 { 00992 /* The current position in the hash table */ 00993 struct rbt_node *it = NULL; 00994 /* The root of the tree currently being inspected */ 00995 struct rbt_head *root; 00996 /* Buffer descriptors for the key and value */ 00997 hash_data_t *data = NULL; 00998 /* String representation of the key */ 00999 char dispkey[HASHTABLE_DISPLAY_STRLEN]; 01000 /* String representation of the stored value */ 01001 char dispval[HASHTABLE_DISPLAY_STRLEN]; 01002 /* Index for traversing the partitions */ 01003 uint32_t i = 0; 01004 /* Running count of entries */ 01005 size_t nb_entries = 0; 01006 /* Recomputed partitionindex */ 01007 uint32_t index = 0; 01008 /* Recomputed hash for Red-Black tree*/ 01009 uint64_t rbt_hash = 0; 01010 01011 LogFullDebug(component, 01012 "The hash is partitioned into %d trees", 01013 ht->parameter.index_size); 01014 01015 for (i = 0; i < ht->parameter.index_size; i++) { 01016 nb_entries += ht->partitions[i].count; 01017 } 01018 01019 LogFullDebug(component, "The hash contains %zd entries", 01020 nb_entries); 01021 01022 for (i = 0; i < ht->parameter.index_size; i++) { 01023 root = &ht->partitions[i].rbt; 01024 LogFullDebug(component, 01025 "The partition in position %"PRIu32 01026 "contains: %u entries", 01027 i, root->rbt_num_node); 01028 RBT_LOOP(root, it) { 01029 data = it->rbt_opaq; 01030 01031 ht->parameter.key_to_str(&(data->buffkey), dispkey); 01032 ht->parameter.val_to_str(&(data->buffval), dispval); 01033 01034 if (compute(ht, &data->buffkey, &index, &rbt_hash) 01035 != HASHTABLE_SUCCESS) { 01036 LogCrit(component, 01037 "Possible implementation error in hash_func_both"); 01038 index = 0; 01039 rbt_hash = 0; 01040 } 01041 01042 LogFullDebug(component, 01043 "%s => %s; index=%"PRIu32" rbt_hash=%"PRIu64, 01044 dispkey, dispval, index, rbt_hash); 01045 RBT_INCREMENT(it); 01046 } 01047 } 01048 } /* HashTable_Log */ 01049 01067 hash_error_t 01068 HashTable_Test_And_Set(struct hash_table *ht, 01069 struct hash_buff *key, 01070 struct hash_buff *val, 01071 hash_set_how_t how) 01072 { 01073 /* structure to hold retained state */ 01074 struct hash_latch latch; 01075 /* Stored return code */ 01076 hash_error_t rc = 0; 01077 01078 rc = HashTable_GetLatch(ht, key, NULL, 01079 (how != HASHTABLE_SET_HOW_TEST_ONLY), 01080 &latch); 01081 01082 if ((rc != HASHTABLE_SUCCESS) && 01083 (rc != HASHTABLE_ERROR_NO_SUCH_KEY)) { 01084 return rc; 01085 } 01086 01087 if (how == HASHTABLE_SET_HOW_TEST_ONLY) { 01088 HashTable_ReleaseLatched(ht, &latch); 01089 return rc; 01090 } 01091 01092 /* No point in calling HashTable_SetLatched when we know it will 01093 error. */ 01094 01095 if ((how == HASHTABLE_SET_HOW_SET_NO_OVERWRITE) && 01096 (rc == HASHTABLE_SUCCESS)) { 01097 HashTable_ReleaseLatched(ht, &latch); 01098 return HASHTABLE_ERROR_KEY_ALREADY_EXISTS; 01099 } 01100 01101 rc = HashTable_SetLatched(ht, 01102 key, 01103 val, 01104 &latch, 01105 (how == HASHTABLE_SET_HOW_SET_OVERWRITE), 01106 NULL, 01107 NULL); 01108 01109 if (rc == HASHTABLE_OVERWRITTEN) { 01110 rc = HASHTABLE_SUCCESS; 01111 } 01112 01113 return rc; 01114 } /* HashTable_Test_And_Set */ 01115 01132 hash_error_t 01133 HashTable_GetRef(hash_table_t *ht, 01134 hash_buffer_t *key, 01135 hash_buffer_t *val, 01136 void (*get_ref)(hash_buffer_t *)) 01137 { 01138 /* structure to hold retained state */ 01139 struct hash_latch latch; 01140 /* Stored return code */ 01141 hash_error_t rc = 0; 01142 01143 rc = HashTable_GetLatch(ht, key, val, FALSE, &latch); 01144 01145 switch (rc) { 01146 case HASHTABLE_SUCCESS: 01147 if (get_ref != NULL) { 01148 get_ref(val); 01149 } 01150 case HASHTABLE_ERROR_NO_SUCH_KEY: 01151 HashTable_ReleaseLatched(ht, &latch); 01152 break; 01153 01154 default: 01155 break; 01156 } 01157 01158 return rc; 01159 } /* HashTable_GetRef */ 01160 01175 hash_error_t 01176 HashTable_Get_and_Del(hash_table_t *ht, 01177 hash_buffer_t *key, 01178 hash_buffer_t *val, 01179 hash_buffer_t *stored_key) 01180 { 01181 /* structure to hold retained state */ 01182 struct hash_latch latch; 01183 /* Stored return code */ 01184 hash_error_t rc = 0; 01185 01186 rc = HashTable_GetLatch(ht, key, NULL, 01187 TRUE, &latch); 01188 01189 switch (rc) { 01190 case HASHTABLE_SUCCESS: 01191 return HashTable_DeleteLatched(ht, 01192 key, 01193 &latch, 01194 stored_key, 01195 val); 01196 01197 01198 case HASHTABLE_ERROR_NO_SUCH_KEY: 01199 HashTable_ReleaseLatched(ht, &latch); 01200 default: 01201 return rc; 01202 } 01203 01204 } /* HashTable_Get_and_Del */ 01205 01226 hash_error_t 01227 HashTable_DelRef(hash_table_t *ht, 01228 hash_buffer_t *key, 01229 hash_buffer_t *stored_key, 01230 hash_buffer_t *stored_val, 01231 int (*put_ref)(hash_buffer_t *)) 01232 { 01233 /* structure to hold retained state */ 01234 struct hash_latch latch; 01235 /* Stored return code */ 01236 hash_error_t rc = 0; 01237 /* Temporary buffer descriptor. We need the value to call the 01238 decrement function, even if the caller didn't request the 01239 value. */ 01240 struct hash_buff temp_val; 01241 01242 rc = HashTable_GetLatch(ht, key, &temp_val, 01243 TRUE, &latch); 01244 01245 switch (rc) { 01246 case HASHTABLE_ERROR_NO_SUCH_KEY: 01247 HashTable_ReleaseLatched(ht, &latch); 01248 break; 01249 01250 case HASHTABLE_SUCCESS: 01251 if (put_ref != NULL) { 01252 if (put_ref(&temp_val) != 0) { 01253 HashTable_ReleaseLatched(ht, &latch); 01254 rc = HASHTABLE_NOT_DELETED; 01255 goto out; 01256 } 01257 } 01258 rc = HashTable_DeleteLatched(ht, 01259 key, 01260 &latch, 01261 stored_key, 01262 stored_val); 01263 break; 01264 01265 default: 01266 break; 01267 } 01268 01269 out: 01270 01271 return rc; 01272 } /* HashTable_DelRef */ 01273 01290 hash_error_t 01291 HashTable_DelSafe(hash_table_t *ht, 01292 hash_buffer_t *key, 01293 hash_buffer_t *val) 01294 { 01295 /* structure to hold retained state */ 01296 struct hash_latch latch; 01297 /* Stored return code */ 01298 hash_error_t rc = 0; 01299 /* Temporary buffer descriptor. We need the value to call the 01300 decrement function, even if the caller didn't request the 01301 value. */ 01302 struct hash_buff found_val; 01303 01304 rc = HashTable_GetLatch(ht, key, &found_val, 01305 TRUE, &latch); 01306 01307 switch (rc) { 01308 case HASHTABLE_ERROR_NO_SUCH_KEY: 01309 HashTable_ReleaseLatched(ht, &latch); 01310 break; 01311 01312 case HASHTABLE_SUCCESS: 01313 if (found_val.pdata == val->pdata) { 01314 rc = HashTable_DeleteLatched(ht, 01315 key, 01316 &latch, 01317 NULL, 01318 NULL); 01319 } else { 01320 rc = HASHTABLE_ERROR_NO_SUCH_KEY; 01321 HashTable_ReleaseLatched(ht, &latch); 01322 } 01323 break; 01324 01325 default: 01326 break; 01327 } 01328 01329 return rc; 01330 } /* HashTable_DelSafe */ 01331 01332 /* @} */ 01333