1 /*	$NetBSD: refcount.h,v 1.6 2022/09/23 12:15:33 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/platform.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 /** \def isc_refcount_init(ref, n)
40  *  \brief Initialize the reference counter.
41  *  \param[in] ref pointer to reference counter.
42  *  \param[in] n an initial number of references.
43  *  \return nothing.
44  *
45  *  \warning No memory barrier are being imposed here.
46  */
47 #define isc_refcount_init(target, value) atomic_init(target, value)
48 
49 /** \def isc_refcount_current(ref)
50  *  \brief Returns current number of references.
51  *  \param[in] ref pointer to reference counter.
52  *  \returns current value of reference counter.
53  *
54  *   Undo implicit promotion to 64 bits in our Windows implementation of
55  *   atomic_load_explicit() by casting to uint_fast32_t.
56  */
57 
58 #define isc_refcount_current(target) (uint_fast32_t) atomic_load_acquire(target)
59 
60 /** \def isc_refcount_destroy(ref)
61  *  \brief a destructor that makes sure that all references were cleared.
62  *  \param[in] ref pointer to reference counter.
63  *  \returns nothing.
64  */
65 #define isc_refcount_destroy(target) \
66 	ISC_REQUIRE(isc_refcount_current(target) == 0)
67 
68 /** \def isc_refcount_increment0(ref)
69  *  \brief increases reference counter by 1.
70  *  \param[in] ref pointer to reference counter.
71  *  \returns previous value of reference counter.
72  */
73 #if _MSC_VER
74 static inline uint_fast32_t
isc_refcount_increment0(isc_refcount_t * target)75 isc_refcount_increment0(isc_refcount_t *target) {
76 	uint_fast32_t __v;
77 	__v = (uint_fast32_t)atomic_fetch_add_relaxed(target, 1);
78 	INSIST(__v < UINT32_MAX);
79 	return (__v);
80 }
81 #else /* _MSC_VER */
82 #define isc_refcount_increment0(target)                    \
83 	({                                                 \
84 		/* cppcheck-suppress shadowVariable */     \
85 		uint_fast32_t __v;                         \
86 		__v = atomic_fetch_add_relaxed(target, 1); \
87 		INSIST(__v < UINT32_MAX);                  \
88 		__v;                                       \
89 	})
90 #endif /* _MSC_VER */
91 
92 /** \def isc_refcount_increment(ref)
93  *  \brief increases reference counter by 1.
94  *  \param[in] ref pointer to reference counter.
95  *  \returns previous value of reference counter.
96  */
97 #if _MSC_VER
98 static inline uint_fast32_t
isc_refcount_increment(isc_refcount_t * target)99 isc_refcount_increment(isc_refcount_t *target) {
100 	uint_fast32_t __v;
101 	__v = (uint_fast32_t)atomic_fetch_add_relaxed(target, 1);
102 	INSIST(__v > 0 && __v < UINT32_MAX);
103 	return (__v);
104 }
105 #else /* _MSC_VER */
106 #define isc_refcount_increment(target)                     \
107 	({                                                 \
108 		/* cppcheck-suppress shadowVariable */     \
109 		uint_fast32_t __v;                         \
110 		__v = atomic_fetch_add_relaxed(target, 1); \
111 		INSIST(__v > 0 && __v < UINT32_MAX);       \
112 		__v;                                       \
113 	})
114 #endif /* _MSC_VER */
115 
116 /** \def isc_refcount_decrement(ref)
117  *  \brief decreases reference counter by 1.
118  *  \param[in] ref pointer to reference counter.
119  *  \returns previous value of reference counter.
120  */
121 #if _MSC_VER
122 static inline uint_fast32_t
isc_refcount_decrement(isc_refcount_t * target)123 isc_refcount_decrement(isc_refcount_t *target) {
124 	uint_fast32_t __v;
125 	__v = (uint_fast32_t)atomic_fetch_sub_acq_rel(target, 1);
126 	INSIST(__v > 0);
127 	return (__v);
128 }
129 #else /* _MSC_VER */
130 #define isc_refcount_decrement(target)                     \
131 	({                                                 \
132 		/* cppcheck-suppress shadowVariable */     \
133 		uint_fast32_t __v;                         \
134 		__v = atomic_fetch_sub_acq_rel(target, 1); \
135 		INSIST(__v > 0);                           \
136 		__v;                                       \
137 	})
138 #endif /* _MSC_VER */
139 
140 #define isc_refcount_decrementz(target)                               \
141 	do {                                                          \
142 		uint_fast32_t _refs = isc_refcount_decrement(target); \
143 		ISC_INSIST(_refs == 1);                               \
144 	} while (0)
145 
146 #define isc_refcount_decrement1(target)                               \
147 	do {                                                          \
148 		uint_fast32_t _refs = isc_refcount_decrement(target); \
149 		ISC_INSIST(_refs > 1);                                \
150 	} while (0)
151 
152 #define isc_refcount_decrement0(target)                               \
153 	do {                                                          \
154 		uint_fast32_t _refs = isc_refcount_decrement(target); \
155 		ISC_INSIST(_refs > 0);                                \
156 	} while (0)
157 
158 ISC_LANG_ENDDECLS
159