// Function handles the rehash process encountered when a hash reaches // 80% capacity. Hate locking the entire function, but we're rehashing, so it's // pretty much unavoidable. void rehash(hash_t *table) { // Acquire write-lock for hash. pthread_rwlock_wrlock(&table->lock); // Abort rehash if we're frozen. if (table->frozen) { pthread_rwlock_unlock(&table->lock); return; } // Allocate new table with calloc to allow for NULL checks. hash_node_t **new_data = calloc(table->size * 2, sizeof(hash_node_t *)); // Copy all previous data into new, larger, hash. hash_node_t **old_data = table->data; for(int i = 0; i < table->size; i++) { hash_node_t *current = old_data[i]; while (current) { hash_node_t *tmp = current->next; current->next = NULL; // Calculate new hash value and insert. unsigned int hash = (unsigned int) XXH64(current->key, strlen(current->key), 0) % (table->size * 2); new_data[hash] = insert_hash_node(new_data[hash], current); current = tmp; } } // Update hash struct with changes. table->data = new_data; table->size *= 2; free(old_data); }
void hash_insert(HASH_NODE** table,int value) { int hash_value; hash_value = hash(value); insert_hash_node(&table[hash_value],value); }
// Insert data into a hash for a specific key. int hash_put(hash_t *table, char *key, void *data) { // Verify parameters. if (!table || !key || !data) return HASH_INVAL_ERROR; // Check if table needs a rehash. int rehashed = 0; if (table->count / (float) table->size > 0.8) { rehash(table); rehashed = 1; } // Acquire read lock if we didn't rehash the table. if (!rehashed) pthread_rwlock_rdlock(&table->lock); // Cache the table size so that we can detect rehashes in the future. int orig_size = table->size; // Generate hash value and insert. unsigned int hash = (unsigned int) XXH64(key, strlen(key), 0) % table->size; // Abort if we're frozen. if (table->frozen) { pthread_rwlock_unlock(&table->lock); return HASH_FROZEN_ERROR; } // Verify that table does not already contain given key. // Check if we're dealing with a hash collision, or a repeat key. if (!find_hash_node(table->data[hash], key)) { // If we didn't need to rehash the table, grab the write-lock now. // If we didn't rehash, means that we've gotten to this point with only a read-lock. // Possible for two threads to attempt to insert the same key at the same time and // both make it here. After acquiring the write-lock, double check that the key // doesn't exist in the list. if (!rehashed) { pthread_rwlock_unlock(&table->lock); pthread_rwlock_wrlock(&table->lock); // It's possible, since read-locks can't be converted to write-locks atomically, // that someone rehashed the table while we were unlocked. Update the hash value // if this happened. if (table->size != orig_size) hash = (unsigned int) XXH64(key, strlen(key), 0) % table->size; // Recheck for the key. if (find_hash_node(table->data[hash], key)) { // Contention on duplicate key insert. pthread_rwlock_unlock(&table->lock); return HASH_EXISTS_ERROR; } } // Data is new. hash_node_t *node = create_hash_node(key, data, table->elem_size); // We have exclusive write access to the hash, and are the only thread attempting to insert this // key. Do the deed. table->data[hash] = insert_hash_node(table->data[hash], node); table->count++; pthread_rwlock_unlock(&table->lock); return HASH_SUCCESS; } else { // Key already exists in table. pthread_rwlock_unlock(&table->lock); return HASH_EXISTS_ERROR; } }