From c5fa68fdf5bc2a175794a2695a0d54ff759f8fcf Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Fri, 30 Mar 2018 22:37:30 -0700 Subject: [PATCH] Implement an initial version of reference counting. --- CMakeLists.txt | 2 +- include/refcount.h | 79 ++++++++++++++++++++++++++++++++++++++++++++++ src/refcount.c | 57 +++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 include/refcount.h create mode 100644 src/refcount.c diff --git a/CMakeLists.txt b/CMakeLists.txt index b9a588c..d35f0c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ project(libabacus) add_compile_options(-pedantic -Wall) -add_library(abacus STATIC src/lexer.c src/util.c src/table.c src/parser.c src/libabacus.c src/tree.c src/debug.c src/parsetype.c src/reserved.c src/trie.c) +add_library(abacus STATIC src/lexer.c src/util.c src/table.c src/parser.c src/libabacus.c src/tree.c src/debug.c src/parsetype.c src/reserved.c src/trie.c src/refcount.c) add_executable(libabacus src/main.c) add_subdirectory(external/liblex) diff --git a/include/refcount.h b/include/refcount.h new file mode 100644 index 0000000..aa74c23 --- /dev/null +++ b/include/refcount.h @@ -0,0 +1,79 @@ +#ifndef LIBABACUS_REFCOUNT_H +#define LIBABACUS_REFCOUNT_H + +#include "result.h" + +/** + * A struct for holding + * the number of references + * to a value, as well as the function required + * to free the value. + */ +struct libab_ref_count_s { + /** + * The fucntion to free the value. + * Can be NULL for no-op. + */ + void (*free_func)(void* data); + /** + * The number of references that + * prevent the deallocation of the value. + */ + int strong; + /** + * The number of references + * that still exist, even to a freed instance. + */ + int weak; +}; + +/** + * A reference to a value. + */ +struct libab_ref_s { + /** + * Whether this reference is a strong reference. + */ + int strong; + /** + * The reference count struct keeping track + * of how many references are pointing to the value. + */ + struct libab_ref_count_s* count; + /** + * The value this reference holds. + */ + void* data; +}; + +typedef struct libab_ref_s libab_ref; +typedef struct libab_ref_count_s libab_ref_count; + +/** + * Creates a new referene, using the given data and free function. + * @param ref the reference to initialize with the given data. + * @param data the data to reference count. + * @param free_func the function to use to realease the data when refcount reaches 0. + * @return the result of the construction of the reference. + */ +libab_result libab_ref_new(libab_ref* ref, void* data, void (*free_func)(void* data)); +/** + * Turns the given reference into a weak reference, + * making it not keep the data allocated. + */ +void libab_ref_weaken(libab_ref* ref); +/** + * Releases this particular reference to the data. + * This doesn't necessarily free the underlying data. + */ +void libab_ref_free(libab_ref* ref); +/** + * Copies this reference, thereby increasing the reference count. + */ +void libab_ref_copy(libab_ref* ref, libab_ref* into); +/** + * Gets the value of the reference. + */ +void* libab_ref_get(libab_ref* ref); + +#endif diff --git a/src/refcount.c b/src/refcount.c new file mode 100644 index 0000000..aa426e1 --- /dev/null +++ b/src/refcount.c @@ -0,0 +1,57 @@ +#include "refcount.h" +#include +#include + +libab_result libab_ref_new(libab_ref* ref, void* data, void (*free_func)(void* data)) { + libab_result result = LIBAB_SUCCESS; + ref->strong = 1; + ref->data = data; + if((ref->count = malloc(sizeof(*(ref->count))))) { + ref->count->strong = ref->count->weak = 1; + ref->count->free_func = free_func; + } else { + result = LIBAB_MALLOC; + } + return result; +} + +void _libab_ref_changed(libab_ref* ref) { + if(ref->count->strong == 0) { + ref->count->strong--; + if(ref->count->free_func) { + ref->count->free_func(ref->data); + } + free(ref->data); + } + if(ref->count->weak == 0) { + free(ref->count); + } +} + +void libab_ref_weaken(libab_ref* ref) { + if(ref->strong) { + ref->count->strong--; + ref->strong = 0; + _libab_ref_changed(ref); + } +} + +void libab_ref_free(libab_ref* ref) { + ref->count->strong -= ref->strong; + ref->count->weak--; + _libab_ref_changed(ref); +} + +void libab_ref_copy(libab_ref* ref, libab_ref* into) { + ref->count->strong++; + ref->count->weak++; + memcpy(into, ref, sizeof(*ref)); +} + +void* libab_ref_get(libab_ref* ref) { + void* to_return = NULL; + if(ref->count->strong > 0) { + to_return = ref->data; + } + return to_return; +}