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