Commit | Line | Data |
---|---|---|
d3d3857f MJ |
1 | // SPDX-FileCopyrightText: 2009 Novell Inc. |
2 | // SPDX-FileCopyrightText: 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
3 | // | |
4 | // SPDX-License-Identifier: LGPL-2.1-only | |
5 | ||
453629a9 MD |
6 | #ifndef _URCU_REF_H |
7 | #define _URCU_REF_H | |
8 | ||
9 | /* | |
10 | * Userspace RCU - Reference counting | |
11 | * | |
453629a9 | 12 | * Author: Jan Blunck <jblunck@suse.de> |
453629a9 MD |
13 | */ |
14 | ||
cd21955a | 15 | #include <stdbool.h> |
7ce99d02 MD |
16 | #include <limits.h> |
17 | #include <stdlib.h> | |
01477510 | 18 | #include <urcu/assert.h> |
a2e7bf9c | 19 | #include <urcu/uatomic.h> |
453629a9 MD |
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 | ||
8b4b5a1a MD |
35 | static inline bool __attribute__((warn_unused_result)) |
36 | urcu_ref_get_safe(struct urcu_ref *ref) | |
453629a9 | 37 | { |
7ce99d02 MD |
38 | long old, _new, res; |
39 | ||
40 | old = uatomic_read(&ref->refcount); | |
41 | for (;;) { | |
42 | if (old == LONG_MAX) { | |
8b4b5a1a | 43 | return false; /* Failure. */ |
7ce99d02 MD |
44 | } |
45 | _new = old + 1; | |
46 | res = uatomic_cmpxchg(&ref->refcount, old, _new); | |
47 | if (res == old) { | |
8b4b5a1a | 48 | return true; /* Success. */ |
7ce99d02 MD |
49 | } |
50 | old = res; | |
51 | } | |
453629a9 MD |
52 | } |
53 | ||
8b4b5a1a MD |
54 | static inline void urcu_ref_get(struct urcu_ref *ref) |
55 | { | |
56 | if (!urcu_ref_get_safe(ref)) | |
57 | abort(); | |
58 | } | |
59 | ||
453629a9 MD |
60 | static inline void urcu_ref_put(struct urcu_ref *ref, |
61 | void (*release)(struct urcu_ref *)) | |
62 | { | |
bf27322d | 63 | long res = uatomic_sub_return(&ref->refcount, 1); |
01477510 | 64 | urcu_posix_assert(res >= 0); |
bf27322d | 65 | if (res == 0) |
453629a9 MD |
66 | release(ref); |
67 | } | |
68 | ||
cd21955a MD |
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 | |
7ce99d02 MD |
76 | * count. False is also returned in case incrementing the refcount would |
77 | * result in an overflow. | |
cd21955a MD |
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 (;;) { | |
7ce99d02 | 85 | if (old == 0 || old == LONG_MAX) |
cd21955a MD |
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 | ||
453629a9 | 96 | #endif /* _URCU_REF_H */ |