Add flag to check if currently doing GC
This commit is contained in:
@ -66,6 +66,9 @@ typedef struct RefcountContext {
|
||||
#ifdef REFCOUNT_HAS_THREADS
|
||||
mtx_t so_mtx; //<! Mutex protecting static_objects.
|
||||
mtx_t gr_mtx; //<! Mutex protecting gc_roots.
|
||||
_Atomic bool doing_gc;
|
||||
#else
|
||||
bool doing_gc;
|
||||
#endif
|
||||
} 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);
|
||||
|
||||
/**
|
||||
* 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,
|
||||
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
|
||||
|
@ -26,6 +26,8 @@
|
||||
(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
|
||||
@ -34,11 +36,14 @@
|
||||
# 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;
|
||||
}
|
||||
|
@ -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");
|
||||
|
||||
|
Reference in New Issue
Block a user