* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
#include <isc/assertions.h>
#include <isc/platform.h>
#if defined(ISC_PLATFORM_HAVESTDATOMIC)
#if defined (__cplusplus)
#include <isc/stdatomic.h>
* \brief Implements a locked reference counter.
* These functions may actually be
* implemented using macros, and implementations of these macros are below.
* The isc_refcount_t type should not be accessed directly, as its contents
* depend on the implementation.
* isc_refcount_init(isc_refcount_t *ref, unsigned int n);
* Initialize the reference counter. There will be 'n' initial references.
* isc_refcount_destroy(isc_refcount_t *ref);
* Destroys a reference counter.
* The number of references is 0.
* isc_refcount_increment(isc_refcount_t *ref, unsigned int *targetp);
* isc_refcount_increment0(isc_refcount_t *ref, unsigned int *targetp);
* Increments the reference count, returning the new value in targetp if it's
* not NULL. The reference counter typically begins with the initial counter
* of 1, and will be destroyed once the counter reaches 0. Thus,
* isc_refcount_increment() additionally requires the previous counter be
* larger than 0 so that an error which violates the usage can be easily
* caught. isc_refcount_increment0() does not have this restriction.
* isc_refcount_decrement(isc_refcount_t *ref, unsigned int *targetp);
* Decrements the reference count, returning the new value in targetp if it's
#ifdef ISC_PLATFORM_USETHREADS
#if defined(ISC_PLATFORM_HAVESTDATOMIC)
# define ISC_REFCOUNT_HAVEATOMIC 1
# define ISC_REFCOUNT_HAVESTDATOMIC 1
#else /* defined(ISC_PLATFORM_HAVESTDATOMIC) */
# if defined(ISC_PLATFORM_HAVEXADD)
# define ISC_REFCOUNT_HAVEATOMIC 1
# endif /* defined(ISC_PLATFORM_HAVEXADD */
#endif /* !defined(ISC_REFCOUNT_HAVEATOMIC) */
#if defined(ISC_REFCOUNT_HAVEATOMIC)
typedef struct isc_refcount {
#if defined(ISC_REFCOUNT_HAVESTDATOMIC)
atomic_int_fast32_t refs;
#if defined(ISC_REFCOUNT_HAVESTDATOMIC)
#define isc_refcount_current(rp) \
((unsigned int)(atomic_load_explicit(&(rp)->refs, \
#define isc_refcount_destroy(rp) ISC_REQUIRE(isc_refcount_current(rp) == 0)
#define isc_refcount_increment0(rp, tp) \
unsigned int *_tmp = (unsigned int *)(tp); \
prev = atomic_fetch_add_explicit \
(&(rp)->refs, 1, memory_order_relaxed); \
#define isc_refcount_increment(rp, tp) \
unsigned int *_tmp = (unsigned int *)(tp); \
prev = atomic_fetch_add_explicit \
(&(rp)->refs, 1, memory_order_relaxed); \
#define isc_refcount_decrement(rp, tp) \
unsigned int *_tmp = (unsigned int *)(tp); \
prev = atomic_fetch_sub_explicit \
(&(rp)->refs, 1, memory_order_acq_rel); \
#else /* defined(ISC_REFCOUNT_HAVESTDATOMIC) */
#define isc_refcount_current(rp) \
((unsigned int)(isc_atomic_xadd(&(rp)->refs, 0)))
#define isc_refcount_destroy(rp) ISC_REQUIRE(isc_refcount_current(rp) == 0)
#define isc_refcount_increment0(rp, tp) \
unsigned int *_tmp = (unsigned int *)(tp); \
prev = isc_atomic_xadd(&(rp)->refs, 1); \
#define isc_refcount_increment(rp, tp) \
unsigned int *_tmp = (unsigned int *)(tp); \
prev = isc_atomic_xadd(&(rp)->refs, 1); \
#define isc_refcount_decrement(rp, tp) \
unsigned int *_tmp = (unsigned int *)(tp); \
prev = isc_atomic_xadd(&(rp)->refs, -1); \
#endif /* defined(ISC_REFCOUNT_HAVESTDATOMIC) */
#else /* defined(ISC_REFCOUNT_HAVEATOMIC) */
typedef struct isc_refcount {
/*% Destroys a reference counter. */
#define isc_refcount_destroy(rp) \
ISC_REQUIRE((rp)->refs == 0); \
_result = isc_mutex_destroy(&(rp)->lock); \
ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
unsigned int isc_refcount_current(isc_refcount_t *rp);
* Increments the reference count, returning the new value in
#define isc_refcount_increment0(rp, tp) \
unsigned int *_tmp = (unsigned int *)(tp); \
_result = isc_mutex_lock(&(rp)->lock); \
ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
_result = isc_mutex_unlock(&(rp)->lock); \
ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
#define isc_refcount_increment(rp, tp) \
unsigned int *_tmp = (unsigned int *)(tp); \
_result = isc_mutex_lock(&(rp)->lock); \
ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
ISC_REQUIRE((rp)->refs > 0); \
_result = isc_mutex_unlock(&(rp)->lock); \
ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
* Decrements the reference count, returning the new value in 'tp'
#define isc_refcount_decrement(rp, tp) \
unsigned int *_tmp = (unsigned int *)(tp); \
_result = isc_mutex_lock(&(rp)->lock); \
ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
ISC_REQUIRE((rp)->refs > 0); \
_result = isc_mutex_unlock(&(rp)->lock); \
ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
#endif /* defined(ISC_REFCOUNT_ATOMIC) */
#else /* ISC_PLATFORM_USETHREADS */
typedef struct isc_refcount {
#define isc_refcount_destroy(rp) ISC_REQUIRE((rp)->refs == 0)
#define isc_refcount_current(rp) ((unsigned int)((rp)->refs))
#define isc_refcount_increment0(rp, tp) \
unsigned int *_tmp = (unsigned int *)(tp); \
#define isc_refcount_increment(rp, tp) \
unsigned int *_tmp = (unsigned int *)(tp); \
ISC_REQUIRE((rp)->refs > 0); \
#define isc_refcount_decrement(rp, tp) \
unsigned int *_tmp = (unsigned int *)(tp); \
ISC_REQUIRE((rp)->refs > 0); \
#endif /* ISC_PLATFORM_USETHREADS */
isc_refcount_init(isc_refcount_t *ref, unsigned int n);
#endif /* ISC_REFCOUNT_H */