Remove floating refs
This commit is contained in:
@ -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.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user