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