nfs-ganesha 1.4

HashTable.c

Go to the documentation of this file.
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