cyclone/ck-polyfill.c
2024-01-17 19:43:47 -08:00

382 lines
9.9 KiB
C

/**
* Cyclone Scheme
* https://github.com/justinethier/cyclone
*
* Copyright (c) 2020, Justin Ethier
* All rights reserved.
*
* An optional module that can be used to compile Cyclone when
* libck is not available. Note there will be a performance impact!
* It is recommended to compile with libck where possible.
*/
#include "cyclone/types.h"
#include "cyclone/runtime.h"
#include "ck-polyfill.h"
#include <unistd.h>
static pthread_mutex_t glock;
void ck_polyfill_init()
{
// will need to call this as soon as possible, perhaps from main()
if (pthread_mutex_init(&(glock), NULL) != 0) {
fprintf(stderr, "Unable to initialize global ck mutex\n");
exit(1);
}
}
// CK Hashset section
bool ck_hs_init(ck_hs_t * hs, unsigned int mode, ck_hs_hash_cb_t * hash_func,
ck_hs_compare_cb_t * cmp, struct ck_malloc *alloc,
unsigned long capacity, unsigned long seed)
{
(*hs).hs = simple_hashset_create();
if (pthread_mutex_init(&((*hs).lock), NULL) != 0) {
fprintf(stderr, "Unable to initialize ck hashset mutex\n");
exit(1);
}
return true;
}
void *ck_hs_get(ck_hs_t * _hs, unsigned long hash, const void *key)
{
void *result = NULL;
int index = -1;
simple_hashset_t set = (*_hs).hs;
pthread_mutex_lock(&((*_hs).lock));
index = simple_hashset_is_member(set, (symbol_type *) key);
if (index > 0) {
result = (void *)(set->items[index].item);
}
pthread_mutex_unlock(&((*_hs).lock));
return result;
}
bool ck_hs_put(ck_hs_t * _hs, unsigned long hash, const void *key)
{
bool result = false;
int rv, index;
simple_hashset_t hs = (*_hs).hs;
pthread_mutex_lock(&((*_hs).lock));
//index = simple_hashset_is_member(hs, (symbol_type *)key);
//if (index == 0) {
rv = simple_hashset_add(hs, (symbol_type *) key);
if (rv >= 0) {
result = true;
}
//}
pthread_mutex_unlock(&((*_hs).lock));
return result;
}
// CK Array section
bool
ck_array_init(ck_array_t * array, unsigned int mode,
struct ck_malloc *allocator, unsigned int initial_length)
{
(*array).hs = hashset_create();
if (pthread_mutex_init(&((*array).lock), NULL) != 0) {
fprintf(stderr, "Unable to initialize ck array mutex\n");
exit(1);
}
return true;
}
// DESCRIPTION
// The ck_array_put_unique(3) function will attempt to insert the value of
// pointer into the array pointed to by array. This function may incur
// additional memory allocations if not enough memory has been allocated in
// the array for a new entry. The operation is also free to apply the opera-
// tion immediately if there is an opportunity for elimination with a pend-
// ing (uncommitted) remove operation. The function will not make any modi-
// fications if the pointer already exists in the array.
//
// RETURN VALUES
// This function returns 1 if the pointer already exists in the array. It
// returns 0 if the put operation succeeded. It returns -1 on error due to
// internal memory allocation failures.
int ck_array_put_unique(ck_array_t * array, void *pointer)
{
pthread_mutex_lock(&(array->lock));
hashset_add(array->hs, pointer);
pthread_mutex_unlock(&(array->lock));
return true;
}
// DESCRIPTION
// The ck_array_remove(3) function will attempt to remove the value of
// pointer into the array pointed to by array. The operation is also free to
// apply the operation immediately if there is an opportunity for elimina-
// tion with a pending (uncommitted) put operation. If no elimination was
// possible, the function may require to allocate more memory.
//
// RETURN VALUES
// This function returns true if the remove operation succeeded. It will
// return false otherwise due to internal allocation failures or because the
// value did not exist.
bool ck_array_remove(ck_array_t * array, void *pointer)
{
pthread_mutex_lock(&(array->lock));
hashset_remove(array->hs, pointer);
pthread_mutex_unlock(&(array->lock));
return true;
}
// DESCRIPTION
// The ck_array_commit(3) function will commit any pending put or remove
// operations associated with the array. The function may end up requesting
// the safe reclamation of memory actively being iterated upon by other
// threads.
//
// RETURN VALUES
// This function returns true if the commit operation succeeded. It will
// return false otherwise, and pending operations will not be applied.
bool ck_array_commit(ck_array_t * array)
{
// Nothing to do in this polyfill
return true;
}
// TODO: global pthread mutex lock for this? obviously not ideal but the
// whole purpose of this module is a minimal interface for compatibility
// not speed
bool ck_pr_cas_int(int *target, int old_value, int new_value)
{
bool result = false;
pthread_mutex_lock(&glock);
if (*target == old_value) {
*target = new_value;
result = true;
}
pthread_mutex_unlock(&glock);
return result;
}
bool ck_pr_cas_ptr(void *target, void *old_value, void *new_value)
{
bool result = false;
pthread_mutex_lock(&glock);
if (*(void **)target == old_value) {
*(void **)target = new_value;
result = true;
}
pthread_mutex_unlock(&glock);
return result;
// *(void **)v = set;
}
bool ck_pr_cas_8(uint8_t * target, uint8_t old_value, uint8_t new_value)
{
bool result = false;
pthread_mutex_lock(&glock);
if (*target == old_value) {
*target = new_value;
result = true;
}
pthread_mutex_unlock(&glock);
return result;
}
void ck_pr_add_ptr(void *target, uintptr_t delta)
{
pthread_mutex_lock(&glock);
size_t value = (size_t)target;
size_t d = (size_t)delta;
size_t result = value + d;
*(void **)target = (void *)result;
// *(void **)v = set;
pthread_mutex_unlock(&glock);
}
void ck_pr_add_int(int *target, int delta)
{
pthread_mutex_lock(&glock);
(*target) += delta;
pthread_mutex_unlock(&glock);
}
void ck_pr_add_8(uint8_t * target, uint8_t delta)
{
pthread_mutex_lock(&glock);
(*target) += delta;
pthread_mutex_unlock(&glock);
}
void *ck_pr_load_ptr(const void *target)
{
void *result;
pthread_mutex_lock(&glock);
result = *(void **)target;
pthread_mutex_unlock(&glock);
return result;
}
int ck_pr_load_int(const int *target)
{
int result;
pthread_mutex_lock(&glock);
result = *target;
pthread_mutex_unlock(&glock);
return result;
}
uint8_t ck_pr_load_8(const uint8_t * target)
{
uint8_t result;
pthread_mutex_lock(&glock);
result = *target;
pthread_mutex_unlock(&glock);
return result;
}
void ck_pr_store_ptr(void *target, void *value)
{
pthread_mutex_lock(&glock);
*(void **)target = value;
pthread_mutex_unlock(&glock);
}
// Simple hashset
static const size_t prime_1 = 73;
static const size_t prime_2 = 5009;
size_t hash_function(const char *str, size_t len)
{
unsigned long hash = 5381;
int c;
while (c = *str++) {
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
}
return hash;
}
simple_hashset_t simple_hashset_create()
{
simple_hashset_t set =
(simple_hashset_t) calloc(1, sizeof(struct simple_hashset_st));
if (set == NULL) {
return NULL;
}
set->hash_func = hash_function;
set->nbits = 3;
set->capacity = (size_t)(1 << set->nbits);
set->mask = set->capacity - 1;
set->items =
(struct simple_hashset_item_st *)calloc(set->capacity,
sizeof(struct
simple_hashset_item_st));
if (set->items == NULL) {
simple_hashset_destroy(set);
return NULL;
}
set->nitems = 0;
set->n_deleted_items = 0;
return set;
}
void simple_hashset_destroy(simple_hashset_t set)
{
if (set) {
free(set->items);
}
free(set);
}
void simple_hashset_set_hash_function(simple_hashset_t set, hash_func_t func)
{
set->hash_func = func;
}
static int simple_hashset_add_member(simple_hashset_t set, symbol_type * key,
size_t hash)
{
size_t index;
if (hash < 2) {
return -1;
}
index = set->mask & (prime_1 * hash);
while (set->items[index].hash != 0 && set->items[index].hash != 1) {
if (set->items[index].hash == hash) {
return 0;
} else {
/* search free slot */
index = set->mask & (index + prime_2);
}
}
++set->nitems;
if (set->items[index].hash == 1) {
--set->n_deleted_items;
}
set->items[index].hash = hash;
set->items[index].item = key;
return 1;
}
static void set_maybe_rehash(simple_hashset_t set)
{
struct simple_hashset_item_st *old_items;
size_t old_capacity, index;
if (set->nitems + set->n_deleted_items >= (double)set->capacity * 0.85) {
old_items = set->items;
old_capacity = set->capacity;
++set->nbits;
set->capacity = (size_t)(1 << set->nbits);
set->mask = set->capacity - 1;
set->items =
(struct simple_hashset_item_st *)calloc(set->capacity,
sizeof(struct
simple_hashset_item_st));
set->nitems = 0;
set->n_deleted_items = 0;
//assert(set->items);
for (index = 0; index < old_capacity; ++index) {
simple_hashset_add_member(set, old_items[index].item,
old_items[index].hash);
}
free(old_items);
}
}
int simple_hashset_add(simple_hashset_t set, symbol_type * key)
{
size_t key_len = strlen(key->desc);
size_t hash = set->hash_func(key->desc, key_len);
int rv = simple_hashset_add_member(set, key, hash);
set_maybe_rehash(set);
return rv;
}
int simple_hashset_is_member(simple_hashset_t set, symbol_type * key)
{
size_t key_len = strlen(key->desc);
size_t hash = set->hash_func(key->desc, key_len);
size_t index = set->mask & (prime_1 * hash);
while (set->items[index].hash != 0) {
if (set->items[index].hash == hash) {
return index;
} else {
index = set->mask & (index + prime_2);
}
}
return 0;
}