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