Remove floating refs
This commit is contained in:
		| @ -192,7 +192,6 @@ void *refcount_context_unref(RefcountContext *ctx, void *obj); | |||||||
| static inline void refcount_context_unref_as_callback(void *obj, void *ctx) { | static inline void refcount_context_unref_as_callback(void *obj, void *ctx) { | ||||||
|     refcount_context_unref(ctx, obj); |     refcount_context_unref(ctx, obj); | ||||||
| } | } | ||||||
| void *refcount_context_float(RefcountContext *ctx, void *obj); |  | ||||||
|  |  | ||||||
| ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx); | ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx); | ||||||
|  |  | ||||||
| @ -320,15 +319,6 @@ static inline void refcount_unref_as_callback(void *obj) { | |||||||
|     refcount_unref(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 |  * Same as #refcount_context_garbage_collect, but only operates on the globa | ||||||
|  * context. |  * context. | ||||||
|  | |||||||
| @ -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 ctx The #RefcountContext | ||||||
|  * @param obj The object to initialize |  * @param obj The object to initialize | ||||||
|  * @return True on success, false on failure. Note that you don't have to do |  * @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) { |     if (obj) { | ||||||
|         ENTRY->is_static = false; |         ENTRY->is_static = false; | ||||||
|         ENTRY->impl.counted.gc_root = NULL; |         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)) { |         if (!init_obj_destructor_table(ctx, obj)) { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
| @ -394,19 +396,14 @@ static void remove_gc_root(RefcountContext *ctx, void *obj) { | |||||||
|  * @param ctx The #RefcountContext |  * @param ctx The #RefcountContext | ||||||
|  * @param obj The object to unref |  * @param obj The object to unref | ||||||
|  * @param queue A double pointer to a #RefcountList acting as a queue |  * @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 |  * @return NULL if the reference count fell to 0, the given object otherwise | ||||||
|  */ |  */ | ||||||
| static void *unref_to_queue(RefcountContext *ctx, void *obj, | static void *unref_to_queue(RefcountContext *ctx, void *obj, | ||||||
|                             RefcountList **queue, void *toplevel) { |                             RefcountList **queue) { | ||||||
|     if (!obj) { |     if (!obj) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } else if (ENTRY->is_static) { |     } else if (ENTRY->is_static) { | ||||||
|         return obj; |         return obj; | ||||||
|     } else if (obj == toplevel) { |  | ||||||
|         return NULL; |  | ||||||
|     } |     } | ||||||
|     if (!lock_entry_mtx(ENTRY)) { |     if (!lock_entry_mtx(ENTRY)) { | ||||||
|         // if this fails, we prefer a memory leak to causing undefined behavior |         // 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 { | struct ContextAndQueue { | ||||||
|     RefcountContext *ctx; //!< The context. |     RefcountContext *ctx; //!< The context. | ||||||
|     void *toplevel; //!< Toplevel object that triggered the unref. |  | ||||||
|     RefcountList **queue; //!< The queue. |     RefcountList **queue; //!< The queue. | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @ -448,8 +444,7 @@ struct ContextAndQueue { | |||||||
|  */ |  */ | ||||||
| static void unref_to_queue_as_callback(void *obj, void *ctx_and_queue_raw) { | static void unref_to_queue_as_callback(void *obj, void *ctx_and_queue_raw) { | ||||||
|     struct ContextAndQueue *ctx_and_queue = 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, |     unref_to_queue(ctx_and_queue->ctx, obj, ctx_and_queue->queue); | ||||||
|                    ctx_and_queue->toplevel); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @ -479,8 +474,7 @@ static inline void destroy_object(RefcountContext *ctx, void *obj) { | |||||||
|  */ |  */ | ||||||
| static void process_unref_queue(RefcountContext *ctx, RefcountList *queue, | static void process_unref_queue(RefcountContext *ctx, RefcountList *queue, | ||||||
|                                 void *toplevel) { |                                 void *toplevel) { | ||||||
|     struct ContextAndQueue ctx_and_queue = { |     struct ContextAndQueue ctx_and_queue = {.ctx = ctx, .queue = &queue}; | ||||||
|         .ctx = ctx, .queue = &queue, .toplevel = toplevel}; |  | ||||||
|     while (queue) { |     while (queue) { | ||||||
|         void *cur = refcount_list_peek(queue); |         void *cur = refcount_list_peek(queue); | ||||||
|         RefcountList *held_refs = NULL; |         RefcountList *held_refs = NULL; | ||||||
| @ -514,38 +508,11 @@ void *refcount_context_unref(RefcountContext *ctx, void *obj) { | |||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     RefcountList *queue = 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); |     process_unref_queue(ctx, queue, obj); | ||||||
|     return retval; |     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. |  * Set of hash table functions used in #check_gc_root. | ||||||
|  */ |  */ | ||||||
|  | |||||||
| @ -51,48 +51,45 @@ int main(int argc, const char **argv) { | |||||||
|  |  | ||||||
|     A *a = make_a(c, 10, "Hello world\n"); |     A *a = make_a(c, 10, "Hello world\n"); | ||||||
|     assert(!refcount_context_is_static(c, a)); |     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); |     assert(refcount_context_num_refs(c, a) == 1); | ||||||
|  |  | ||||||
|     a = refcount_context_ref(c, a); |     a = refcount_context_ref(c, a); | ||||||
|     assert(refcount_context_num_refs(c, a) == 2); |     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); |     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); |     a = refcount_context_unref(c, a); | ||||||
|     assert(a); |  | ||||||
|     assert(refcount_context_num_refs(c, a) == 0); |  | ||||||
|  |  | ||||||
|     refcount_context_ref(c, a); |  | ||||||
|     assert(refcount_context_num_refs(c, a) == 1); |     assert(refcount_context_num_refs(c, a) == 1); | ||||||
|  |  | ||||||
|     a = refcount_context_unref(c, a); |     a = refcount_context_unref(c, a); | ||||||
|     assert(!a); |     assert(!a); | ||||||
|  |  | ||||||
|     a = make_a(c, 10, "Hello World\n"); |     a = make_a(c, 10, "Hello World\n"); | ||||||
|     A *b = make_a(c, 42, "The answer!"); |     a->next = make_a(c, 42, "The answer!"); | ||||||
|     a->next = refcount_context_ref(c, b); |  | ||||||
|     assert(refcount_context_num_refs(c, a->next) == 1); |     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); |     refcount_context_unref(c, a); | ||||||
|  |  | ||||||
|     a = make_a(c, 'a', "a"); |     a = make_a(c, 'a', "a"); | ||||||
|     a->next = refcount_context_ref(c, 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); |     refcount_context_unref(c, a); | ||||||
|  |  | ||||||
|  |     assert(refcount_context_garbage_collect(c) == 1); | ||||||
|  |  | ||||||
|     a = NULL; |     a = NULL; | ||||||
|     for (char i = 'a'; i <= 'z'; ++i) { |     for (char i = 'a'; i <= 'z'; ++i) { | ||||||
|         A *na = make_a(c, i, ""); |         A *na = make_a(c, i, ""); | ||||||
|         na->next = refcount_context_ref(c, a); |         na->next = a; | ||||||
|         a = na; |         a = na; | ||||||
|     } |     } | ||||||
|     assert(refcount_context_num_refs(c, a) == 0); |     assert(refcount_context_num_refs(c, a) == 1); | ||||||
|  |  | ||||||
|     refcount_context_unref(c, a); |     refcount_context_unref(c, a); | ||||||
|  |  | ||||||
| @ -103,11 +100,11 @@ int main(int argc, const char **argv) { | |||||||
|         if (!first) { |         if (!first) { | ||||||
|             first = na; |             first = na; | ||||||
|         } |         } | ||||||
|         na->next = refcount_context_ref(c, a); |         na->next = a; | ||||||
|         a = na; |         a = na; | ||||||
|     } |     } | ||||||
|     assert(refcount_context_num_refs(c, a) == 0); |     assert(refcount_context_num_refs(c, a) == 1); | ||||||
|     first->next = refcount_context_ref(c, a); |     first->next = a; | ||||||
|     a = first; |     a = first; | ||||||
|     assert(refcount_context_num_refs(c, a) == 1); |     assert(refcount_context_num_refs(c, a) == 1); | ||||||
|  |  | ||||||
| @ -121,10 +118,10 @@ int main(int argc, const char **argv) { | |||||||
|     a = NULL; |     a = NULL; | ||||||
|     for (char i = 'a'; i <= 'z'; ++i) { |     for (char i = 'a'; i <= 'z'; ++i) { | ||||||
|         A *na = make_a(c, i, ""); |         A *na = make_a(c, i, ""); | ||||||
|         na->next = refcount_context_ref(c, a); |         na->next = a; | ||||||
|         a = na; |         a = na; | ||||||
|     } |     } | ||||||
|     assert(refcount_context_num_refs(c, a) == 0); |     assert(refcount_context_num_refs(c, a) == 1); | ||||||
|     refcount_context_unref(c, a); |     refcount_context_unref(c, a); | ||||||
|  |  | ||||||
|     assert(refcount_context_garbage_collect(c) == 0); |     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); |     RefcountWeakref *w = refcount_context_make_weakref(c, a); | ||||||
|     assert(w); |     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); |     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); |     assert(refcount_context_num_refs(c, a) == 2); | ||||||
|  |  | ||||||
|     RefcountWeakref *x = refcount_context_weaken(c, a); |     RefcountWeakref *x = refcount_context_weaken(c, a); | ||||||
| @ -167,9 +161,9 @@ int main(int argc, const char **argv) { | |||||||
|     refcount_context_destroy_weakref(c, x); |     refcount_context_destroy_weakref(c, x); | ||||||
|  |  | ||||||
|     a = make_a(c, 10, "test destructor"); |     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_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_unref(c, a)); | ||||||
|     assert(refcount_context_num_refs(c, a) == 1); |     assert(refcount_context_num_refs(c, a) == 1); | ||||||
|     assert(refcount_context_remove_destructor(c, a, &key)); |     assert(refcount_context_remove_destructor(c, a, &key)); | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user