diff --git a/include/refcount/refcount.h b/include/refcount/refcount.h index 5ee108a..e2db9c8 100644 --- a/include/refcount/refcount.h +++ b/include/refcount/refcount.h @@ -66,6 +66,9 @@ typedef struct RefcountContext { #ifdef REFCOUNT_HAS_THREADS mtx_t so_mtx; //doing_gc, memory_order_relaxed); +#else + return ctx->doing_gc; +#endif +} + RefcountWeakref *refcount_context_make_weakref(const RefcountContext *ctx, 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. * @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); } +/** + * 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. * @param obj The object to reference diff --git a/src/refcount.c b/src/refcount.c index 0e55c31..51c1912 100644 --- a/src/refcount.c +++ b/src/refcount.c @@ -26,19 +26,24 @@ (atomic_fetch_sub_explicit(obj, 1, memory_order_relaxed)) # define lock_entry_mtx(obj) (lock_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 -# define lock_mutex(m) true -# define unlock_mutex(m) true -# define store_wr_count(obj, c) (*(obj) = (c)) -# define inc_wr_count(obj) ((*(obj))++) -# define dec_wr_count(obj) ((*(obj))--) -# define lock_entry_mtx(obj) true -# define unlock_entry_mtx(obj) true +# define lock_mutex(m) true +# define unlock_mutex(m) true +# define store_wr_count(obj, c) (*(obj) = (c)) +# define inc_wr_count(obj) ((*(obj))++) +# define dec_wr_count(obj) ((*(obj))--) +# define lock_entry_mtx(obj) true +# define unlock_entry_mtx(obj) true +# define store_flag_maybe_atomic(obj, value) (*(obj) = (value)) #endif /** * Default context to use for functions that do not take a context. You must * initialize this before use. + * + * > The default value of this is NULL. */ RefcountContext *refcount_default_context = NULL; @@ -85,6 +90,7 @@ refcount_make_context(size_t entry_offset, ctx->static_objects = NULL; ctx->gc_roots = NULL; + ctx->doing_gc = false; return ctx; } @@ -689,6 +695,7 @@ ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx) { if (!lock_mutex(&ctx->gr_mtx)) { return -1; } + store_flag_maybe_atomic(&ctx->doing_gc, true); ptrdiff_t total_cleared = 0; RefcountList *root = ctx->gc_roots; while (root) { @@ -698,6 +705,7 @@ ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx) { } total_cleared += res; } + store_flag_maybe_atomic(&ctx->doing_gc, false); unlock_mutex(&ctx->gr_mtx); return total_cleared; } diff --git a/test/test_refcount.c b/test/test_refcount.c index 171e870..00c37ce 100644 --- a/test/test_refcount.c +++ b/test/test_refcount.c @@ -26,7 +26,16 @@ bool held_refs_callback(void *a_raw, RefcountList **out, void *ignored) { 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; counting_free(a->str); counting_free(a); @@ -38,9 +47,13 @@ void reref_destructor(void *a, void *ctx_raw) { } int main(int argc, const char **argv) { - RefcountContext *c = - refcount_make_context(offsetof(A, refcount), held_refs_callback, - destroy_callback, NULL, &COUNTING_ALLOCATOR); + struct ContextAndFlag ctx_and_flag = {.should_be_doing_gc = false}; + + 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 = { .num = 0, @@ -127,7 +140,11 @@ int main(int argc, const char **argv) { assert(refcount_context_garbage_collect(c) == 0); assert(refcount_context_num_refs(c, a_with_destructor) == 1); 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); + ctx_and_flag.should_be_doing_gc = false; + assert(!refcount_context_is_doing_gc(c)); a = make_a(c, 0, "a");