diff --git a/include/refcount/refcount.h b/include/refcount/refcount.h index 0be0fb3..5ee108a 100644 --- a/include/refcount/refcount.h +++ b/include/refcount/refcount.h @@ -192,7 +192,6 @@ void *refcount_context_unref(RefcountContext *ctx, void *obj); static inline void refcount_context_unref_as_callback(void *obj, void *ctx) { refcount_context_unref(ctx, obj); } -void *refcount_context_float(RefcountContext *ctx, void *obj); ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx); @@ -320,15 +319,6 @@ static inline void refcount_unref_as_callback(void *obj) { refcount_unref(obj); } -/** - * Same as #refcount_context_float, but only operates on the global context. - * @param obj The object - * @return The input object - */ -static inline void *refcount_float(void *obj) { - return refcount_context_float(refcount_default_context, obj); -} - /** * Same as #refcount_context_garbage_collect, but only operates on the globa * context. diff --git a/src/refcount.c b/src/refcount.c index 212744f..0e55c31 100644 --- a/src/refcount.c +++ b/src/refcount.c @@ -170,7 +170,9 @@ static bool init_obj_destructor_table(const RefcountContext *ctx, void *obj) { } /** - * Initialize the #RefcountEntry member of an object. + * Initialize the #RefcountEntry member of an object. After this call, the + * object will have a reference count of 1. Note that it is not safe to call + * this multiple times on the same object. * @param ctx The #RefcountContext * @param obj The object to initialize * @return True on success, false on failure. Note that you don't have to do @@ -182,7 +184,7 @@ bool refcount_context_init_obj(const RefcountContext *ctx, void *obj) { if (obj) { ENTRY->is_static = false; ENTRY->impl.counted.gc_root = NULL; - ENTRY->impl.counted.ref_count = 0; + ENTRY->impl.counted.ref_count = 1; if (!init_obj_destructor_table(ctx, obj)) { return false; } @@ -394,19 +396,14 @@ static void remove_gc_root(RefcountContext *ctx, void *obj) { * @param ctx The #RefcountContext * @param obj The object to unref * @param queue A double pointer to a #RefcountList acting as a queue - * @param toplevel The toplevel object that triggered this unref. It is ignored - * so that it is not freed twice in the specific case that an object with a - * floating ref and a reference cycle is freed * @return NULL if the reference count fell to 0, the given object otherwise */ static void *unref_to_queue(RefcountContext *ctx, void *obj, - RefcountList **queue, void *toplevel) { + RefcountList **queue) { if (!obj) { return NULL; } else if (ENTRY->is_static) { return obj; - } else if (obj == toplevel) { - return NULL; } if (!lock_entry_mtx(ENTRY)) { // if this fails, we prefer a memory leak to causing undefined behavior @@ -437,7 +434,6 @@ static void *unref_to_queue(RefcountContext *ctx, void *obj, */ struct ContextAndQueue { RefcountContext *ctx; //!< The context. - void *toplevel; //!< Toplevel object that triggered the unref. RefcountList **queue; //!< The queue. }; @@ -448,8 +444,7 @@ struct ContextAndQueue { */ static void unref_to_queue_as_callback(void *obj, void *ctx_and_queue_raw) { struct ContextAndQueue *ctx_and_queue = ctx_and_queue_raw; - unref_to_queue(ctx_and_queue->ctx, obj, ctx_and_queue->queue, - ctx_and_queue->toplevel); + unref_to_queue(ctx_and_queue->ctx, obj, ctx_and_queue->queue); } /** @@ -479,8 +474,7 @@ static inline void destroy_object(RefcountContext *ctx, void *obj) { */ static void process_unref_queue(RefcountContext *ctx, RefcountList *queue, void *toplevel) { - struct ContextAndQueue ctx_and_queue = { - .ctx = ctx, .queue = &queue, .toplevel = toplevel}; + struct ContextAndQueue ctx_and_queue = {.ctx = ctx, .queue = &queue}; while (queue) { void *cur = refcount_list_peek(queue); RefcountList *held_refs = NULL; @@ -514,38 +508,11 @@ void *refcount_context_unref(RefcountContext *ctx, void *obj) { return NULL; } RefcountList *queue = NULL; - void *retval = unref_to_queue(ctx, obj, &queue, NULL); + void *retval = unref_to_queue(ctx, obj, &queue); process_unref_queue(ctx, queue, obj); return retval; } -/** - * Float a reference of an object. That is, decrement it's refrerence count, but - * don't free it if the count drops to 0. It is safe to call this on a static - * object. It is an error to call this on an object that already has a floating - * reference. - * @param ctx The #RefcountContext - * @param obj The object - * @return The input object - */ -void *refcount_context_float(RefcountContext *ctx, void *obj) { - if (!obj) { - return NULL; - } else if (ENTRY->is_static) { - return obj; - } - if (!lock_entry_mtx(ENTRY)) { - return NULL; - } - if (--ENTRY->impl.counted.ref_count) { - track_gc_root(ctx, obj); - } else { - remove_gc_root(ctx, obj); - } - unlock_entry_mtx(ENTRY); - return obj; -} - /** * Set of hash table functions used in #check_gc_root. */ diff --git a/test/test_refcount.c b/test/test_refcount.c index fbd4b42..171e870 100644 --- a/test/test_refcount.c +++ b/test/test_refcount.c @@ -51,48 +51,45 @@ int main(int argc, const char **argv) { A *a = make_a(c, 10, "Hello world\n"); assert(!refcount_context_is_static(c, a)); - assert(refcount_context_num_refs(c, a) == 0); - - a = refcount_context_ref(c, a); assert(refcount_context_num_refs(c, a) == 1); a = refcount_context_ref(c, a); assert(refcount_context_num_refs(c, a) == 2); + a = refcount_context_ref(c, a); + assert(refcount_context_num_refs(c, a) == 3); + a = refcount_context_unref(c, a); - assert(refcount_context_num_refs(c, a) == 1); + assert(refcount_context_num_refs(c, a) == 2); - a = refcount_context_float(c, a); - assert(a); - assert(refcount_context_num_refs(c, a) == 0); - - refcount_context_ref(c, a); + a = refcount_context_unref(c, a); assert(refcount_context_num_refs(c, a) == 1); a = refcount_context_unref(c, a); assert(!a); a = make_a(c, 10, "Hello World\n"); - A *b = make_a(c, 42, "The answer!"); - a->next = refcount_context_ref(c, b); + a->next = make_a(c, 42, "The answer!"); assert(refcount_context_num_refs(c, a->next) == 1); - assert(refcount_context_num_refs(c, a) == 0); + assert(refcount_context_num_refs(c, a) == 1); refcount_context_unref(c, a); a = make_a(c, 'a', "a"); a->next = refcount_context_ref(c, a); - assert(refcount_context_num_refs(c, a) == 1); + assert(refcount_context_num_refs(c, a) == 2); refcount_context_unref(c, a); + assert(refcount_context_garbage_collect(c) == 1); + a = NULL; for (char i = 'a'; i <= 'z'; ++i) { A *na = make_a(c, i, ""); - na->next = refcount_context_ref(c, a); + na->next = a; a = na; } - assert(refcount_context_num_refs(c, a) == 0); + assert(refcount_context_num_refs(c, a) == 1); refcount_context_unref(c, a); @@ -103,11 +100,11 @@ int main(int argc, const char **argv) { if (!first) { first = na; } - na->next = refcount_context_ref(c, a); + na->next = a; a = na; } - assert(refcount_context_num_refs(c, a) == 0); - first->next = refcount_context_ref(c, a); + assert(refcount_context_num_refs(c, a) == 1); + first->next = a; a = first; assert(refcount_context_num_refs(c, a) == 1); @@ -121,10 +118,10 @@ int main(int argc, const char **argv) { a = NULL; for (char i = 'a'; i <= 'z'; ++i) { A *na = make_a(c, i, ""); - na->next = refcount_context_ref(c, a); + na->next = a; a = na; } - assert(refcount_context_num_refs(c, a) == 0); + assert(refcount_context_num_refs(c, a) == 1); refcount_context_unref(c, a); assert(refcount_context_garbage_collect(c) == 0); @@ -136,14 +133,11 @@ int main(int argc, const char **argv) { RefcountWeakref *w = refcount_context_make_weakref(c, a); assert(w); - assert(refcount_context_num_refs(c, a) == 0); - - b = refcount_context_strengthen(c, w); - assert(b); - assert(a == b); assert(refcount_context_num_refs(c, a) == 1); - refcount_context_ref(c, a); + A *b = refcount_context_strengthen(c, w); + assert(b); + assert(a == b); assert(refcount_context_num_refs(c, a) == 2); RefcountWeakref *x = refcount_context_weaken(c, a); @@ -167,9 +161,9 @@ int main(int argc, const char **argv) { refcount_context_destroy_weakref(c, x); a = make_a(c, 10, "test destructor"); - assert(refcount_context_num_refs(c, a) == 0); + assert(refcount_context_num_refs(c, a) == 1); assert(refcount_context_add_destructor(c, a, &key, reref_destructor, c)); - assert(refcount_context_num_refs(c, a) == 0); + assert(refcount_context_num_refs(c, a) == 1); assert(!refcount_context_unref(c, a)); assert(refcount_context_num_refs(c, a) == 1); assert(refcount_context_remove_destructor(c, a, &key));