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); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -103,16 +103,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 +141,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 +154,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 +176,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 +193,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 +338,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