PatchworkOS  c9fea19
A non-POSIX operating system.
Loading...
Searching...
No Matches
ref.h
Go to the documentation of this file.
1#pragma once
2
3#include <kernel/defs.h>
4
5#include <assert.h>
6#include <stdatomic.h>
7#include <stdint.h>
8
9/**
10 * @brief Reference counting
11 * @defgroup kernel_utils_ref Reference counting
12 * @ingroup kernel_utils
13 * @{
14 */
15
16/**
17 * @brief Magic value used in debug builds to check for corruption or invalid use of the `ref_t` structure.
18 */
19#define REF_MAGIC 0x26CB6E4C
20
21/**
22 * @brief Reference counting structure
23 * @struct ref_t
24 *
25 * Provides a generic interface for reference counting. Must be placed as the first element in any struct that requires
26 * reference counting.
27 *
28 */
29typedef struct ref
30{
31#ifndef NDEBUG
32 /**
33 * @brief Debug magic value to detect corruption
34 */
36#endif
37 /**
38 * @brief Atomic reference counter
39 */
40 atomic_uint32_t count;
41 /**
42 * @brief Cleanup function called when count reaches zero
43 */
44 void (*free)(void* self);
45} ref_t;
46
47/**
48 * @brief RAII-style cleanup for scoped references
49 *
50 * Uses GCC's cleanup attribute to automatically call `ref_dec` when going out of scope.
51 *
52 * @param ptr Pointer to the struct containing `ref_t` as its first member, can be `NULL`.
53 */
54#define UNREF_DEFER(ptr) __attribute__((cleanup(ref_defer_cleanup))) void* CONCAT(p, __COUNTER__) = (ptr)
55
56/**
57 * @brief Increment reference count
58 *
59 * Atomically increments the reference counter. Used to avoid the need for a typecast. The magic number checking makes
60 * sure we cant accidentally misuse this.
61 *
62 * @param ptr Pointer to the struct containing `ref_t` as its first member, can be `NULL`.
63 * @return The `ptr` passed as input
64 */
65#define REF(ptr) \
66 ({ \
67 ref_t* ref = (ref_t*)ptr; \
68 ref_inc(ref); \
69 ptr; \
70 })
71
72/**
73 * @brief Decrement reference count
74 *
75 * Atomically decrements the reference counter. Used to avoid the need for a typecast. The magic number checking makes
76 * sure we cant accidentally misuse this.
77 *
78 * @param ptr Pointer to the struct containing `ref_t` as its first member, can be `NULL`.
79 */
80#define UNREF(ptr) \
81 ({ \
82 ref_t* ref = (ref_t*)ptr; \
83 ref_dec(ref); \
84 })
85
86/**
87 * @brief Initialize a reference counter
88 *
89 * @param ref Pointer to the reference counter structure
90 * @param free Cleanup function to call when count reaches zero
91 */
92static inline void ref_init(ref_t* ref, void* free)
93{
94#ifndef NDEBUG
95 ref->magic = REF_MAGIC;
96#endif
97 atomic_init(&ref->count, 1);
98 ref->free = free;
99}
100
101/**
102 * @brief Increment reference count
103 *
104 * @param ptr Pointer to the struct containing `ref_t` as its first member, can be `NULL`.
105 * @return The `ptr` passed as input
106 */
107static inline void* ref_inc(void* ptr)
108{
109 ref_t* ref = (ref_t*)ptr;
110 if (ref == NULL)
111 {
112 return NULL;
113 }
114
115 assert(ref->magic == REF_MAGIC);
117 return ptr;
118}
119
120/**
121 * @brief Decrement reference count
122 *
123 * If count reaches zero it calls the registered cleanup function.
124 *
125 * @param ptr Pointer to the struct containing `ref_t` as its first member, can be `NULL`.
126 */
127static inline void ref_dec(void* ptr)
128{
129 ref_t* ref = (ref_t*)ptr;
130 if (ref == NULL)
131 {
132 return;
133 }
134
135 assert(ref->magic == REF_MAGIC);
137 if (count > 1)
138 {
139 return;
140 }
141
143 assert(count == 1); // Count is now zero, if it was zero before then we have a double free.
144 if (ref->free == NULL)
145 {
146 return;
147 }
148
149#ifndef NDEBUG
150 ref->magic = 0;
151#endif
152 ref->free(ptr);
153}
154
155static inline void ref_defer_cleanup(void** ptr)
156{
157 ref_dec(*ptr);
158}
159
160/** @} */
#define assert(expression)
Definition assert.h:29
static void ref_defer_cleanup(void **ptr)
Definition ref.h:155
static void ref_dec(void *ptr)
Decrement reference count.
Definition ref.h:127
static void ref_init(ref_t *ref, void *free)
Initialize a reference counter.
Definition ref.h:92
#define REF_MAGIC
Magic value used in debug builds to check for corruption or invalid use of the ref_t structure.
Definition ref.h:19
static void * ref_inc(void *ptr)
Increment reference count.
Definition ref.h:107
#define NULL
Pointer error value.
Definition NULL.h:23
static atomic_long count
Definition main.c:10
@ memory_order_relaxed
Definition stdatomic.h:116
@ memory_order_acquire
Definition stdatomic.h:118
#define atomic_fetch_add_explicit(object, operand, order)
Definition stdatomic.h:259
#define atomic_thread_fence(order)
Definition stdatomic.h:135
#define atomic_init(obj, value)
Definition stdatomic.h:75
#define atomic_fetch_sub_explicit(object, operand, order)
Definition stdatomic.h:262
__UINT32_TYPE__ uint32_t
Definition stdint.h:15
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
_PUBLIC void free(void *ptr)
Definition free.c:11
Reference counting structure.
Definition ref.h:30
atomic_uint32_t count
Atomic reference counter.
Definition ref.h:40
void(* free)(void *self)
Cleanup function called when count reaches zero.
Definition ref.h:44
uint32_t magic
Debug magic value to detect corruption.
Definition ref.h:35