xref: /netbsd-src/external/mpl/bind/dist/lib/isc/include/isc/refcount.h (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: refcount.h,v 1.8 2025/01/26 16:25:42 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 #pragma once
17 
18 #include <inttypes.h>
19 
20 #include <isc/assertions.h>
21 #include <isc/atomic.h>
22 #include <isc/error.h>
23 #include <isc/lang.h>
24 #include <isc/mutex.h>
25 #include <isc/tid.h>
26 #include <isc/types.h>
27 
28 /*! \file isc/refcount.h
29  * \brief Implements a locked reference counter.
30  *
31  * These macros uses C11(-like) atomic functions to implement reference
32  * counting.  The isc_refcount_t type must not be accessed directly.
33  */
34 
35 ISC_LANG_BEGINDECLS
36 
37 typedef atomic_uint_fast32_t isc_refcount_t;
38 
39 #define ISC_REFCOUNT_INITIALIZER(a) (a)
40 
41 /** \def isc_refcount_init(ref, n)
42  *  \brief Initialize the reference counter.
43  *  \param[in] ref pointer to reference counter.
44  *  \param[in] n an initial number of references.
45  *  \return nothing.
46  *
47  *  \warning No memory barrier are being imposed here.
48  */
49 #define isc_refcount_init(target, value) atomic_init(target, value)
50 
51 /** \def isc_refcount_current(ref)
52  *  \brief Returns current number of references.
53  *  \param[in] ref pointer to reference counter.
54  *  \returns current value of reference counter.
55  */
56 
57 #define isc_refcount_current(target) atomic_load_acquire(target)
58 
59 /** \def isc_refcount_destroy(ref)
60  *  \brief a destructor that makes sure that all references were cleared.
61  *  \param[in] ref pointer to reference counter.
62  *  \returns nothing.
63  */
64 #define isc_refcount_destroy(target) \
65 	ISC_REQUIRE(isc_refcount_current(target) == 0)
66 
67 /** \def isc_refcount_increment0(ref)
68  *  \brief increases reference counter by 1.
69  *  \param[in] ref pointer to reference counter.
70  *  \returns previous value of reference counter.
71  */
72 #define isc_refcount_increment0(target)                    \
73 	({                                                 \
74 		uint_fast32_t __v;                         \
75 		__v = atomic_fetch_add_release(target, 1); \
76 		INSIST(__v < UINT32_MAX);                  \
77 		__v;                                       \
78 	})
79 
80 /** \def isc_refcount_increment(ref)
81  *  \brief increases reference counter by 1.
82  *  \param[in] ref pointer to reference counter.
83  *  \returns previous value of reference counter.
84  */
85 #define isc_refcount_increment(target)                     \
86 	({                                                 \
87 		uint_fast32_t __v;                         \
88 		__v = atomic_fetch_add_release(target, 1); \
89 		INSIST(__v > 0 && __v < UINT32_MAX);       \
90 		__v;                                       \
91 	})
92 
93 /** \def isc_refcount_decrement(ref)
94  *  \brief decreases reference counter by 1.
95  *  \param[in] ref pointer to reference counter.
96  *  \returns previous value of reference counter.
97  */
98 #define isc_refcount_decrement(target)                     \
99 	({                                                 \
100 		uint_fast32_t __v;                         \
101 		__v = atomic_fetch_sub_acq_rel(target, 1); \
102 		INSIST(__v > 0);                           \
103 		__v;                                       \
104 	})
105 
106 #define isc_refcount_decrementz(target)                               \
107 	do {                                                          \
108 		uint_fast32_t _refs = isc_refcount_decrement(target); \
109 		ISC_INSIST(_refs == 1);                               \
110 	} while (0)
111 
112 #define isc_refcount_decrement1(target)                               \
113 	do {                                                          \
114 		uint_fast32_t _refs = isc_refcount_decrement(target); \
115 		ISC_INSIST(_refs > 1);                                \
116 	} while (0)
117 
118 #define isc_refcount_decrement0(target)                               \
119 	do {                                                          \
120 		uint_fast32_t _refs = isc_refcount_decrement(target); \
121 		ISC_INSIST(_refs > 0);                                \
122 	} while (0)
123 
124 #define ISC__REFCOUNT_TRACE_DECL(name, stat)                               \
125 	stat name##_t *name##__ref(name##_t *ptr, const char *func,        \
126 				   const char *file, unsigned int line);   \
127 	stat void      name##__unref(name##_t *ptr, const char *func,      \
128 				     const char *file, unsigned int line); \
129 	stat void      name##__attach(name##_t *ptr, name##_t **ptrp,      \
130 				      const char *func, const char *file,  \
131 				      unsigned int line);                  \
132 	stat void      name##__detach(name##_t **ptrp, const char *func,   \
133 				      const char *file, unsigned int line)
134 
135 #define ISC_REFCOUNT_BLANK
136 #define ISC_REFCOUNT_TRACE_DECL(name) \
137 	ISC__REFCOUNT_TRACE_DECL(name, ISC_REFCOUNT_BLANK)
138 #define ISC_REFCOUNT_STATIC_TRACE_DECL(name) \
139 	ISC__REFCOUNT_TRACE_DECL(name, static inline)
140 
141 #define ISC__REFCOUNT_TRACE_IMPL(name, destroy, stat)                         \
142 	stat name##_t *name##__ref(name##_t *ptr, const char *func,           \
143 				   const char *file, unsigned int line) {     \
144 		REQUIRE(ptr != NULL);                                         \
145 		uint_fast32_t refs =                                          \
146 			isc_refcount_increment(&ptr->references) + 1;         \
147 		fprintf(stderr,                                               \
148 			"%s:%s:%s:%u:t%u:%p->references = %" PRIuFAST32 "\n", \
149 			__func__, func, file, line, isc_tid(), ptr, refs);    \
150 		return (ptr);                                                 \
151 	}                                                                     \
152                                                                               \
153 	stat void name##__unref(name##_t *ptr, const char *func,              \
154 				const char *file, unsigned int line) {        \
155 		REQUIRE(ptr != NULL);                                         \
156 		uint_fast32_t refs =                                          \
157 			isc_refcount_decrement(&ptr->references) - 1;         \
158 		if (refs == 0) {                                              \
159 			isc_refcount_destroy(&ptr->references);               \
160 			destroy(ptr);                                         \
161 		}                                                             \
162 		fprintf(stderr,                                               \
163 			"%s:%s:%s:%u:t%u:%p->references = %" PRIuFAST32 "\n", \
164 			__func__, func, file, line, isc_tid(), ptr, refs);    \
165 	}                                                                     \
166 	stat void name##__attach(name##_t *ptr, name##_t **ptrp,              \
167 				 const char *func, const char *file,          \
168 				 unsigned int line) {                         \
169 		REQUIRE(ptrp != NULL && *ptrp == NULL);                       \
170 		uint_fast32_t refs =                                          \
171 			isc_refcount_increment(&ptr->references) + 1;         \
172 		fprintf(stderr,                                               \
173 			"%s:%s:%s:%u:t%u:%p->references = %" PRIuFAST32 "\n", \
174 			__func__, func, file, line, isc_tid(), ptr, refs);    \
175 		*ptrp = ptr;                                                  \
176 	}                                                                     \
177                                                                               \
178 	stat void name##__detach(name##_t **ptrp, const char *func,           \
179 				 const char *file, unsigned int line) {       \
180 		REQUIRE(ptrp != NULL && *ptrp != NULL);                       \
181 		name##_t *ptr = *ptrp;                                        \
182 		*ptrp = NULL;                                                 \
183 		uint_fast32_t refs =                                          \
184 			isc_refcount_decrement(&ptr->references) - 1;         \
185 		if (refs == 0) {                                              \
186 			isc_refcount_destroy(&ptr->references);               \
187 			destroy(ptr);                                         \
188 		}                                                             \
189 		fprintf(stderr,                                               \
190 			"%s:%s:%s:%u:t%u:%p->references = %" PRIuFAST32 "\n", \
191 			__func__, func, file, line, isc_tid(), ptr, refs);    \
192 	}
193 
194 #define ISC_REFCOUNT_TRACE_IMPL(name, destroy) \
195 	ISC__REFCOUNT_TRACE_IMPL(name, destroy, ISC_REFCOUNT_BLANK)
196 #define ISC_REFCOUNT_STATIC_TRACE_IMPL(name, destroy) \
197 	ISC__REFCOUNT_TRACE_IMPL(name, destroy, static inline)
198 
199 #define ISC__REFCOUNT_DECL(name, stat)                                      \
200 	stat name##_t *name##_ref(name##_t *ptr) __attribute__((unused));   \
201 	stat void      name##_unref(name##_t *ptr) __attribute__((unused)); \
202 	stat void      name##_attach(name##_t *ptr, name##_t **ptrp)        \
203 		__attribute__((unused));                                    \
204 	stat void name##_detach(name##_t **ptrp) __attribute__((unused))
205 
206 #define ISC_REFCOUNT_DECL(name)	       ISC__REFCOUNT_DECL(name, ISC_REFCOUNT_BLANK)
207 #define ISC_REFCOUNT_STATIC_DECL(name) ISC__REFCOUNT_DECL(name, static inline)
208 
209 #define ISC__REFCOUNT_IMPL(name, destroy, stat)                      \
210 	stat name##_t *name##_ref(name##_t *ptr) {                   \
211 		REQUIRE(ptr != NULL);                                \
212 		isc_refcount_increment(&ptr->references);            \
213 		return (ptr);                                        \
214 	}                                                            \
215                                                                      \
216 	stat void name##_unref(name##_t *ptr) {                      \
217 		REQUIRE(ptr != NULL);                                \
218 		if (isc_refcount_decrement(&ptr->references) == 1) { \
219 			isc_refcount_destroy(&ptr->references);      \
220 			destroy(ptr);                                \
221 		}                                                    \
222 	}                                                            \
223 	stat void name##_attach(name##_t *ptr, name##_t **ptrp) {    \
224 		REQUIRE(ptrp != NULL && *ptrp == NULL);              \
225 		name##_ref(ptr);                                     \
226 		*ptrp = ptr;                                         \
227 	}                                                            \
228                                                                      \
229 	stat void name##_detach(name##_t **ptrp) {                   \
230 		REQUIRE(ptrp != NULL && *ptrp != NULL);              \
231 		name##_t *ptr = *ptrp;                               \
232 		*ptrp = NULL;                                        \
233 		name##_unref(ptr);                                   \
234 	}
235 
236 #define ISC_REFCOUNT_IMPL(name, destroy) \
237 	ISC__REFCOUNT_IMPL(name, destroy, ISC_REFCOUNT_BLANK)
238 #define ISC_REFCOUNT_STATIC_IMPL(name, destroy) \
239 	ISC__REFCOUNT_IMPL(name, destroy, static inline)
240 
241 ISC_LANG_ENDDECLS
242