Add flag to check if currently doing GC
This commit is contained in:
		| @ -66,6 +66,9 @@ typedef struct RefcountContext { | |||||||
| #ifdef REFCOUNT_HAS_THREADS | #ifdef REFCOUNT_HAS_THREADS | ||||||
|     mtx_t so_mtx; //<! Mutex protecting static_objects. |     mtx_t so_mtx; //<! Mutex protecting static_objects. | ||||||
|     mtx_t gr_mtx; //<! Mutex protecting gc_roots. |     mtx_t gr_mtx; //<! Mutex protecting gc_roots. | ||||||
|  |     _Atomic bool doing_gc; | ||||||
|  | #else | ||||||
|  |     bool doing_gc; | ||||||
| #endif | #endif | ||||||
| } RefcountContext; | } RefcountContext; | ||||||
|  |  | ||||||
| @ -195,6 +198,20 @@ static inline void refcount_context_unref_as_callback(void *obj, void *ctx) { | |||||||
|  |  | ||||||
| ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx); | ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Test weather a give context is currently performing a GC operation. | ||||||
|  |  * @param ctx The #RefcountContext | ||||||
|  |  * @return True if the context is currently doing a GC operation, false | ||||||
|  |  * otherwise | ||||||
|  |  */ | ||||||
|  | static bool refcount_context_is_doing_gc(const RefcountContext *ctx) { | ||||||
|  | #ifdef REFCOUNT_HAS_THREADS | ||||||
|  |     return atomic_load_explicit(&ctx->doing_gc, memory_order_relaxed); | ||||||
|  | #else | ||||||
|  |     return ctx->doing_gc; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
| RefcountWeakref *refcount_context_make_weakref(const RefcountContext *ctx, | RefcountWeakref *refcount_context_make_weakref(const RefcountContext *ctx, | ||||||
|                                                void *obj); |                                                void *obj); | ||||||
|  |  | ||||||
| @ -320,7 +337,7 @@ static inline void refcount_unref_as_callback(void *obj) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Same as #refcount_context_garbage_collect, but only operates on the globa |  * Same as #refcount_context_garbage_collect, but only operates on the global | ||||||
|  * context. |  * context. | ||||||
|  * @return The number of object's freed, or -1 if an error occurred |  * @return The number of object's freed, or -1 if an error occurred | ||||||
|  */ |  */ | ||||||
| @ -328,6 +345,16 @@ static inline ptrdiff_t refcount_garbage_collect(void) { | |||||||
|     return refcount_context_garbage_collect(refcount_default_context); |     return refcount_context_garbage_collect(refcount_default_context); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Same as #refcount_context_is_doing_gc, but only operates on the global | ||||||
|  |  * context. | ||||||
|  |  * @return True if the context is currently doing a GC operation, false | ||||||
|  |  * otherwise | ||||||
|  |  */ | ||||||
|  | static inline bool refcount_is_doing_gc(void) { | ||||||
|  |     return refcount_context_is_doing_gc(refcount_default_context); | ||||||
|  | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Same as #refcount_context_make_weakref, but operates on the global context. |  * Same as #refcount_context_make_weakref, but operates on the global context. | ||||||
|  * @param obj The object to reference |  * @param obj The object to reference | ||||||
|  | |||||||
| @ -26,19 +26,24 @@ | |||||||
|         (atomic_fetch_sub_explicit(obj, 1, memory_order_relaxed)) |         (atomic_fetch_sub_explicit(obj, 1, memory_order_relaxed)) | ||||||
| #    define lock_entry_mtx(obj)   (lock_mutex(&obj->weak_ref->mtx)) | #    define lock_entry_mtx(obj)   (lock_mutex(&obj->weak_ref->mtx)) | ||||||
| #    define unlock_entry_mtx(obj) (unlock_mutex(&obj->weak_ref->mtx)) | #    define unlock_entry_mtx(obj) (unlock_mutex(&obj->weak_ref->mtx)) | ||||||
|  | #    define store_flag_maybe_atomic(obj, value) \ | ||||||
|  |         (atomic_store_explicit(obj, value, memory_order_relaxed)) | ||||||
| #else | #else | ||||||
| #    define lock_mutex(m)          true | #    define lock_mutex(m)                       true | ||||||
| #    define unlock_mutex(m)        true | #    define unlock_mutex(m)                     true | ||||||
| #    define store_wr_count(obj, c) (*(obj) = (c)) | #    define store_wr_count(obj, c)              (*(obj) = (c)) | ||||||
| #    define inc_wr_count(obj)      ((*(obj))++) | #    define inc_wr_count(obj)                   ((*(obj))++) | ||||||
| #    define dec_wr_count(obj)      ((*(obj))--) | #    define dec_wr_count(obj)                   ((*(obj))--) | ||||||
| #    define lock_entry_mtx(obj)    true | #    define lock_entry_mtx(obj)                 true | ||||||
| #    define unlock_entry_mtx(obj)  true | #    define unlock_entry_mtx(obj)               true | ||||||
|  | #    define store_flag_maybe_atomic(obj, value) (*(obj) = (value)) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Default context to use for functions that do not take a context. You must |  * Default context to use for functions that do not take a context. You must | ||||||
|  * initialize this before use. |  * initialize this before use. | ||||||
|  |  * | ||||||
|  |  * > The default value of this is NULL. | ||||||
|  */ |  */ | ||||||
| RefcountContext *refcount_default_context = NULL; | RefcountContext *refcount_default_context = NULL; | ||||||
|  |  | ||||||
| @ -85,6 +90,7 @@ refcount_make_context(size_t entry_offset, | |||||||
|  |  | ||||||
|     ctx->static_objects = NULL; |     ctx->static_objects = NULL; | ||||||
|     ctx->gc_roots = NULL; |     ctx->gc_roots = NULL; | ||||||
|  |     ctx->doing_gc = false; | ||||||
|     return ctx; |     return ctx; | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -689,6 +695,7 @@ ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx) { | |||||||
|     if (!lock_mutex(&ctx->gr_mtx)) { |     if (!lock_mutex(&ctx->gr_mtx)) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |     store_flag_maybe_atomic(&ctx->doing_gc, true); | ||||||
|     ptrdiff_t total_cleared = 0; |     ptrdiff_t total_cleared = 0; | ||||||
|     RefcountList *root = ctx->gc_roots; |     RefcountList *root = ctx->gc_roots; | ||||||
|     while (root) { |     while (root) { | ||||||
| @ -698,6 +705,7 @@ ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx) { | |||||||
|         } |         } | ||||||
|         total_cleared += res; |         total_cleared += res; | ||||||
|     } |     } | ||||||
|  |     store_flag_maybe_atomic(&ctx->doing_gc, false); | ||||||
|     unlock_mutex(&ctx->gr_mtx); |     unlock_mutex(&ctx->gr_mtx); | ||||||
|     return total_cleared; |     return total_cleared; | ||||||
| } | } | ||||||
|  | |||||||
| @ -26,7 +26,16 @@ bool held_refs_callback(void *a_raw, RefcountList **out, void *ignored) { | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void destroy_callback(void *a_raw, void *ignored) { | struct ContextAndFlag { | ||||||
|  |     const RefcountContext *ctx; | ||||||
|  |     bool should_be_doing_gc; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | void destroy_callback(void *a_raw, void *ctx_and_flag_raw) { | ||||||
|  |     struct ContextAndFlag *ctx_and_flag = ctx_and_flag_raw; | ||||||
|  |     if (ctx_and_flag->should_be_doing_gc) { | ||||||
|  |         assert(refcount_context_is_doing_gc(ctx_and_flag->ctx)); | ||||||
|  |     } | ||||||
|     A *a = a_raw; |     A *a = a_raw; | ||||||
|     counting_free(a->str); |     counting_free(a->str); | ||||||
|     counting_free(a); |     counting_free(a); | ||||||
| @ -38,9 +47,13 @@ void reref_destructor(void *a, void *ctx_raw) { | |||||||
| } | } | ||||||
|  |  | ||||||
| int main(int argc, const char **argv) { | int main(int argc, const char **argv) { | ||||||
|     RefcountContext *c = |     struct ContextAndFlag ctx_and_flag = {.should_be_doing_gc = false}; | ||||||
|         refcount_make_context(offsetof(A, refcount), held_refs_callback, |  | ||||||
|                               destroy_callback, NULL, &COUNTING_ALLOCATOR); |     RefcountContext *c = refcount_make_context( | ||||||
|  |         offsetof(A, refcount), held_refs_callback, destroy_callback, | ||||||
|  |         &ctx_and_flag, &COUNTING_ALLOCATOR); | ||||||
|  |  | ||||||
|  |     ctx_and_flag.ctx = c; | ||||||
|  |  | ||||||
|     A static_a = { |     A static_a = { | ||||||
|         .num = 0, |         .num = 0, | ||||||
| @ -127,7 +140,11 @@ int main(int argc, const char **argv) { | |||||||
|     assert(refcount_context_garbage_collect(c) == 0); |     assert(refcount_context_garbage_collect(c) == 0); | ||||||
|     assert(refcount_context_num_refs(c, a_with_destructor) == 1); |     assert(refcount_context_num_refs(c, a_with_destructor) == 1); | ||||||
|     assert(refcount_context_remove_destructor(c, a_with_destructor, &key)); |     assert(refcount_context_remove_destructor(c, a_with_destructor, &key)); | ||||||
|  |     assert(!refcount_context_is_doing_gc(c)); | ||||||
|  |     ctx_and_flag.should_be_doing_gc = true; | ||||||
|     assert(refcount_context_garbage_collect(c) == 26); |     assert(refcount_context_garbage_collect(c) == 26); | ||||||
|  |     ctx_and_flag.should_be_doing_gc = false; | ||||||
|  |     assert(!refcount_context_is_doing_gc(c)); | ||||||
|  |  | ||||||
|     a = make_a(c, 0, "a"); |     a = make_a(c, 0, "a"); | ||||||
|  |  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user