#include <assert.h>
#include <stdlib.h>
#include <system.h>
#include <pthread.h>
static size_t
lookup (NAME *htab, HASHTYPE hval)
{
size_t idx = 1 + (hval < htab->size ? hval : hval % htab->size);
HASHTYPE hash;
hash = atomic_load_explicit(&htab->table[idx].hashval,
memory_order_acquire);
if (hash == hval)
return idx;
else if (hash == 0)
return 0;
HASHTYPE second_hash = 1 + hval % (htab->size - 2);
for(;;)
{
if (idx <= second_hash)
idx = htab->size + idx - second_hash;
else
idx -= second_hash;
hash = atomic_load_explicit(&htab->table[idx].hashval,
memory_order_acquire);
if (hash == hval)
return idx;
else if (hash == 0)
return 0;
}
}
static int
insert_helper (NAME *htab, HASHTYPE hval, TYPE val)
{
size_t idx = 1 + (hval < htab->size ? hval : hval % htab->size);
TYPE val_ptr;
HASHTYPE hash;
hash = atomic_load_explicit(&htab->table[idx].hashval,
memory_order_acquire);
if (hash == hval)
return -1;
else if (hash == 0)
{
val_ptr = NULL;
atomic_compare_exchange_strong_explicit(&htab->table[idx].val_ptr,
(uintptr_t *) &val_ptr,
(uintptr_t) val,
memory_order_acquire,
memory_order_acquire);
if (val_ptr == NULL)
{
atomic_store_explicit(&htab->table[idx].hashval, hval,
memory_order_release);
return 0;
}
else
{
do
{
hash = atomic_load_explicit(&htab->table[idx].hashval,
memory_order_acquire);
}
while (hash == 0);
if (hash == hval)
return -1;
}
}
HASHTYPE second_hash = 1 + hval % (htab->size - 2);
for(;;)
{
if (idx <= second_hash)
idx = htab->size + idx - second_hash;
else
idx -= second_hash;
hash = atomic_load_explicit(&htab->table[idx].hashval,
memory_order_acquire);
if (hash == hval)
return -1;
else if (hash == 0)
{
val_ptr = NULL;
atomic_compare_exchange_strong_explicit(&htab->table[idx].val_ptr,
(uintptr_t *) &val_ptr,
(uintptr_t) val,
memory_order_acquire,
memory_order_acquire);
if (val_ptr == NULL)
{
atomic_store_explicit(&htab->table[idx].hashval, hval,
memory_order_release);
return 0;
}
else
{
do
{
hash = atomic_load_explicit(&htab->table[idx].hashval,
memory_order_acquire);
}
while (hash == 0);
if (hash == hval)
return -1;
}
}
}
}
#define NO_RESIZING 0u
#define ALLOCATING_MEMORY 1u
#define MOVING_DATA 3u
#define CLEANING 2u
#define STATE_BITS 2u
#define STATE_INCREMENT (1u << STATE_BITS)
#define STATE_MASK (STATE_INCREMENT - 1)
#define GET_STATE(A) ((A) & STATE_MASK)
#define IS_NO_RESIZE_OR_CLEANING(A) (((A) & 0x1u) == 0)
#define GET_ACTIVE_WORKERS(A) ((A) >> STATE_BITS)
#define INITIALIZATION_BLOCK_SIZE 256
#define MOVE_BLOCK_SIZE 256
#define CEIL(A, B) (((A) + (B) - 1) / (B))
static void resize_helper(NAME *htab, int blocking)
{
size_t num_old_blocks = CEIL(htab->old_size, MOVE_BLOCK_SIZE);
size_t num_new_blocks = CEIL(htab->size, INITIALIZATION_BLOCK_SIZE);
size_t my_block;
size_t num_finished_blocks = 0;
while ((my_block = atomic_fetch_add_explicit(&htab->next_init_block, 1,
memory_order_acquire))
< num_new_blocks)
{
size_t record_it = my_block * INITIALIZATION_BLOCK_SIZE;
size_t record_end = (my_block + 1) * INITIALIZATION_BLOCK_SIZE;
if (record_end > htab->size)
record_end = htab->size;
while (record_it++ != record_end)
{
atomic_init(&htab->table[record_it].hashval, (uintptr_t) NULL);
atomic_init(&htab->table[record_it].val_ptr, (uintptr_t) NULL);
}
num_finished_blocks++;
}
atomic_fetch_add_explicit(&htab->num_initialized_blocks,
num_finished_blocks, memory_order_release);
while (atomic_load_explicit(&htab->num_initialized_blocks,
memory_order_acquire) != num_new_blocks);
num_finished_blocks = 0;
while ((my_block = atomic_fetch_add_explicit(&htab->next_move_block, 1,
memory_order_acquire))
< num_old_blocks)
{
size_t record_it = my_block * MOVE_BLOCK_SIZE;
size_t record_end = (my_block + 1) * MOVE_BLOCK_SIZE;
if (record_end > htab->old_size)
record_end = htab->old_size;
while (record_it++ != record_end)
{
TYPE val_ptr = (TYPE) atomic_load_explicit(
&htab->old_table[record_it].val_ptr,
memory_order_acquire);
if (val_ptr == NULL)
continue;
HASHTYPE hashval = atomic_load_explicit(
&htab->old_table[record_it].hashval,
memory_order_acquire);
assert(hashval);
insert_helper(htab, hashval, val_ptr);
}
num_finished_blocks++;
}
atomic_fetch_add_explicit(&htab->num_moved_blocks, num_finished_blocks,
memory_order_release);
if (blocking)
while (atomic_load_explicit(&htab->num_moved_blocks,
memory_order_acquire) != num_old_blocks);
}
static void
resize_coordinator(NAME *htab)
{
htab->old_size = htab->size;
htab->old_table = htab->table;
htab->size = next_prime(htab->size * 2);
htab->table = malloc((1 + htab->size) * sizeof(htab->table[0]));
assert(htab->table);
atomic_fetch_xor_explicit(&htab->resizing_state,
ALLOCATING_MEMORY ^ MOVING_DATA,
memory_order_release);
resize_helper(htab, 1);
size_t resize_state = atomic_fetch_xor_explicit(&htab->resizing_state,
MOVING_DATA ^ CLEANING,
memory_order_acq_rel);
while (GET_ACTIVE_WORKERS(resize_state) != 0)
resize_state = atomic_load_explicit(&htab->resizing_state,
memory_order_acquire);
atomic_store_explicit(&htab->next_init_block, 0, memory_order_relaxed);
atomic_store_explicit(&htab->num_initialized_blocks, 0,
memory_order_relaxed);
atomic_store_explicit(&htab->next_move_block, 0, memory_order_relaxed);
atomic_store_explicit(&htab->num_moved_blocks, 0, memory_order_relaxed);
free(htab->old_table);
atomic_fetch_xor_explicit(&htab->resizing_state, CLEANING ^ NO_RESIZING,
memory_order_relaxed);
}
static void
resize_worker(NAME *htab)
{
size_t resize_state = atomic_load_explicit(&htab->resizing_state,
memory_order_acquire);
if (IS_NO_RESIZE_OR_CLEANING(resize_state))
return;
resize_state = atomic_fetch_add_explicit(&htab->resizing_state,
STATE_INCREMENT,
memory_order_acquire);
if (IS_NO_RESIZE_OR_CLEANING(resize_state))
{
atomic_fetch_sub_explicit(&htab->resizing_state, STATE_INCREMENT,
memory_order_relaxed);
return;
}
while (GET_STATE(resize_state) == ALLOCATING_MEMORY)
resize_state = atomic_load_explicit(&htab->resizing_state,
memory_order_acquire);
assert(GET_STATE(resize_state) != NO_RESIZING);
if (GET_STATE(resize_state) == CLEANING)
{
atomic_fetch_sub_explicit(&htab->resizing_state, STATE_INCREMENT,
memory_order_relaxed);
return;
}
resize_helper(htab, 0);
atomic_fetch_sub_explicit(&htab->resizing_state, STATE_INCREMENT,
memory_order_release);
}
int
#define INIT(name) _INIT (name)
#define _INIT(name) \
name##_init
INIT(NAME) (NAME *htab, size_t init_size)
{
init_size = next_prime (init_size);
htab->size = init_size;
atomic_init(&htab->filled, 0);
atomic_init(&htab->resizing_state, 0);
atomic_init(&htab->next_init_block, 0);
atomic_init(&htab->num_initialized_blocks, 0);
atomic_init(&htab->next_move_block, 0);
atomic_init(&htab->num_moved_blocks, 0);
pthread_rwlock_init(&htab->resize_rwl, NULL);
htab->table = malloc ((init_size + 1) * sizeof (htab->table[0]));
if (htab->table == NULL)
return -1;
for (size_t i = 0; i <= init_size; i++)
{
atomic_init(&htab->table[i].hashval, (uintptr_t) NULL);
atomic_init(&htab->table[i].val_ptr, (uintptr_t) NULL);
}
return 0;
}
int
#define FREE(name) _FREE (name)
#define _FREE(name) \
name##_free
FREE(NAME) (NAME *htab)
{
pthread_rwlock_destroy(&htab->resize_rwl);
free (htab->table);
return 0;
}
int
#define INSERT(name) _INSERT (name)
#define _INSERT(name) \
name##_insert
INSERT(NAME) (NAME *htab, HASHTYPE hval, TYPE data)
{
int incremented = 0;
for(;;)
{
while (pthread_rwlock_tryrdlock(&htab->resize_rwl) != 0)
resize_worker(htab);
size_t filled;
if (!incremented)
{
filled = atomic_fetch_add_explicit(&htab->filled, 1,
memory_order_acquire);
incremented = 1;
}
else
{
filled = atomic_load_explicit(&htab->filled,
memory_order_acquire);
}
if (100 * filled > 90 * htab->size)
{
size_t resizing_state = atomic_load_explicit(&htab->resizing_state,
memory_order_acquire);
if (resizing_state == 0 &&
atomic_compare_exchange_strong_explicit(&htab->resizing_state,
&resizing_state,
ALLOCATING_MEMORY,
memory_order_acquire,
memory_order_acquire))
{
pthread_rwlock_unlock(&htab->resize_rwl);
pthread_rwlock_wrlock(&htab->resize_rwl);
resize_coordinator(htab);
pthread_rwlock_unlock(&htab->resize_rwl);
}
else
{
pthread_rwlock_unlock(&htab->resize_rwl);
resize_worker(htab);
}
}
else
{
break;
}
}
int ret_val = insert_helper(htab, hval, data);
if (ret_val == -1)
atomic_fetch_sub_explicit(&htab->filled, 1, memory_order_relaxed);
pthread_rwlock_unlock(&htab->resize_rwl);
return ret_val;
}
TYPE
#define FIND(name) _FIND (name)
#define _FIND(name) \
name##_find
FIND(NAME) (NAME *htab, HASHTYPE hval)
{
while (pthread_rwlock_tryrdlock(&htab->resize_rwl) != 0)
resize_worker(htab);
size_t idx;
hval = hval ?: 1;
idx = lookup(htab, hval);
if (idx == 0)
{
pthread_rwlock_unlock(&htab->resize_rwl);
return NULL;
}
TYPE ret_val = (TYPE) atomic_load_explicit(&htab->table[idx].val_ptr,
memory_order_relaxed);
pthread_rwlock_unlock(&htab->resize_rwl);
return ret_val;
}