1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
|
// SPDX-FileCopyrightText: 2009 Novell Inc.
// SPDX-FileCopyrightText: 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
//
// SPDX-License-Identifier: LGPL-2.1-only
#ifndef _URCU_REF_H
#define _URCU_REF_H
/*
* Userspace RCU - Reference counting
*
* Author: Jan Blunck <jblunck@suse.de>
*/
#include <stdbool.h>
#include <limits.h>
#include <stdlib.h>
#include <urcu/assert.h>
#include <urcu/uatomic.h>
struct urcu_ref {
long refcount; /* ATOMIC */
};
static inline void urcu_ref_set(struct urcu_ref *ref, long val)
{
uatomic_store(&ref->refcount, val);
}
static inline void urcu_ref_init(struct urcu_ref *ref)
{
urcu_ref_set(ref, 1);
}
static inline bool __attribute__((__warn_unused_result__))
urcu_ref_get_safe(struct urcu_ref *ref)
{
long old, _new, res;
old = uatomic_load(&ref->refcount);
for (;;) {
if (old == LONG_MAX) {
return false; /* Failure. */
}
_new = old + 1;
res = uatomic_cmpxchg(&ref->refcount, old, _new);
if (res == old) {
return true; /* Success. */
}
old = res;
}
}
static inline void urcu_ref_get(struct urcu_ref *ref)
{
if (!urcu_ref_get_safe(ref))
abort();
}
static inline void urcu_ref_put(struct urcu_ref *ref,
void (*release)(struct urcu_ref *))
{
long res = uatomic_sub_return(&ref->refcount, 1);
urcu_posix_assert(res >= 0);
if (res == 0)
release(ref);
}
/*
* urcu_ref_get_unless_zero
*
* Allows getting a reference atomically if the reference count is not
* zero. Returns true if the reference is taken, false otherwise. This
* needs to be used in conjunction with another synchronization
* technique (e.g. RCU or mutex) to ensure existence of the reference
* count. False is also returned in case incrementing the refcount would
* result in an overflow.
*/
static inline bool urcu_ref_get_unless_zero(struct urcu_ref *ref)
{
long old, _new, res;
old = uatomic_load(&ref->refcount);
for (;;) {
if (old == 0 || old == LONG_MAX)
return false; /* Failure. */
_new = old + 1;
res = uatomic_cmpxchg(&ref->refcount, old, _new);
if (res == old) {
return true; /* Success. */
}
old = res;
}
}
#endif /* _URCU_REF_H */
|