/** * 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 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; }