1*c0b5d9fbSchristos /*	$NetBSD: refcount.h,v 1.6 2022/09/23 12:15:33 christos Exp $	*/
2e2b1b9c0Schristos 
3e2b1b9c0Schristos /*
4e2b1b9c0Schristos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5e2b1b9c0Schristos  *
6*c0b5d9fbSchristos  * SPDX-License-Identifier: MPL-2.0
7*c0b5d9fbSchristos  *
8e2b1b9c0Schristos  * This Source Code Form is subject to the terms of the Mozilla Public
9e2b1b9c0Schristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
1073584a28Schristos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11e2b1b9c0Schristos  *
12e2b1b9c0Schristos  * See the COPYRIGHT file distributed with this work for additional
13e2b1b9c0Schristos  * information regarding copyright ownership.
14e2b1b9c0Schristos  */
15e2b1b9c0Schristos 
16f2e20987Schristos #pragma once
17e2b1b9c0Schristos 
18f2e20987Schristos #include <inttypes.h>
19e2b1b9c0Schristos 
20e2b1b9c0Schristos #include <isc/assertions.h>
21e2b1b9c0Schristos #include <isc/atomic.h>
22e2b1b9c0Schristos #include <isc/error.h>
23e2b1b9c0Schristos #include <isc/lang.h>
24e2b1b9c0Schristos #include <isc/mutex.h>
25e2b1b9c0Schristos #include <isc/platform.h>
26e2b1b9c0Schristos #include <isc/types.h>
27e2b1b9c0Schristos 
28e2b1b9c0Schristos /*! \file isc/refcount.h
29e2b1b9c0Schristos  * \brief Implements a locked reference counter.
30e2b1b9c0Schristos  *
31f2e20987Schristos  * These macros uses C11(-like) atomic functions to implement reference
32f2e20987Schristos  * counting.  The isc_refcount_t type must not be accessed directly.
33e2b1b9c0Schristos  */
34e2b1b9c0Schristos 
35e2b1b9c0Schristos ISC_LANG_BEGINDECLS
36e2b1b9c0Schristos 
37f2e20987Schristos typedef atomic_uint_fast32_t isc_refcount_t;
38f2e20987Schristos 
39f2e20987Schristos /** \def isc_refcount_init(ref, n)
40f2e20987Schristos  *  \brief Initialize the reference counter.
41f2e20987Schristos  *  \param[in] ref pointer to reference counter.
42f2e20987Schristos  *  \param[in] n an initial number of references.
43f2e20987Schristos  *  \return nothing.
44f2e20987Schristos  *
45f2e20987Schristos  *  \warning No memory barrier are being imposed here.
46f2e20987Schristos  */
479742fdb4Schristos #define isc_refcount_init(target, value) atomic_init(target, value)
48f2e20987Schristos 
49f2e20987Schristos /** \def isc_refcount_current(ref)
50f2e20987Schristos  *  \brief Returns current number of references.
51f2e20987Schristos  *  \param[in] ref pointer to reference counter.
52f2e20987Schristos  *  \returns current value of reference counter.
53f2e20987Schristos  *
549742fdb4Schristos  *   Undo implicit promotion to 64 bits in our Windows implementation of
55f2e20987Schristos  *   atomic_load_explicit() by casting to uint_fast32_t.
56e2b1b9c0Schristos  */
57e2b1b9c0Schristos 
589742fdb4Schristos #define isc_refcount_current(target) (uint_fast32_t) atomic_load_acquire(target)
59f2e20987Schristos 
60f2e20987Schristos /** \def isc_refcount_destroy(ref)
61f2e20987Schristos  *  \brief a destructor that makes sure that all references were cleared.
62f2e20987Schristos  *  \param[in] ref pointer to reference counter.
63f2e20987Schristos  *  \returns nothing.
64e2b1b9c0Schristos  */
65f2e20987Schristos #define isc_refcount_destroy(target) \
66f2e20987Schristos 	ISC_REQUIRE(isc_refcount_current(target) == 0)
67e2b1b9c0Schristos 
68f2e20987Schristos /** \def isc_refcount_increment0(ref)
69f2e20987Schristos  *  \brief increases reference counter by 1.
70f2e20987Schristos  *  \param[in] ref pointer to reference counter.
71f2e20987Schristos  *  \returns previous value of reference counter.
72e2b1b9c0Schristos  */
739742fdb4Schristos #if _MSC_VER
749742fdb4Schristos static inline uint_fast32_t
isc_refcount_increment0(isc_refcount_t * target)759742fdb4Schristos isc_refcount_increment0(isc_refcount_t *target) {
769742fdb4Schristos 	uint_fast32_t __v;
779742fdb4Schristos 	__v = (uint_fast32_t)atomic_fetch_add_relaxed(target, 1);
789742fdb4Schristos 	INSIST(__v < UINT32_MAX);
799742fdb4Schristos 	return (__v);
809742fdb4Schristos }
819742fdb4Schristos #else /* _MSC_VER */
82f2e20987Schristos #define isc_refcount_increment0(target)                    \
839742fdb4Schristos 	({                                                 \
849742fdb4Schristos 		/* cppcheck-suppress shadowVariable */     \
859742fdb4Schristos 		uint_fast32_t __v;                         \
869742fdb4Schristos 		__v = atomic_fetch_add_relaxed(target, 1); \
879742fdb4Schristos 		INSIST(__v < UINT32_MAX);                  \
889742fdb4Schristos 		__v;                                       \
899742fdb4Schristos 	})
909742fdb4Schristos #endif /* _MSC_VER */
91e2b1b9c0Schristos 
92f2e20987Schristos /** \def isc_refcount_increment(ref)
93f2e20987Schristos  *  \brief increases reference counter by 1.
94f2e20987Schristos  *  \param[in] ref pointer to reference counter.
95f2e20987Schristos  *  \returns previous value of reference counter.
96e2b1b9c0Schristos  */
979742fdb4Schristos #if _MSC_VER
989742fdb4Schristos static inline uint_fast32_t
isc_refcount_increment(isc_refcount_t * target)999742fdb4Schristos isc_refcount_increment(isc_refcount_t *target) {
1009742fdb4Schristos 	uint_fast32_t __v;
1019742fdb4Schristos 	__v = (uint_fast32_t)atomic_fetch_add_relaxed(target, 1);
1029742fdb4Schristos 	INSIST(__v > 0 && __v < UINT32_MAX);
1039742fdb4Schristos 	return (__v);
1049742fdb4Schristos }
1059742fdb4Schristos #else /* _MSC_VER */
106f2e20987Schristos #define isc_refcount_increment(target)                     \
1079742fdb4Schristos 	({                                                 \
1089742fdb4Schristos 		/* cppcheck-suppress shadowVariable */     \
1099742fdb4Schristos 		uint_fast32_t __v;                         \
1109742fdb4Schristos 		__v = atomic_fetch_add_relaxed(target, 1); \
1119742fdb4Schristos 		INSIST(__v > 0 && __v < UINT32_MAX);       \
1129742fdb4Schristos 		__v;                                       \
1139742fdb4Schristos 	})
1149742fdb4Schristos #endif /* _MSC_VER */
115e2b1b9c0Schristos 
116f2e20987Schristos /** \def isc_refcount_decrement(ref)
117f2e20987Schristos  *  \brief decreases reference counter by 1.
118f2e20987Schristos  *  \param[in] ref pointer to reference counter.
119f2e20987Schristos  *  \returns previous value of reference counter.
120e2b1b9c0Schristos  */
1219742fdb4Schristos #if _MSC_VER
1229742fdb4Schristos static inline uint_fast32_t
isc_refcount_decrement(isc_refcount_t * target)1239742fdb4Schristos isc_refcount_decrement(isc_refcount_t *target) {
1249742fdb4Schristos 	uint_fast32_t __v;
12573584a28Schristos 	__v = (uint_fast32_t)atomic_fetch_sub_acq_rel(target, 1);
1269742fdb4Schristos 	INSIST(__v > 0);
1279742fdb4Schristos 	return (__v);
1289742fdb4Schristos }
1299742fdb4Schristos #else /* _MSC_VER */
130f2e20987Schristos #define isc_refcount_decrement(target)                     \
1319742fdb4Schristos 	({                                                 \
1329742fdb4Schristos 		/* cppcheck-suppress shadowVariable */     \
1339742fdb4Schristos 		uint_fast32_t __v;                         \
13473584a28Schristos 		__v = atomic_fetch_sub_acq_rel(target, 1); \
1359742fdb4Schristos 		INSIST(__v > 0);                           \
1369742fdb4Schristos 		__v;                                       \
1379742fdb4Schristos 	})
1389742fdb4Schristos #endif /* _MSC_VER */
139e2b1b9c0Schristos 
14073584a28Schristos #define isc_refcount_decrementz(target)                               \
14173584a28Schristos 	do {                                                          \
14273584a28Schristos 		uint_fast32_t _refs = isc_refcount_decrement(target); \
14373584a28Schristos 		ISC_INSIST(_refs == 1);                               \
14473584a28Schristos 	} while (0)
14573584a28Schristos 
14673584a28Schristos #define isc_refcount_decrement1(target)                               \
14773584a28Schristos 	do {                                                          \
14873584a28Schristos 		uint_fast32_t _refs = isc_refcount_decrement(target); \
14973584a28Schristos 		ISC_INSIST(_refs > 1);                                \
15073584a28Schristos 	} while (0)
15173584a28Schristos 
15273584a28Schristos #define isc_refcount_decrement0(target)                               \
15373584a28Schristos 	do {                                                          \
15473584a28Schristos 		uint_fast32_t _refs = isc_refcount_decrement(target); \
15573584a28Schristos 		ISC_INSIST(_refs > 0);                                \
15673584a28Schristos 	} while (0)
15773584a28Schristos 
158e2b1b9c0Schristos ISC_LANG_ENDDECLS
159