Update documentation

This commit is contained in:
2025-08-31 00:34:08 -07:00
parent d78cf29765
commit 4efdcc97ae
4 changed files with 89 additions and 33 deletions

View File

@ -25,7 +25,7 @@ const RefcountAllocator *refcount_global_allocator = &DEFAULT_ALLOCATOR;
/**
* HTAllocator malloc function that delegates to #refcount_malloc.
* @param size The number of bytes to allocate
* @parma alloc The #RefcountAllocator to use
* @param alloc The #RefcountAllocator to use
* @return The result of #refcount_malloc
*/
static void *refcount_ht_malloc(size_t size, void *alloc) {
@ -35,7 +35,7 @@ static void *refcount_ht_malloc(size_t size, void *alloc) {
/**
* HTAllocator free function that delegates to #refcount_free.
* @param ptr The pointer to free
* @parma alloc The #RefcountAllocator to use
* @param alloc The #RefcountAllocator to use
*/
static void refcount_ht_free(void *ptr, void *alloc) {
refcount_free(alloc, ptr);
@ -44,7 +44,7 @@ static void refcount_ht_free(void *ptr, void *alloc) {
/**
* Create a new HTAllocator that delegates to src.
* @param src The #RefcountAllocator to delegate to
* @param dest A pointer to a #HTAllocator to initialize
* @param dest A pointer to a HTAllocator to initialize
*/
void refcount_allocator_to_ht_allocator(const RefcountAllocator *src,
HTAllocator *dest) {

View File

@ -40,6 +40,8 @@ RefcountContext *refcount_default_context = NULL;
* object
* @param destroy_callback A function to be called when an objects reference
* count drops to zero
* @param user_data Extra data to pass to the callbacks
* @param alloc The #RefcountAllocator to use for all internal allocations
* @return The new context, or NULL in the case of an error
*/
RefcountContext *
@ -214,7 +216,9 @@ static void remove_gc_root(RefcountContext *ctx, void *obj) {
*/
static void *unref_to_queue(RefcountContext *ctx, void *obj,
RefcountList **queue, void *toplevel) {
if (ENTRY->is_static) {
if (!obj) {
return NULL;
} else if (ENTRY->is_static) {
return obj;
} else if (obj == toplevel) {
return NULL;
@ -335,7 +339,7 @@ void *refcount_context_float(RefcountContext *ctx, void *obj) {
}
/**
* Set of hash table functions used in #gc_check_root.
* Set of hash table functions used in #check_gc_root.
*/
static const HTTableFunctions ROOT_COUNTS_FNS = {
.hash = ht_intptr_hash_callback,
@ -345,15 +349,29 @@ static const HTTableFunctions ROOT_COUNTS_FNS = {
.user_data = NULL,
};
/**
* Holds data for #free_roots_foreach, used in #check_gc_root.
*/
struct ContextAndRootPtr {
RefcountContext *ctx;
RefcountList **root_ptr;
bool did_update;
RefcountContext *ctx; //!< The context.
RefcountList **root_ptr; //!< Double pointer to the root.
bool did_update; //!< Weather or not *root_ptr was changed.
};
/**
* Foreach function to free roots from a hash table. Used in #check_gc_root.
* @param obj The object to free
* @param ignored Ignored
* @param user_data A #ContextAndRootPtr
* @return Always false
*/
static bool free_roots_foreach(void *obj, void *ignored, void *user_data) {
if (!obj) {
return false;
}
struct ContextAndRootPtr *data = user_data;
if (*data->root_ptr == REFCOUNT_OBJECT_ENTRY(data->ctx, obj)->gc_root) {
if (*data->root_ptr
&& *data->root_ptr == REFCOUNT_OBJECT_ENTRY(data->ctx, obj)->gc_root) {
*data->root_ptr = (*data->root_ptr)->next;
data->did_update = true;
}
@ -366,28 +384,30 @@ static bool free_roots_foreach(void *obj, void *ignored, void *user_data) {
* root_ptr is set to the next root to be checked.
* @param ctx The context
* @param root_ptr Double pointer to one of the GC roots
* @return True on success
* @return The number of object's freed, or -1 if an error happened
*/
static bool check_gc_root(RefcountContext *ctx, RefcountList **root_ptr) {
static ptrdiff_t check_gc_root(RefcountContext *ctx, RefcountList **root_ptr) {
HTTable *counts = ht_new(&ROOT_COUNTS_FNS, &ctx->ht_alloc, NULL);
if (!counts) {
*root_ptr = (*root_ptr)->next;
return false;
return -1;
}
RefcountList *root = *root_ptr;
RefcountList *queue = NULL;
if (!obj_held_refs(ctx, root->data, &queue)) {
ht_free(counts);
*root_ptr = (*root_ptr)->next;
return false;
return -1;
}
size_t seen_objects = 0;
size_t clear_objects = 0;
// ignore allocation errors until I decide how to deal with them (never)
// ignore allocation errors until I decide how to deal with them (in the far
// future)
while (queue) {
void *obj = queue->data;
if (!obj || refcount_context_is_static(ctx, obj)) {
goto next;
queue = refcount_list_pop_full(queue, NULL, &ctx->alloc);
continue;
}
uintptr_t count;
if (ht_has(counts, obj)) {
@ -396,15 +416,14 @@ static bool check_gc_root(RefcountContext *ctx, RefcountList **root_ptr) {
count = REFCOUNT_OBJECT_ENTRY(ctx, obj)->ref_count;
++seen_objects;
}
queue = refcount_list_pop_full(queue, NULL, &ctx->alloc);
if (count > 0) {
ht_insert(counts, obj, HT_STUFF(--count));
if (count == 0) {
++clear_objects;
}
obj_held_refs(ctx, root->data, &queue);
obj_held_refs(ctx, obj, &queue);
}
next:
queue = refcount_list_pop_full(queue, NULL, &ctx->alloc);
}
if (seen_objects == clear_objects) {
struct ContextAndRootPtr data = {
@ -417,24 +436,27 @@ static bool check_gc_root(RefcountContext *ctx, RefcountList **root_ptr) {
*root_ptr = (*root_ptr)->next;
}
ht_free(counts);
return true;
return seen_objects == clear_objects ? seen_objects : 0;
}
/**
* Run the garbage collector on a context.
* @param cts The #RefcountContext
* @return False if an error occurred, true otherwise
* @param ctx The #RefcountContext
* @return The number of object's freed, or -1 if an error occurred
*/
bool refcount_context_garbage_collect(RefcountContext *ctx) {
ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx) {
if (!ctx->held_refs_callback) {
// no loops possible
return true;
return 0;
}
ptrdiff_t total_cleared = 0;
RefcountList *root = ctx->gc_roots;
while (root) {
if (!check_gc_root(ctx, &root)) {
return false;
ptrdiff_t res = check_gc_root(ctx, &root);
if (res < 0) {
return -1;
}
total_cleared += res;
}
return true;
return total_cleared;
}