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