#include <isc/platform.h>
#include <isc/types.h>
-#if defined(ISC_PLATFORM_HAVESTDATOMIC)
#include <stdatomic.h>
-#else
-#include <isc/atomic.h>
-#endif
/*! \file isc/refcount.h
* \brief Implements a locked reference counter.
/*
* Sample implementations
*/
-#ifdef ISC_PLATFORM_USETHREADS
-#if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_INT_LOCK_FREE)) || defined(ISC_PLATFORM_HAVEXADD)
-#define ISC_REFCOUNT_HAVEATOMIC 1
-#if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_INT_LOCK_FREE))
-#define ISC_REFCOUNT_HAVESTDATOMIC 1
-#endif
typedef struct isc_refcount {
-#if defined(ISC_REFCOUNT_HAVESTDATOMIC)
atomic_int_fast32_t refs;
-#else
- isc_int32_t refs;
-#endif
} isc_refcount_t;
-#if defined(ISC_REFCOUNT_HAVESTDATOMIC)
+#define isc_refcount_init(rp, n) atomic_init(&(rp)->refs, n)
#define isc_refcount_current(rp) \
((unsigned int)(atomic_load_explicit(&(rp)->refs, \
*_tmp = prev - 1; \
} while (0)
-#else /* ISC_REFCOUNT_HAVESTDATOMIC */
-
-#define isc_refcount_init(rp, n) isc_atomic_store(&(rp)->refs, n)
-
-#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) \
- do { \
- unsigned int *_tmp = (unsigned int *)(tp); \
- isc_int32_t prev; \
- prev = isc_atomic_xadd(&(rp)->refs, 1); \
- if (_tmp != NULL) \
- *_tmp = prev + 1; \
- } while (0)
-
-#define isc_refcount_increment(rp, tp) \
- do { \
- unsigned int *_tmp = (unsigned int *)(tp); \
- isc_int32_t prev; \
- prev = isc_atomic_xadd(&(rp)->refs, 1); \
- ISC_REQUIRE(prev > 0); \
- if (_tmp != NULL) \
- *_tmp = prev + 1; \
- } while (0)
-
-#define isc_refcount_decrement(rp, tp) \
- do { \
- unsigned int *_tmp = (unsigned int *)(tp); \
- isc_int32_t prev; \
- prev = isc_atomic_xadd(&(rp)->refs, -1); \
- ISC_REQUIRE(prev > 0); \
- if (_tmp != NULL) \
- *_tmp = prev - 1; \
- } while (0)
-
-#endif /* ISC_REFCOUNT_HAVESTDATOMIC */
-
-#else /* ISC_PLATFORM_HAVEXADD */
-
-typedef struct isc_refcount {
- int refs;
- isc_mutex_t lock;
-} isc_refcount_t;
-
-#define isc_refcount_init(rp, n) \
- do { \
- REQUIRE(isc_mutex_init(&ref->lock) == ISC_R_SUCCESS); \
- (rp)->refs = n; \
- } while(0);
-
-/*% Destroys a reference counter. */
-#define isc_refcount_destroy(rp) \
- do { \
- isc_result_t _result; \
- ISC_REQUIRE((rp)->refs == 0); \
- _result = isc_mutex_destroy(&(rp)->lock); \
- ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
- } while (0)
-
-#define isc_refcount_current(rp) ((unsigned int)((rp)->refs))
-
-/*%
- * Increments the reference count, returning the new value in
- * 'tp' if it's not NULL.
- */
-#define isc_refcount_increment0(rp, tp) \
- do { \
- isc_result_t _result; \
- unsigned int *_tmp = (unsigned int *)(tp); \
- _result = isc_mutex_lock(&(rp)->lock); \
- ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
- ++((rp)->refs); \
- if (_tmp != NULL) \
- *_tmp = ((rp)->refs); \
- _result = isc_mutex_unlock(&(rp)->lock); \
- ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
- } while (0)
-
-#define isc_refcount_increment(rp, tp) \
- do { \
- isc_result_t _result; \
- unsigned int *_tmp = (unsigned int *)(tp); \
- _result = isc_mutex_lock(&(rp)->lock); \
- ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
- ISC_REQUIRE((rp)->refs > 0); \
- ++((rp)->refs); \
- if (_tmp != NULL) \
- *_tmp = ((rp)->refs); \
- _result = isc_mutex_unlock(&(rp)->lock); \
- ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
- } while (0)
-
-/*%
- * Decrements the reference count, returning the new value in 'tp'
- * if it's not NULL.
- */
-#define isc_refcount_decrement(rp, tp) \
- do { \
- isc_result_t _result; \
- unsigned int *_tmp = (unsigned int *)(tp); \
- _result = isc_mutex_lock(&(rp)->lock); \
- ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
- ISC_REQUIRE((rp)->refs > 0); \
- --((rp)->refs); \
- if (_tmp != NULL) \
- *_tmp = ((rp)->refs); \
- _result = isc_mutex_unlock(&(rp)->lock); \
- ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \
- } while (0)
-
-#endif /* (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_INT_LOCK_FREE)) || defined(ISC_PLATFORM_HAVEXADD) */
-#else /* ISC_PLATFORM_USETHREADS */
-
-typedef struct isc_refcount {
- int refs;
-} isc_refcount_t;
-
-#define isc_refcount_init(rp, n) ((rp)->refs = n)
-#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) \
- do { \
- unsigned int *_tmp = (unsigned int *)(tp); \
- int _n = ++(rp)->refs; \
- if (_tmp != NULL) \
- *_tmp = _n; \
- } while (0)
-
-#define isc_refcount_increment(rp, tp) \
- do { \
- unsigned int *_tmp = (unsigned int *)(tp); \
- int _n; \
- ISC_REQUIRE((rp)->refs > 0); \
- _n = ++(rp)->refs; \
- if (_tmp != NULL) \
- *_tmp = _n; \
- } while (0)
-
-#define isc_refcount_decrement(rp, tp) \
- do { \
- unsigned int *_tmp = (unsigned int *)(tp); \
- int _n; \
- ISC_REQUIRE((rp)->refs > 0); \
- _n = --(rp)->refs; \
- if (_tmp != NULL) \
- *_tmp = _n; \
- } while (0)
-
-#endif /* ISC_PLATFORM_USETHREADS */
-
ISC_LANG_ENDDECLS
#endif /* ISC_REFCOUNT_H */
#include <isc/stats.h>
#include <isc/util.h>
-#if defined(ISC_PLATFORM_HAVESTDATOMIC)
#include <stdatomic.h>
-#endif
#define ISC_STATS_MAGIC ISC_MAGIC('S', 't', 'a', 't')
#define ISC_STATS_VALID(x) ISC_MAGIC_VALID(x, ISC_STATS_MAGIC)
-/*%
- * Local macro confirming prescence of 64-bit
- * increment and store operations, just to make
- * the later macros simpler
- */
-#if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_LONG_LOCK_FREE)) || \
- (defined(ISC_PLATFORM_HAVEXADDQ) && defined(ISC_PLATFORM_HAVEATOMICSTOREQ))
-#define ISC_STATS_HAVEATOMICQ 1
-#if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_LONG_LOCK_FREE))
-#define ISC_STATS_HAVESTDATOMICQ 1
-#endif
-#else
-#define ISC_STATS_HAVEATOMICQ 0
-#endif
-
-/*%
- * Only lock the counters if 64-bit atomic operations are
- * not available but cheap atomic lock operations are.
- * On a modern 64-bit system this should never be the case.
- *
- * Normal locks are too expensive to be used whenever a counter
- * is updated.
- */
-#if !ISC_STATS_HAVEATOMICQ && defined(ISC_RWLOCK_HAVEATOMIC)
-#define ISC_STATS_LOCKCOUNTERS 1
-#else
-#define ISC_STATS_LOCKCOUNTERS 0
-#endif
-
-/*%
- * If 64-bit atomic operations are not available but
- * 32-bit operations are then split the counter into two,
- * using the atomic operations to try to ensure that any carry
- * from the low word is correctly carried into the high word.
- *
- * Otherwise, just rely on standard 64-bit data types
- * and operations
- */
-#if !ISC_STATS_HAVEATOMICQ && ((defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_INT_LOCK_FREE)) || defined(ISC_PLATFORM_HAVEXADD))
-#define ISC_STATS_USEMULTIFIELDS 1
-#if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_INT_LOCK_FREE))
-#define ISC_STATS_HAVESTDATOMIC 1
-#endif
-#else
-#define ISC_STATS_USEMULTIFIELDS 0
-#endif
-
-#if ISC_STATS_USEMULTIFIELDS
-typedef struct {
-#if defined(ISC_STATS_HAVESTDATOMIC)
- atomic_int_fast32_t hi;
- atomic_int_fast32_t lo;
-#else
- isc_uint32_t hi;
- isc_uint32_t lo;
-#endif
-} isc_stat_t;
-#else
-#if defined(ISC_STATS_HAVESTDATOMICQ)
typedef atomic_int_fast64_t isc_stat_t;
-#else
-typedef isc_uint64_t isc_stat_t;
-#endif
-#endif
struct isc_stats {
/*% Unlocked */
* Locked by counterlock or unlocked if efficient rwlock is not
* available.
*/
-#if ISC_STATS_LOCKCOUNTERS
- isc_rwlock_t counterlock;
-#endif
isc_stat_t *counters;
/*%
goto clean_counters;
}
-#if ISC_STATS_LOCKCOUNTERS
- result = isc_rwlock_init(&stats->counterlock, 0, 0);
- if (result != ISC_R_SUCCESS)
- goto clean_copiedcounters;
-#endif
-
stats->references = 1;
memset(stats->counters, 0, sizeof(isc_stat_t) * ncounters);
stats->mctx = NULL;
clean_counters:
isc_mem_put(mctx, stats->counters, sizeof(isc_stat_t) * ncounters);
-#if ISC_STATS_LOCKCOUNTERS
-clean_copiedcounters:
- isc_mem_put(mctx, stats->copiedcounters,
- sizeof(isc_stat_t) * ncounters);
-#endif
-
clean_mutex:
DESTROYLOCK(&stats->lock);
sizeof(isc_stat_t) * stats->ncounters);
UNLOCK(&stats->lock);
DESTROYLOCK(&stats->lock);
-#if ISC_STATS_LOCKCOUNTERS
- isc_rwlock_destroy(&stats->counterlock);
-#endif
isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats));
return;
}
static inline void
incrementcounter(isc_stats_t *stats, int counter) {
- isc_int32_t prev;
-
-#if ISC_STATS_LOCKCOUNTERS
- /*
- * We use a "read" lock to prevent other threads from reading the
- * counter while we "writing" a counter field. The write access itself
- * is protected by the atomic operation.
- */
- isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_read);
-#endif
-
-#if ISC_STATS_USEMULTIFIELDS
-#if defined(ISC_STATS_HAVESTDATOMIC)
- prev = atomic_fetch_add_explicit(&stats->counters[counter].lo, 1,
- memory_order_relaxed);
-#else
- prev = isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].lo, 1);
-#endif
- /*
- * If the lower 32-bit field overflows, increment the higher field.
- * Note that it's *theoretically* possible that the lower field
- * overlaps again before the higher field is incremented. It doesn't
- * matter, however, because we don't read the value until
- * isc_stats_copy() is called where the whole process is protected
- * by the write (exclusive) lock.
- */
- if (prev == (isc_int32_t)0xffffffff) {
-#if defined(ISC_STATS_HAVESTDATOMIC)
- atomic_fetch_add_explicit(&stats->counters[counter].hi, 1,
- memory_order_relaxed);
-#else
- isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].hi, 1);
-#endif
- }
-#elif ISC_STATS_HAVEATOMICQ
- UNUSED(prev);
-#if defined(ISC_STATS_HAVESTDATOMICQ)
atomic_fetch_add_explicit(&stats->counters[counter], 1,
memory_order_relaxed);
-#else
- isc_atomic_xaddq((isc_int64_t *)&stats->counters[counter], 1);
-#endif
-#else
- UNUSED(prev);
- stats->counters[counter]++;
-#endif
-
-#if ISC_STATS_LOCKCOUNTERS
- isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_read);
-#endif
}
static inline void
decrementcounter(isc_stats_t *stats, int counter) {
- isc_int32_t prev;
-
-#if ISC_STATS_LOCKCOUNTERS
- isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_read);
-#endif
-
-#if ISC_STATS_USEMULTIFIELDS
-#if defined(ISC_STATS_HAVESTDATOMIC)
- prev = atomic_fetch_sub_explicit(&stats->counters[counter].lo, 1,
- memory_order_relaxed);
-#else
- prev = isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].lo, -1);
-#endif
- if (prev == 0) {
-#if defined(ISC_STATS_HAVESTDATOMIC)
- atomic_fetch_sub_explicit(&stats->counters[counter].hi, 1,
- memory_order_relaxed);
-#else
- isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].hi,
- -1);
-#endif
- }
-#elif ISC_STATS_HAVEATOMICQ
- UNUSED(prev);
-#if defined(ISC_STATS_HAVESTDATOMICQ)
atomic_fetch_sub_explicit(&stats->counters[counter], 1,
memory_order_relaxed);
-#else
- isc_atomic_xaddq((isc_int64_t *)&stats->counters[counter], -1);
-#endif
-#else
- UNUSED(prev);
- stats->counters[counter]--;
-#endif
-
-#if ISC_STATS_LOCKCOUNTERS
- isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_read);
-#endif
}
static void
copy_counters(isc_stats_t *stats) {
int i;
-#if ISC_STATS_LOCKCOUNTERS
- /*
- * We use a "write" lock before "reading" the statistics counters as
- * an exclusive lock.
- */
- isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_write);
-#endif
-
for (i = 0; i < stats->ncounters; i++) {
-#if ISC_STATS_USEMULTIFIELDS
- stats->copiedcounters[i] =
- (isc_uint64_t)(stats->counters[i].hi) << 32 |
- stats->counters[i].lo;
-#elif ISC_STATS_HAVEATOMICQ
-#if defined(ISC_STATS_HAVESTDATOMICQ)
stats->copiedcounters[i] =
atomic_load_explicit(&stats->counters[i],
memory_order_relaxed);
-#else
- /* use xaddq(..., 0) as an atomic load */
- stats->copiedcounters[i] =
- (isc_uint64_t)isc_atomic_xaddq((isc_int64_t *)&stats->counters[i], 0);
-#endif
-#else
- stats->copiedcounters[i] = stats->counters[i];
-#endif
}
-
-#if ISC_STATS_LOCKCOUNTERS
- isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_write);
-#endif
}
isc_result_t
REQUIRE(ISC_STATS_VALID(stats));
REQUIRE(counter < stats->ncounters);
-#if ISC_STATS_LOCKCOUNTERS
- /*
- * We use a "write" lock before "reading" the statistics counters as
- * an exclusive lock.
- */
- isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_write);
-#endif
-
-#if ISC_STATS_USEMULTIFIELDS
- stats->counters[counter].hi = (isc_uint32_t)((val >> 32) & 0xffffffff);
- stats->counters[counter].lo = (isc_uint32_t)(val & 0xffffffff);
-#elif ISC_STATS_HAVEATOMICQ
-#if defined(ISC_STATS_HAVESTDATOMICQ)
atomic_store_explicit(&stats->counters[counter], val,
memory_order_relaxed);
-#else
- isc_atomic_storeq((isc_int64_t *)&stats->counters[counter], val);
-#endif
-#else
- stats->counters[counter] = val;
-#endif
-
-#if ISC_STATS_LOCKCOUNTERS
- isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_write);
-#endif
}