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) {
|
||||
refcount_context_unref(ctx, obj);
|
||||
}
|
||||
void *refcount_context_float(RefcountContext *ctx, void *obj);
|
||||
|
||||
ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx);
|
||||
|
||||
@ -320,15 +319,6 @@ static inline void refcount_unref_as_callback(void *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
|
||||
* 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 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.
|
||||
*/
|
||||
|
@ -51,48 +51,45 @@ int main(int argc, const char **argv) {
|
||||
|
||||
A *a = make_a(c, 10, "Hello world\n");
|
||||
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);
|
||||
|
||||
a = refcount_context_ref(c, a);
|
||||
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);
|
||||
assert(refcount_context_num_refs(c, a) == 1);
|
||||
assert(refcount_context_num_refs(c, a) == 2);
|
||||
|
||||
a = refcount_context_float(c, a);
|
||||
assert(a);
|
||||
assert(refcount_context_num_refs(c, a) == 0);
|
||||
|
||||
refcount_context_ref(c, a);
|
||||
a = refcount_context_unref(c, a);
|
||||
assert(refcount_context_num_refs(c, a) == 1);
|
||||
|
||||
a = refcount_context_unref(c, a);
|
||||
assert(!a);
|
||||
|
||||
a = make_a(c, 10, "Hello World\n");
|
||||
A *b = make_a(c, 42, "The answer!");
|
||||
a->next = refcount_context_ref(c, b);
|
||||
a->next = make_a(c, 42, "The answer!");
|
||||
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);
|
||||
|
||||
a = make_a(c, 'a', "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);
|
||||
|
||||
assert(refcount_context_garbage_collect(c) == 1);
|
||||
|
||||
a = NULL;
|
||||
for (char i = 'a'; i <= 'z'; ++i) {
|
||||
A *na = make_a(c, i, "");
|
||||
na->next = refcount_context_ref(c, a);
|
||||
na->next = a;
|
||||
a = na;
|
||||
}
|
||||
assert(refcount_context_num_refs(c, a) == 0);
|
||||
assert(refcount_context_num_refs(c, a) == 1);
|
||||
|
||||
refcount_context_unref(c, a);
|
||||
|
||||
@ -103,11 +100,11 @@ int main(int argc, const char **argv) {
|
||||
if (!first) {
|
||||
first = na;
|
||||
}
|
||||
na->next = refcount_context_ref(c, a);
|
||||
na->next = a;
|
||||
a = na;
|
||||
}
|
||||
assert(refcount_context_num_refs(c, a) == 0);
|
||||
first->next = refcount_context_ref(c, a);
|
||||
assert(refcount_context_num_refs(c, a) == 1);
|
||||
first->next = a;
|
||||
a = first;
|
||||
assert(refcount_context_num_refs(c, a) == 1);
|
||||
|
||||
@ -121,10 +118,10 @@ int main(int argc, const char **argv) {
|
||||
a = NULL;
|
||||
for (char i = 'a'; i <= 'z'; ++i) {
|
||||
A *na = make_a(c, i, "");
|
||||
na->next = refcount_context_ref(c, a);
|
||||
na->next = a;
|
||||
a = na;
|
||||
}
|
||||
assert(refcount_context_num_refs(c, a) == 0);
|
||||
assert(refcount_context_num_refs(c, a) == 1);
|
||||
refcount_context_unref(c, a);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
RefcountWeakref *x = refcount_context_weaken(c, a);
|
||||
@ -167,9 +161,9 @@ int main(int argc, const char **argv) {
|
||||
refcount_context_destroy_weakref(c, x);
|
||||
|
||||
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_num_refs(c, a) == 0);
|
||||
assert(refcount_context_num_refs(c, a) == 1);
|
||||
assert(!refcount_context_unref(c, a));
|
||||
assert(refcount_context_num_refs(c, a) == 1);
|
||||
assert(refcount_context_remove_destructor(c, a, &key));
|
||||
|
Reference in New Issue
Block a user