Change to C99 and add weak references
This commit is contained in:
@ -10,8 +10,8 @@
|
||||
* Initial value of #refcount_global_allocator.
|
||||
*/
|
||||
static const RefcountAllocator DEFAULT_ALLOCATOR = {
|
||||
.malloc = malloc,
|
||||
.free = free,
|
||||
.malloc.no_data = malloc,
|
||||
.free.no_data = free,
|
||||
.pass_data = false,
|
||||
.user_data = NULL,
|
||||
};
|
||||
|
122
src/refcount.c
122
src/refcount.c
@ -94,11 +94,12 @@ void refcount_context_destroy(RefcountContext *ctx) {
|
||||
* @param ctx The #RefcountContext
|
||||
* @param obj The object to initialize
|
||||
*/
|
||||
void refcount_context_init_obj(RefcountContext *ctx, void *obj) {
|
||||
void refcount_context_init_obj(const RefcountContext *ctx, void *obj) {
|
||||
if (obj) {
|
||||
ENTRY->is_static = false;
|
||||
ENTRY->gc_root = NULL;
|
||||
ENTRY->ref_count = 0;
|
||||
ENTRY->impl.counted.gc_root = NULL;
|
||||
ENTRY->impl.counted.ref_count = 0;
|
||||
ENTRY->weak_refs = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,7 +116,8 @@ bool refcount_context_init_static(RefcountContext *ctx, void *obj) {
|
||||
if (!ctx->static_objects) {
|
||||
return false;
|
||||
}
|
||||
ENTRY->static_entry = ctx->static_objects;
|
||||
ENTRY->impl.static_entry = ctx->static_objects;
|
||||
ENTRY->weak_refs = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -134,6 +136,27 @@ static inline bool obj_held_refs(RefcountContext *ctx, void *obj,
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call back used from #clear_weak_references.
|
||||
* @param wr_raw The #RefcountWeakref object
|
||||
*/
|
||||
static void clear_weak_reference_callback(void *wr_raw) {
|
||||
RefcountWeakref *wr = wr_raw;
|
||||
wr->entry = NULL;
|
||||
wr->data = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all weak references of an object without freeing them.
|
||||
* @param ctx The #RefcountContext
|
||||
* @param obj The object to clear
|
||||
*/
|
||||
static inline void clear_weak_references(const RefcountContext *ctx,
|
||||
void *obj) {
|
||||
refcount_list_free_full(ENTRY->weak_refs, clear_weak_reference_callback,
|
||||
&ctx->alloc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a static object in a context.
|
||||
* @param ctx The #RefcountContext
|
||||
@ -146,8 +169,9 @@ bool refcount_context_deinit_static(RefcountContext *ctx, void *obj) {
|
||||
if (!obj_held_refs(ctx, obj, &held_refs)) {
|
||||
return false;
|
||||
}
|
||||
clear_weak_references(ctx, obj);
|
||||
ctx->static_objects = refcount_list_remove_full(
|
||||
ctx->static_objects, ENTRY->static_entry, NULL, &ctx->alloc);
|
||||
ctx->static_objects, ENTRY->impl.static_entry, NULL, &ctx->alloc);
|
||||
refcount_list_free_with_data_full(
|
||||
held_refs, refcount_context_unref_as_callback, ctx, &ctx->alloc);
|
||||
return true;
|
||||
@ -163,11 +187,11 @@ bool refcount_context_deinit_static(RefcountContext *ctx, void *obj) {
|
||||
* @param obj The object to reference
|
||||
* @return The input object
|
||||
*/
|
||||
void *refcount_context_ref(RefcountContext *ctx, void *obj) {
|
||||
void *refcount_context_ref(const RefcountContext *ctx, void *obj) {
|
||||
if (!obj) {
|
||||
return NULL;
|
||||
} else if (!ENTRY->is_static) {
|
||||
++ENTRY->ref_count;
|
||||
++ENTRY->impl.counted.ref_count;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
@ -180,13 +204,13 @@ void *refcount_context_ref(RefcountContext *ctx, void *obj) {
|
||||
* @return True on success
|
||||
*/
|
||||
static bool track_gc_root(RefcountContext *ctx, void *obj) {
|
||||
if (!ENTRY->gc_root) {
|
||||
if (!ENTRY->impl.counted.gc_root) {
|
||||
ctx->gc_roots =
|
||||
refcount_list_push_full(ctx->gc_roots, obj, &ctx->alloc);
|
||||
if (!ctx->gc_roots) {
|
||||
return false;
|
||||
}
|
||||
ENTRY->gc_root = ctx->gc_roots;
|
||||
ENTRY->impl.counted.gc_root = ctx->gc_roots;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -198,9 +222,9 @@ static bool track_gc_root(RefcountContext *ctx, void *obj) {
|
||||
* @param obj The object to untrack
|
||||
*/
|
||||
static void remove_gc_root(RefcountContext *ctx, void *obj) {
|
||||
ctx->gc_roots = refcount_list_remove_full(ctx->gc_roots, ENTRY->gc_root,
|
||||
NULL, &ctx->alloc);
|
||||
ENTRY->gc_root = NULL;
|
||||
ctx->gc_roots = refcount_list_remove_full(
|
||||
ctx->gc_roots, ENTRY->impl.counted.gc_root, NULL, &ctx->alloc);
|
||||
ENTRY->impl.counted.gc_root = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -222,11 +246,11 @@ static void *unref_to_queue(RefcountContext *ctx, void *obj,
|
||||
return obj;
|
||||
} else if (obj == toplevel) {
|
||||
return NULL;
|
||||
} else if (ENTRY->ref_count <= 1) {
|
||||
} else if (ENTRY->impl.counted.ref_count <= 1) {
|
||||
*queue = refcount_list_push_full(*queue, obj, &ctx->alloc);
|
||||
return NULL;
|
||||
}
|
||||
--ENTRY->ref_count;
|
||||
--ENTRY->impl.counted.ref_count;
|
||||
track_gc_root(ctx, obj);
|
||||
return obj;
|
||||
}
|
||||
@ -258,6 +282,7 @@ static void unref_to_queue_as_callback(void *obj, void *ctx_and_queue_raw) {
|
||||
* @param obj The object to destroy
|
||||
*/
|
||||
static inline void destroy_object(RefcountContext *ctx, void *obj) {
|
||||
clear_weak_references(ctx, obj);
|
||||
remove_gc_root(ctx, obj);
|
||||
if (ctx->destroy_callback) {
|
||||
ctx->destroy_callback(obj, ctx->user_data);
|
||||
@ -326,11 +351,11 @@ void *refcount_context_float(RefcountContext *ctx, void *obj) {
|
||||
return NULL;
|
||||
} else if (ENTRY->is_static) {
|
||||
return obj;
|
||||
} else if (!ENTRY->ref_count) {
|
||||
} else if (!ENTRY->impl.counted.ref_count) {
|
||||
BREAKPOINT(FLOAT, ctx, obj);
|
||||
return obj;
|
||||
}
|
||||
if (--ENTRY->ref_count) {
|
||||
if (--ENTRY->impl.counted.ref_count) {
|
||||
track_gc_root(ctx, obj);
|
||||
} else {
|
||||
remove_gc_root(ctx, obj);
|
||||
@ -371,7 +396,8 @@ static bool free_roots_foreach(void *obj, void *ignored, void *user_data) {
|
||||
}
|
||||
struct ContextAndRootPtr *data = user_data;
|
||||
if (*data->root_ptr
|
||||
&& *data->root_ptr == REFCOUNT_OBJECT_ENTRY(data->ctx, obj)->gc_root) {
|
||||
&& *data->root_ptr
|
||||
== REFCOUNT_OBJECT_ENTRY(data->ctx, obj)->impl.counted.gc_root) {
|
||||
*data->root_ptr = (*data->root_ptr)->next;
|
||||
data->did_update = true;
|
||||
}
|
||||
@ -413,7 +439,7 @@ static ptrdiff_t check_gc_root(RefcountContext *ctx, RefcountList **root_ptr) {
|
||||
if (ht_has(counts, obj)) {
|
||||
count = HT_UUNSTUFF(ht_get(counts, obj));
|
||||
} else {
|
||||
count = REFCOUNT_OBJECT_ENTRY(ctx, obj)->ref_count;
|
||||
count = REFCOUNT_OBJECT_ENTRY(ctx, obj)->impl.counted.ref_count;
|
||||
++seen_objects;
|
||||
}
|
||||
queue = refcount_list_pop_full(queue, NULL, &ctx->alloc);
|
||||
@ -460,3 +486,63 @@ ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx) {
|
||||
}
|
||||
return total_cleared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new weak reference for an object. A weak reference will allow safe
|
||||
* access to the referenced object without holding a reference. That is, the
|
||||
* referenced object can be accessed until it's reference count falls to 0 and
|
||||
* it is freed. After this, attempts to use the weak reference will just return
|
||||
* NULL to indicate that the referenced object is no longer in existence.
|
||||
* @param ctx The #RefcountContext
|
||||
* @param obj The object for which to create a weak reference
|
||||
* @return The newly created weak reference, or NULL if an allocated error
|
||||
* occurred.
|
||||
*/
|
||||
RefcountWeakref *refcount_context_make_weakref(const RefcountContext *ctx,
|
||||
void *obj) {
|
||||
RefcountWeakref *wr = refcount_malloc(&ctx->alloc, sizeof(RefcountWeakref));
|
||||
if (!wr) {
|
||||
return NULL;
|
||||
}
|
||||
RefcountList *new_weak_refs =
|
||||
refcount_list_push_full(ENTRY->weak_refs, wr, &ctx->alloc);
|
||||
if (!new_weak_refs) {
|
||||
refcount_free(&ctx->alloc, wr);
|
||||
return NULL;
|
||||
}
|
||||
ENTRY->weak_refs = new_weak_refs;
|
||||
wr->entry = new_weak_refs;
|
||||
wr->data = obj;
|
||||
return wr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a weak reference. This has no effect on the reference count of the
|
||||
* original object.
|
||||
* @param ctx The #RefcountContext
|
||||
* @param wr The weak reference
|
||||
*/
|
||||
void refcount_context_destroy_weakref(const RefcountContext *ctx,
|
||||
RefcountWeakref *wr) {
|
||||
if (wr->data) {
|
||||
void *obj = wr->data;
|
||||
ENTRY->weak_refs = refcount_list_remove_full(
|
||||
ENTRY->weak_refs, wr->entry, NULL, &ctx->alloc);
|
||||
}
|
||||
refcount_free(&ctx->alloc, wr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a reference to an object referenced by a weak reference and return the
|
||||
* object. If the referenced object no longer exists, return NULL.
|
||||
* @param ctx The #RefcountContext
|
||||
* @param wr The weak reference
|
||||
* @return The newly referenced object, or NULL
|
||||
*/
|
||||
void *refcount_context_ref_weakref(const RefcountContext *ctx,
|
||||
const RefcountWeakref *wr) {
|
||||
if (!wr->data) {
|
||||
return NULL;
|
||||
}
|
||||
return refcount_context_ref(ctx, wr->data);
|
||||
}
|
||||
|
Reference in New Issue
Block a user