1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7 *
8 * See the COPYRIGHT file distributed with this work for additional
9 * information regarding copyright ownership.
10 */
11
12 #pragma once
13
14 #include <inttypes.h>
15
16 #include <isc/assertions.h>
17 #include <isc/atomic.h>
18 #include <isc/error.h>
19 #include <isc/lang.h>
20 #include <isc/mutex.h>
21 #include <isc/types.h>
22
23 /*! \file isc/refcount.h
24 * \brief Implements a locked reference counter.
25 *
26 * These macros uses C11(-like) atomic functions to implement reference
27 * counting. The isc_refcount_t type must not be accessed directly.
28 */
29
30 ISC_LANG_BEGINDECLS
31
32 typedef atomic_uint_fast32_t isc_refcount_t;
33
34 /** \def isc_refcount_init(ref, n)
35 * \brief Initialize the reference counter.
36 * \param[in] ref pointer to reference counter.
37 * \param[in] n an initial number of references.
38 * \return nothing.
39 *
40 * \warning No memory barrier are being imposed here.
41 */
42 #define isc_refcount_init(target, value) atomic_init(target, value)
43
44 /** \def isc_refcount_current(ref)
45 * \brief Returns current number of references.
46 * \param[in] ref pointer to reference counter.
47 * \returns current value of reference counter.
48 *
49 * Undo implicit promotion to 64 bits in our Windows implementation of
50 * atomic_load_explicit() by casting to uint_fast32_t.
51 */
52
53 #define isc_refcount_current(target) (uint_fast32_t) atomic_load_acquire(target)
54
55 /** \def isc_refcount_destroy(ref)
56 * \brief a destructor that makes sure that all references were cleared.
57 * \param[in] ref pointer to reference counter.
58 * \returns nothing.
59 */
60 #define isc_refcount_destroy(target) \
61 ISC_REQUIRE(isc_refcount_current(target) == 0)
62
63 /** \def isc_refcount_increment0(ref)
64 * \brief increases reference counter by 1.
65 * \param[in] ref pointer to reference counter.
66 * \returns previous value of reference counter.
67 */
68 #if _MSC_VER
69 static inline uint_fast32_t
isc_refcount_increment0(isc_refcount_t * target)70 isc_refcount_increment0(isc_refcount_t *target) {
71 uint_fast32_t __v;
72 __v = (uint_fast32_t)atomic_fetch_add_relaxed(target, 1);
73 INSIST(__v < UINT32_MAX);
74 return (__v);
75 }
76 #else /* _MSC_VER */
77 #define isc_refcount_increment0(target) \
78 ({ \
79 /* cppcheck-suppress shadowVariable */ \
80 uint_fast32_t __v; \
81 __v = atomic_fetch_add_relaxed(target, 1); \
82 INSIST(__v < UINT32_MAX); \
83 __v; \
84 })
85 #endif /* _MSC_VER */
86
87 /** \def isc_refcount_increment(ref)
88 * \brief increases reference counter by 1.
89 * \param[in] ref pointer to reference counter.
90 * \returns previous value of reference counter.
91 */
92 #if _MSC_VER
93 static inline uint_fast32_t
isc_refcount_increment(isc_refcount_t * target)94 isc_refcount_increment(isc_refcount_t *target) {
95 uint_fast32_t __v;
96 __v = (uint_fast32_t)atomic_fetch_add_relaxed(target, 1);
97 INSIST(__v > 0 && __v < UINT32_MAX);
98 return (__v);
99 }
100 #else /* _MSC_VER */
101 #define isc_refcount_increment(target) \
102 ({ \
103 /* cppcheck-suppress shadowVariable */ \
104 uint_fast32_t __v; \
105 __v = atomic_fetch_add_relaxed(target, 1); \
106 INSIST(__v > 0 && __v < UINT32_MAX); \
107 __v; \
108 })
109 #endif /* _MSC_VER */
110
111 /** \def isc_refcount_decrement(ref)
112 * \brief decreases reference counter by 1.
113 * \param[in] ref pointer to reference counter.
114 * \returns previous value of reference counter.
115 */
116 #if _MSC_VER
117 static inline uint_fast32_t
isc_refcount_decrement(isc_refcount_t * target)118 isc_refcount_decrement(isc_refcount_t *target) {
119 uint_fast32_t __v;
120 __v = (uint_fast32_t)atomic_fetch_sub_acq_rel(target, 1);
121 INSIST(__v > 0);
122 return (__v);
123 }
124 #else /* _MSC_VER */
125 #define isc_refcount_decrement(target) \
126 ({ \
127 /* cppcheck-suppress shadowVariable */ \
128 uint_fast32_t __v; \
129 __v = atomic_fetch_sub_acq_rel(target, 1); \
130 INSIST(__v > 0); \
131 __v; \
132 })
133 #endif /* _MSC_VER */
134
135 #define isc_refcount_decrementz(target) \
136 do { \
137 uint_fast32_t _refs = isc_refcount_decrement(target); \
138 ISC_INSIST(_refs == 1); \
139 } while (0)
140
141 #define isc_refcount_decrement1(target) \
142 do { \
143 uint_fast32_t _refs = isc_refcount_decrement(target); \
144 ISC_INSIST(_refs > 1); \
145 } while (0)
146
147 #define isc_refcount_decrement0(target) \
148 do { \
149 uint_fast32_t _refs = isc_refcount_decrement(target); \
150 ISC_INSIST(_refs > 0); \
151 } while (0)
152
153 ISC_LANG_ENDDECLS
154