Change to C99 and add weak references
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.11)
|
cmake_minimum_required(VERSION 3.11)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 99)
|
||||||
|
|
||||||
project(
|
project(
|
||||||
refcount
|
refcount
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# RefCount
|
# RefCount
|
||||||
|
|
||||||
RefCount is a reference counting (thus the name) and garbage collection library
|
RefCount is a reference counting (thus the name) and garbage collection library
|
||||||
for C. It is currently WIP and unfinished.
|
written in standard C99.
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
@ -23,5 +23,5 @@ You can generate documentation with:
|
|||||||
doxygen
|
doxygen
|
||||||
```
|
```
|
||||||
|
|
||||||
For now, you can look in the `test/` directory for some examples of the various
|
You can look in the `test/` directory for some examples of the various features
|
||||||
features of RefCount in use.
|
of RefCount in use.
|
||||||
|
@ -22,19 +22,19 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
typedef struct RefcountAllocator {
|
typedef struct RefcountAllocator {
|
||||||
union {
|
union {
|
||||||
void *(*malloc)(size_t size); //!< malloc implementation which takes no
|
void *(*no_data)(size_t size); //!< malloc implementation which takes no
|
||||||
//!< data.
|
//!< data.
|
||||||
void *(*malloc_with_data)(
|
void *(*with_data)(size_t size,
|
||||||
size_t size,
|
void *user_data); //!< malloc implementation that
|
||||||
void *user_data); //!< malloc implementation that takes a data
|
//!< takes a data argument.
|
||||||
//!< argument.
|
} malloc;
|
||||||
};
|
|
||||||
union {
|
union {
|
||||||
void (*free)(void *ptr); //!< free implementation which takes no data.
|
void (*no_data)(
|
||||||
void (*free_with_data)(void *ptr,
|
void *ptr); //!< free implementation which takes no data.
|
||||||
|
void (*with_data)(void *ptr,
|
||||||
void *user_data); //!< free implementation that
|
void *user_data); //!< free implementation that
|
||||||
//!< takes a data argument.
|
//!< takes a data argument.
|
||||||
};
|
} free;
|
||||||
bool pass_data;
|
bool pass_data;
|
||||||
void *user_data;
|
void *user_data;
|
||||||
} RefcountAllocator;
|
} RefcountAllocator;
|
||||||
@ -50,9 +50,9 @@ extern const RefcountAllocator *refcount_global_allocator;
|
|||||||
static inline void *refcount_malloc(const RefcountAllocator *alloc,
|
static inline void *refcount_malloc(const RefcountAllocator *alloc,
|
||||||
size_t size) {
|
size_t size) {
|
||||||
if (alloc->pass_data) {
|
if (alloc->pass_data) {
|
||||||
return alloc->malloc_with_data(size, alloc->user_data);
|
return alloc->malloc.with_data(size, alloc->user_data);
|
||||||
}
|
}
|
||||||
return alloc->malloc(size);
|
return alloc->malloc.no_data(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,9 +62,9 @@ static inline void *refcount_malloc(const RefcountAllocator *alloc,
|
|||||||
*/
|
*/
|
||||||
static inline void refcount_free(const RefcountAllocator *alloc, void *ptr) {
|
static inline void refcount_free(const RefcountAllocator *alloc, void *ptr) {
|
||||||
if (alloc->pass_data) {
|
if (alloc->pass_data) {
|
||||||
alloc->free_with_data(ptr, alloc->user_data);
|
alloc->free.with_data(ptr, alloc->user_data);
|
||||||
} else {
|
} else {
|
||||||
alloc->free(ptr);
|
alloc->free.no_data(ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <refcount/list.h>
|
#include <refcount/list.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <threads.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -103,16 +104,28 @@ void refcount_context_destroy(RefcountContext *ctx);
|
|||||||
*/
|
*/
|
||||||
typedef struct RefcountEntry {
|
typedef struct RefcountEntry {
|
||||||
bool is_static; //!< Whether the object is static.
|
bool is_static; //!< Whether the object is static.
|
||||||
|
RefcountList *weak_refs; //<! List of #RefcountWeakref structures for
|
||||||
|
//<! holding thread safe weak references.
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
uint64_t ref_count; //!< The object's reference count.
|
uint64_t ref_count; //!< The object's reference count.
|
||||||
RefcountList *gc_root; //!< The object's GC root, or NULL.
|
RefcountList *gc_root; //!< The object's GC root, or NULL.
|
||||||
};
|
} counted;
|
||||||
RefcountList *static_entry; //!< The object's static list entry, never
|
RefcountList *static_entry; //!< The object's static list entry, never
|
||||||
//!< NULL for registed statics.
|
//!< NULL for registered statics.
|
||||||
};
|
} impl;
|
||||||
} RefcountEntry;
|
} RefcountEntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure holding the required information for a weak reference. That is, a
|
||||||
|
* reference that does not prevent an object from being de-allocated.
|
||||||
|
*/
|
||||||
|
typedef struct RefcountWeakref {
|
||||||
|
RefcountList *entry; //!< Link in the #RefcountEntry list of the object this
|
||||||
|
//!< references that points to this reference.
|
||||||
|
void *data; //<! The object this weak ref references (not the entry).
|
||||||
|
} RefcountWeakref;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a pointer to the entry associated with an object.
|
* Return a pointer to the entry associated with an object.
|
||||||
* @param ctx The #RefcountContext
|
* @param ctx The #RefcountContext
|
||||||
@ -129,7 +142,8 @@ typedef struct RefcountEntry {
|
|||||||
* @param obj The object to test
|
* @param obj The object to test
|
||||||
* @return Whether or not the object is static
|
* @return Whether or not the object is static
|
||||||
*/
|
*/
|
||||||
static inline bool refcount_context_is_static(RefcountContext *ctx, void *obj) {
|
static inline bool refcount_context_is_static(const RefcountContext *ctx,
|
||||||
|
void *obj) {
|
||||||
return REFCOUNT_OBJECT_ENTRY(ctx, obj)->is_static;
|
return REFCOUNT_OBJECT_ENTRY(ctx, obj)->is_static;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,12 +155,12 @@ static inline bool refcount_context_is_static(RefcountContext *ctx, void *obj) {
|
|||||||
* @param obj The object
|
* @param obj The object
|
||||||
* @return The number of references the object has
|
* @return The number of references the object has
|
||||||
*/
|
*/
|
||||||
static inline size_t refcount_context_num_refs(RefcountContext *ctx,
|
static inline size_t refcount_context_num_refs(const RefcountContext *ctx,
|
||||||
void *obj) {
|
void *obj) {
|
||||||
return REFCOUNT_OBJECT_ENTRY(ctx, obj)->ref_count;
|
return REFCOUNT_OBJECT_ENTRY(ctx, obj)->impl.counted.ref_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void refcount_context_init_obj(RefcountContext *ctx, void *obj);
|
void refcount_context_init_obj(const RefcountContext *ctx, void *obj);
|
||||||
|
|
||||||
bool refcount_context_init_static(RefcountContext *ctx, void *obj);
|
bool refcount_context_init_static(RefcountContext *ctx, void *obj);
|
||||||
|
|
||||||
@ -163,7 +177,7 @@ static inline void refcount_context_deinit_static_as_callback(void *obj,
|
|||||||
refcount_context_deinit_static(ctx, obj);
|
refcount_context_deinit_static(ctx, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *refcount_context_ref(RefcountContext *ctx, void *obj);
|
void *refcount_context_ref(const RefcountContext *ctx, void *obj);
|
||||||
|
|
||||||
void *refcount_context_unref(RefcountContext *ctx, void *obj);
|
void *refcount_context_unref(RefcountContext *ctx, void *obj);
|
||||||
|
|
||||||
@ -180,6 +194,58 @@ void *refcount_context_float(RefcountContext *ctx, void *obj);
|
|||||||
|
|
||||||
ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx);
|
ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx);
|
||||||
|
|
||||||
|
RefcountWeakref *refcount_context_make_weakref(const RefcountContext *ctx,
|
||||||
|
void *obj);
|
||||||
|
|
||||||
|
void refcount_context_destroy_weakref(const RefcountContext *ctx,
|
||||||
|
RefcountWeakref *wr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return weather the object referenced by a weak reference still exists.
|
||||||
|
* @param ctx The #RefcountContext
|
||||||
|
* @param wr The weak reference
|
||||||
|
* @return Weather the reference is still valid
|
||||||
|
*/
|
||||||
|
static inline bool refcount_context_weakref_is_valid(const RefcountContext *ctx,
|
||||||
|
RefcountWeakref *wr) {
|
||||||
|
return wr->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *refcount_context_ref_weakref(const RefcountContext *ctx,
|
||||||
|
const RefcountWeakref *wr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function that calls #refcount_context_ref_weakref followed by
|
||||||
|
* #refcount_context_destroy_weakref.
|
||||||
|
* @param ctx The #RefcountContext
|
||||||
|
* @param wr The weak reference
|
||||||
|
* @return The newly referenced object, or NULL if the object no longer exists
|
||||||
|
*/
|
||||||
|
static inline void *refcount_context_strengthen(const RefcountContext *ctx,
|
||||||
|
RefcountWeakref *wr) {
|
||||||
|
void *obj = refcount_context_ref_weakref(ctx, wr);
|
||||||
|
refcount_context_destroy_weakref(ctx, wr);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a weak reference for an object and then unref it. This is the same as
|
||||||
|
* calling #refcount_context_make_weakref followed by #refcount_context_unref.
|
||||||
|
* @param ctx The #RefcountContext
|
||||||
|
* @param obj The object to create a weak reference for then unref
|
||||||
|
* @return The newly created weak reference, or NULL if an allocation error
|
||||||
|
* occurred. In this case the object is not unrefed.
|
||||||
|
*/
|
||||||
|
static inline RefcountWeakref *refcount_context_weaken(RefcountContext *ctx,
|
||||||
|
void *obj) {
|
||||||
|
RefcountWeakref *wr = refcount_context_make_weakref(ctx, obj);
|
||||||
|
if (!wr) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
refcount_context_unref(ctx, obj);
|
||||||
|
return wr;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as #refcount_context_is_static, but only operates on the global
|
* Same as #refcount_context_is_static, but only operates on the global
|
||||||
* context.
|
* context.
|
||||||
@ -273,6 +339,63 @@ 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_make_weakref, but operates on the global context.
|
||||||
|
* @param obj The object to reference
|
||||||
|
* @return The new weak reference, or NULL if an allocation error occurred
|
||||||
|
*/
|
||||||
|
static inline RefcountWeakref *refcount_make_weakref(void *obj) {
|
||||||
|
return refcount_context_make_weakref(refcount_default_context, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as #refcount_context_destroy_weakref, but operates on the global
|
||||||
|
* context.
|
||||||
|
* @param wr The weak reference
|
||||||
|
*/
|
||||||
|
static inline void refcount_destroy_weakref(RefcountWeakref *wr) {
|
||||||
|
refcount_context_destroy_weakref(refcount_default_context, wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as #refcount_context_ref_weakref, but operates on the global context.
|
||||||
|
* @param wr The weak reference
|
||||||
|
* @return The newly referenced object, or NULL if the object no longer exists
|
||||||
|
*/
|
||||||
|
static inline void *refcount_ref_weakref(RefcountWeakref *wr) {
|
||||||
|
return refcount_context_ref_weakref(refcount_default_context, wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as #refcount_context_weakref_is_valid, but operates on the global
|
||||||
|
* context.
|
||||||
|
* @param wr The weak reference
|
||||||
|
* @return Weather the reference is still valid
|
||||||
|
*/
|
||||||
|
static inline bool refcount_weakref_is_valid(RefcountWeakref *wr) {
|
||||||
|
return refcount_context_weakref_is_valid(refcount_default_context, wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as #refcount_context_strengthen, but operates on the global context.
|
||||||
|
* @param wr The weak reference
|
||||||
|
* @return The newly referenced object, or NULL if the object no longer
|
||||||
|
* exists
|
||||||
|
*/
|
||||||
|
static inline void *refcount_strengthen(RefcountWeakref *wr) {
|
||||||
|
return refcount_context_strengthen(refcount_default_context, wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as #refcount_context_weaken, but operates ojn the global context.
|
||||||
|
* @param obj The object to create a weak reference for then unref
|
||||||
|
* @return The newly created weak reference, or NULL if an allocation error
|
||||||
|
* occurred. In this case the object is not unrefed.
|
||||||
|
*/
|
||||||
|
static inline RefcountWeakref *refcount_weaken(void *obj) {
|
||||||
|
return refcount_context_weaken(refcount_default_context, obj);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
* Initial value of #refcount_global_allocator.
|
* Initial value of #refcount_global_allocator.
|
||||||
*/
|
*/
|
||||||
static const RefcountAllocator DEFAULT_ALLOCATOR = {
|
static const RefcountAllocator DEFAULT_ALLOCATOR = {
|
||||||
.malloc = malloc,
|
.malloc.no_data = malloc,
|
||||||
.free = free,
|
.free.no_data = free,
|
||||||
.pass_data = false,
|
.pass_data = false,
|
||||||
.user_data = NULL,
|
.user_data = NULL,
|
||||||
};
|
};
|
||||||
|
122
src/refcount.c
122
src/refcount.c
@ -94,11 +94,12 @@ void refcount_context_destroy(RefcountContext *ctx) {
|
|||||||
* @param ctx The #RefcountContext
|
* @param ctx The #RefcountContext
|
||||||
* @param obj The object to initialize
|
* @param obj The object to initialize
|
||||||
*/
|
*/
|
||||||
void refcount_context_init_obj(RefcountContext *ctx, void *obj) {
|
void refcount_context_init_obj(const RefcountContext *ctx, void *obj) {
|
||||||
if (obj) {
|
if (obj) {
|
||||||
ENTRY->is_static = false;
|
ENTRY->is_static = false;
|
||||||
ENTRY->gc_root = NULL;
|
ENTRY->impl.counted.gc_root = NULL;
|
||||||
ENTRY->ref_count = 0;
|
ENTRY->impl.counted.ref_count = 0;
|
||||||
|
ENTRY->weak_refs = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +116,8 @@ bool refcount_context_init_static(RefcountContext *ctx, void *obj) {
|
|||||||
if (!ctx->static_objects) {
|
if (!ctx->static_objects) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ENTRY->static_entry = ctx->static_objects;
|
ENTRY->impl.static_entry = ctx->static_objects;
|
||||||
|
ENTRY->weak_refs = NULL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +136,27 @@ static inline bool obj_held_refs(RefcountContext *ctx, void *obj,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call back used from #clear_weak_references.
|
||||||
|
* @param wr_raw The #RefcountWeakref object
|
||||||
|
*/
|
||||||
|
static void clear_weak_reference_callback(void *wr_raw) {
|
||||||
|
RefcountWeakref *wr = wr_raw;
|
||||||
|
wr->entry = NULL;
|
||||||
|
wr->data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all weak references of an object without freeing them.
|
||||||
|
* @param ctx The #RefcountContext
|
||||||
|
* @param obj The object to clear
|
||||||
|
*/
|
||||||
|
static inline void clear_weak_references(const RefcountContext *ctx,
|
||||||
|
void *obj) {
|
||||||
|
refcount_list_free_full(ENTRY->weak_refs, clear_weak_reference_callback,
|
||||||
|
&ctx->alloc);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregister a static object in a context.
|
* Unregister a static object in a context.
|
||||||
* @param ctx The #RefcountContext
|
* @param ctx The #RefcountContext
|
||||||
@ -146,8 +169,9 @@ bool refcount_context_deinit_static(RefcountContext *ctx, void *obj) {
|
|||||||
if (!obj_held_refs(ctx, obj, &held_refs)) {
|
if (!obj_held_refs(ctx, obj, &held_refs)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
clear_weak_references(ctx, obj);
|
||||||
ctx->static_objects = refcount_list_remove_full(
|
ctx->static_objects = refcount_list_remove_full(
|
||||||
ctx->static_objects, ENTRY->static_entry, NULL, &ctx->alloc);
|
ctx->static_objects, ENTRY->impl.static_entry, NULL, &ctx->alloc);
|
||||||
refcount_list_free_with_data_full(
|
refcount_list_free_with_data_full(
|
||||||
held_refs, refcount_context_unref_as_callback, ctx, &ctx->alloc);
|
held_refs, refcount_context_unref_as_callback, ctx, &ctx->alloc);
|
||||||
return true;
|
return true;
|
||||||
@ -163,11 +187,11 @@ bool refcount_context_deinit_static(RefcountContext *ctx, void *obj) {
|
|||||||
* @param obj The object to reference
|
* @param obj The object to reference
|
||||||
* @return The input object
|
* @return The input object
|
||||||
*/
|
*/
|
||||||
void *refcount_context_ref(RefcountContext *ctx, void *obj) {
|
void *refcount_context_ref(const RefcountContext *ctx, void *obj) {
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (!ENTRY->is_static) {
|
} else if (!ENTRY->is_static) {
|
||||||
++ENTRY->ref_count;
|
++ENTRY->impl.counted.ref_count;
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@ -180,13 +204,13 @@ void *refcount_context_ref(RefcountContext *ctx, void *obj) {
|
|||||||
* @return True on success
|
* @return True on success
|
||||||
*/
|
*/
|
||||||
static bool track_gc_root(RefcountContext *ctx, void *obj) {
|
static bool track_gc_root(RefcountContext *ctx, void *obj) {
|
||||||
if (!ENTRY->gc_root) {
|
if (!ENTRY->impl.counted.gc_root) {
|
||||||
ctx->gc_roots =
|
ctx->gc_roots =
|
||||||
refcount_list_push_full(ctx->gc_roots, obj, &ctx->alloc);
|
refcount_list_push_full(ctx->gc_roots, obj, &ctx->alloc);
|
||||||
if (!ctx->gc_roots) {
|
if (!ctx->gc_roots) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ENTRY->gc_root = ctx->gc_roots;
|
ENTRY->impl.counted.gc_root = ctx->gc_roots;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -198,9 +222,9 @@ static bool track_gc_root(RefcountContext *ctx, void *obj) {
|
|||||||
* @param obj The object to untrack
|
* @param obj The object to untrack
|
||||||
*/
|
*/
|
||||||
static void remove_gc_root(RefcountContext *ctx, void *obj) {
|
static void remove_gc_root(RefcountContext *ctx, void *obj) {
|
||||||
ctx->gc_roots = refcount_list_remove_full(ctx->gc_roots, ENTRY->gc_root,
|
ctx->gc_roots = refcount_list_remove_full(
|
||||||
NULL, &ctx->alloc);
|
ctx->gc_roots, ENTRY->impl.counted.gc_root, NULL, &ctx->alloc);
|
||||||
ENTRY->gc_root = NULL;
|
ENTRY->impl.counted.gc_root = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -222,11 +246,11 @@ static void *unref_to_queue(RefcountContext *ctx, void *obj,
|
|||||||
return obj;
|
return obj;
|
||||||
} else if (obj == toplevel) {
|
} else if (obj == toplevel) {
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (ENTRY->ref_count <= 1) {
|
} else if (ENTRY->impl.counted.ref_count <= 1) {
|
||||||
*queue = refcount_list_push_full(*queue, obj, &ctx->alloc);
|
*queue = refcount_list_push_full(*queue, obj, &ctx->alloc);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
--ENTRY->ref_count;
|
--ENTRY->impl.counted.ref_count;
|
||||||
track_gc_root(ctx, obj);
|
track_gc_root(ctx, obj);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@ -258,6 +282,7 @@ static void unref_to_queue_as_callback(void *obj, void *ctx_and_queue_raw) {
|
|||||||
* @param obj The object to destroy
|
* @param obj The object to destroy
|
||||||
*/
|
*/
|
||||||
static inline void destroy_object(RefcountContext *ctx, void *obj) {
|
static inline void destroy_object(RefcountContext *ctx, void *obj) {
|
||||||
|
clear_weak_references(ctx, obj);
|
||||||
remove_gc_root(ctx, obj);
|
remove_gc_root(ctx, obj);
|
||||||
if (ctx->destroy_callback) {
|
if (ctx->destroy_callback) {
|
||||||
ctx->destroy_callback(obj, ctx->user_data);
|
ctx->destroy_callback(obj, ctx->user_data);
|
||||||
@ -326,11 +351,11 @@ void *refcount_context_float(RefcountContext *ctx, void *obj) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
} else if (ENTRY->is_static) {
|
} else if (ENTRY->is_static) {
|
||||||
return obj;
|
return obj;
|
||||||
} else if (!ENTRY->ref_count) {
|
} else if (!ENTRY->impl.counted.ref_count) {
|
||||||
BREAKPOINT(FLOAT, ctx, obj);
|
BREAKPOINT(FLOAT, ctx, obj);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
if (--ENTRY->ref_count) {
|
if (--ENTRY->impl.counted.ref_count) {
|
||||||
track_gc_root(ctx, obj);
|
track_gc_root(ctx, obj);
|
||||||
} else {
|
} else {
|
||||||
remove_gc_root(ctx, obj);
|
remove_gc_root(ctx, obj);
|
||||||
@ -371,7 +396,8 @@ static bool free_roots_foreach(void *obj, void *ignored, void *user_data) {
|
|||||||
}
|
}
|
||||||
struct ContextAndRootPtr *data = user_data;
|
struct ContextAndRootPtr *data = user_data;
|
||||||
if (*data->root_ptr
|
if (*data->root_ptr
|
||||||
&& *data->root_ptr == REFCOUNT_OBJECT_ENTRY(data->ctx, obj)->gc_root) {
|
&& *data->root_ptr
|
||||||
|
== REFCOUNT_OBJECT_ENTRY(data->ctx, obj)->impl.counted.gc_root) {
|
||||||
*data->root_ptr = (*data->root_ptr)->next;
|
*data->root_ptr = (*data->root_ptr)->next;
|
||||||
data->did_update = true;
|
data->did_update = true;
|
||||||
}
|
}
|
||||||
@ -413,7 +439,7 @@ static ptrdiff_t check_gc_root(RefcountContext *ctx, RefcountList **root_ptr) {
|
|||||||
if (ht_has(counts, obj)) {
|
if (ht_has(counts, obj)) {
|
||||||
count = HT_UUNSTUFF(ht_get(counts, obj));
|
count = HT_UUNSTUFF(ht_get(counts, obj));
|
||||||
} else {
|
} else {
|
||||||
count = REFCOUNT_OBJECT_ENTRY(ctx, obj)->ref_count;
|
count = REFCOUNT_OBJECT_ENTRY(ctx, obj)->impl.counted.ref_count;
|
||||||
++seen_objects;
|
++seen_objects;
|
||||||
}
|
}
|
||||||
queue = refcount_list_pop_full(queue, NULL, &ctx->alloc);
|
queue = refcount_list_pop_full(queue, NULL, &ctx->alloc);
|
||||||
@ -460,3 +486,63 @@ ptrdiff_t refcount_context_garbage_collect(RefcountContext *ctx) {
|
|||||||
}
|
}
|
||||||
return total_cleared;
|
return total_cleared;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new weak reference for an object. A weak reference will allow safe
|
||||||
|
* access to the referenced object without holding a reference. That is, the
|
||||||
|
* referenced object can be accessed until it's reference count falls to 0 and
|
||||||
|
* it is freed. After this, attempts to use the weak reference will just return
|
||||||
|
* NULL to indicate that the referenced object is no longer in existence.
|
||||||
|
* @param ctx The #RefcountContext
|
||||||
|
* @param obj The object for which to create a weak reference
|
||||||
|
* @return The newly created weak reference, or NULL if an allocated error
|
||||||
|
* occurred.
|
||||||
|
*/
|
||||||
|
RefcountWeakref *refcount_context_make_weakref(const RefcountContext *ctx,
|
||||||
|
void *obj) {
|
||||||
|
RefcountWeakref *wr = refcount_malloc(&ctx->alloc, sizeof(RefcountWeakref));
|
||||||
|
if (!wr) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
RefcountList *new_weak_refs =
|
||||||
|
refcount_list_push_full(ENTRY->weak_refs, wr, &ctx->alloc);
|
||||||
|
if (!new_weak_refs) {
|
||||||
|
refcount_free(&ctx->alloc, wr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ENTRY->weak_refs = new_weak_refs;
|
||||||
|
wr->entry = new_weak_refs;
|
||||||
|
wr->data = obj;
|
||||||
|
return wr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy a weak reference. This has no effect on the reference count of the
|
||||||
|
* original object.
|
||||||
|
* @param ctx The #RefcountContext
|
||||||
|
* @param wr The weak reference
|
||||||
|
*/
|
||||||
|
void refcount_context_destroy_weakref(const RefcountContext *ctx,
|
||||||
|
RefcountWeakref *wr) {
|
||||||
|
if (wr->data) {
|
||||||
|
void *obj = wr->data;
|
||||||
|
ENTRY->weak_refs = refcount_list_remove_full(
|
||||||
|
ENTRY->weak_refs, wr->entry, NULL, &ctx->alloc);
|
||||||
|
}
|
||||||
|
refcount_free(&ctx->alloc, wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a reference to an object referenced by a weak reference and return the
|
||||||
|
* object. If the referenced object no longer exists, return NULL.
|
||||||
|
* @param ctx The #RefcountContext
|
||||||
|
* @param wr The weak reference
|
||||||
|
* @return The newly referenced object, or NULL
|
||||||
|
*/
|
||||||
|
void *refcount_context_ref_weakref(const RefcountContext *ctx,
|
||||||
|
const RefcountWeakref *wr) {
|
||||||
|
if (!wr->data) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return refcount_context_ref(ctx, wr->data);
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#if __has_attribute(unused)
|
#if defined(__has_attribute) && __has_attribute(unused)
|
||||||
# define UNUSED __attribute__((unused))
|
# define UNUSED __attribute__((unused))
|
||||||
#else
|
#else
|
||||||
# define UNUSED
|
# define UNUSED
|
||||||
@ -67,8 +67,8 @@ static UNUSED char *counting_strdup(const char *str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static UNUSED const RefcountAllocator COUNTING_ALLOCATOR = {
|
static UNUSED const RefcountAllocator COUNTING_ALLOCATOR = {
|
||||||
.malloc = counting_malloc,
|
.malloc.no_data = counting_malloc,
|
||||||
.free = counting_free,
|
.free.no_data = counting_free,
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -113,6 +113,42 @@ int main(int argc, const char **argv) {
|
|||||||
|
|
||||||
assert(refcount_context_garbage_collect(c) == 26);
|
assert(refcount_context_garbage_collect(c) == 26);
|
||||||
|
|
||||||
|
a = make_a(c, 0, "a");
|
||||||
|
|
||||||
|
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);
|
||||||
|
assert(refcount_context_num_refs(c, a) == 2);
|
||||||
|
|
||||||
|
RefcountWeakref *x = refcount_context_weaken(c, a);
|
||||||
|
w = refcount_context_make_weakref(c, a);
|
||||||
|
assert(refcount_context_num_refs(c, a) == 1);
|
||||||
|
assert(w != x);
|
||||||
|
refcount_context_destroy_weakref(c, x);
|
||||||
|
|
||||||
|
refcount_context_ref(c, a);
|
||||||
|
assert(refcount_context_num_refs(c, a) == 2);
|
||||||
|
|
||||||
|
x = refcount_context_weaken(c, a);
|
||||||
|
assert(refcount_context_num_refs(c, a) == 1);
|
||||||
|
assert(w != x);
|
||||||
|
assert(refcount_context_weakref_is_valid(c, w));
|
||||||
|
assert(refcount_context_weakref_is_valid(c, x));
|
||||||
|
|
||||||
|
assert(!refcount_context_unref(c, a));
|
||||||
|
assert(!refcount_context_weakref_is_valid(c, w));
|
||||||
|
assert(!refcount_context_weakref_is_valid(c, x));
|
||||||
|
|
||||||
|
refcount_context_destroy_weakref(c, w);
|
||||||
|
refcount_context_destroy_weakref(c, x);
|
||||||
|
|
||||||
refcount_context_destroy(c);
|
refcount_context_destroy(c);
|
||||||
|
|
||||||
check_allocator_status();
|
check_allocator_status();
|
||||||
|
Reference in New Issue
Block a user