Home | History | Annotate | Line # | Download | only in isc
      1 /*	$NetBSD: refcount.h,v 1.9 2026/04/08 00:16:16 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