master
h 166 lines 6.56 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #ifndef NETDATA_REFCOUNT_H
4 #define NETDATA_REFCOUNT_H
5
6 #include "libnetdata/common.h"
7 #include <inttypes.h>
8
9 typedef int32_t REFCOUNT;
10
11 // the max number of references supported
12 // we use this to prevent overflowing the reference counter
13 #define REFCOUNT_MAX (1 * 1000 * 1000 * 1000)
14
15 // We set REFCOUNT_DELETED to a big negative,
16 // but to a value we can easily recognize while debugging.
17 #define REFCOUNT_DELETED (-2 * 1000 * 1000 * 1000)
18
19 // The error is a negative number, so that refcount > 0 is still
20 // good for checking if an acquired succeeded
21 #define REFCOUNT_ERROR INT32_MIN
22
23 /*
24 * When debugging:
25 *
26 * 1. refcount 0 to 1 billion => the object is referenced
27 * 2. refcount -1 billion to -1 => double releases or corruption
28 * 2. refcount -2 billion to -1 billion => marked for deletion, with active references
29 * (this happens when you use refcount_acquire_for_deletion_and_wait())
30 * 4. refcount outside -2 billion to 1 billion => memory corruption
31 */
32
33 #define refcount_references(refcount) __atomic_load_n(refcount, __ATOMIC_RELAXED)
34 #define refcount_increment(refcount) __atomic_add_fetch(refcount, 1, __ATOMIC_ACQUIRE)
35 #define refcount_decrement(refcount) __atomic_sub_fetch(refcount, 1, __ATOMIC_RELEASE)
36 #define REFCOUNT_ACQUIRED(refcount) (refcount > 0)
37
38 #define REFCOUNT_VALID(refcount) \
39 (((refcount) >= 0 && (refcount) <= REFCOUNT_MAX) || \
40 ((refcount) >= REFCOUNT_DELETED && (refcount) <= -REFCOUNT_MAX))
41
42 // returns the non-usable refcount found when it fails, the final refcount when it succeeds
43 ALWAYS_INLINE WARNUNUSED
44 static REFCOUNT refcount_acquire_advanced_with_trace(REFCOUNT *refcount, const char *func __maybe_unused) {
45 REFCOUNT expected = refcount_references(refcount);
46 REFCOUNT desired;
47
48 do {
49 if(!REFCOUNT_VALID(expected))
50 fatal("REFCOUNT %d is invalid (detected at %s(), called from %s())", expected, __FUNCTION__, func);
51
52 if(expected >= REFCOUNT_MAX)
53 return REFCOUNT_ERROR;
54
55 if(expected < 0)
56 return expected;
57
58 desired = expected + 1;
59 } while(!__atomic_compare_exchange_n(refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
60
61 return desired;
62 }
63
64 ALWAYS_INLINE WARNUNUSED
65 static bool refcount_acquire_with_trace(REFCOUNT *refcount, const char *func) {
66 return REFCOUNT_ACQUIRED(refcount_acquire_advanced_with_trace(refcount, func));
67 }
68
69 // returns the number of references remaining
70 ALWAYS_INLINE
71 static REFCOUNT refcount_release_with_trace(REFCOUNT *refcount, const char *func __maybe_unused) {
72 REFCOUNT expected, desired;
73
74 do {
75 expected = refcount_references(refcount);
76 if(!REFCOUNT_VALID(expected))
77 fatal("REFCOUNT %d is invalid (detected at %s(), called from %s())", expected, __FUNCTION__, func);
78
79 // // the following is a valid case when using refcount_acquire_for_deletion_and_wait_with_trace()
80 // if(expected <= 0)
81 // fatal("REFCOUNT cannot release a refcount of %d (detected at %s(), called from %s())", expected, __FUNCTION__, func);
82
83 desired = expected - 1;
84 } while(!__atomic_compare_exchange_n(refcount, &expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
85
86 return desired;
87 }
88
89 // returns true when the item can be deleted, false when the item is currently referenced
90 ALWAYS_INLINE WARNUNUSED
91 static bool refcount_acquire_for_deletion_with_trace(REFCOUNT *refcount, const char *func __maybe_unused) {
92 REFCOUNT expected = 0;
93 REFCOUNT desired = REFCOUNT_DELETED;
94
95 if(__atomic_compare_exchange_n(refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
96 return true;
97
98 if(!REFCOUNT_VALID(expected))
99 fatal("REFCOUNT %d is invalid (detected at %s(), called from %s())", expected, __FUNCTION__, func);
100
101 return false;
102 }
103
104 ALWAYS_INLINE WARNUNUSED
105 static REFCOUNT refcount_release_and_acquire_for_deletion_advanced_with_trace(REFCOUNT *refcount, const char *func __maybe_unused) {
106 REFCOUNT expected, desired;
107
108 do {
109 expected = refcount_references(refcount);
110 if (!REFCOUNT_VALID(expected))
111 fatal("REFCOUNT %d is invalid (detected at %s(), called from %s())", expected, __FUNCTION__, func);
112
113 if (expected == 1) {
114 // we can get it for deletion
115 desired = REFCOUNT_DELETED;
116 if (__atomic_compare_exchange_n(refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
117 return desired;
118 }
119 else {
120 // we can only release it
121 desired = expected - 1;
122 if (__atomic_compare_exchange_n(refcount, &expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED))
123 return desired;
124 }
125 } while (true);
126 }
127
128 ALWAYS_INLINE WARNUNUSED
129 static bool refcount_release_and_acquire_for_deletion_with_trace(REFCOUNT *refcount, const char *func __maybe_unused) {
130 return refcount_release_and_acquire_for_deletion_advanced_with_trace(refcount, func) == REFCOUNT_DELETED;
131 }
132
133 // this sleeps for 1 nanosecond (posix systems), or Sleep(0) on Windows
134 void tinysleep(void);
135
136 ALWAYS_INLINE
137 static bool refcount_acquire_for_deletion_and_wait_with_trace(REFCOUNT *refcount, const char *func) {
138 REFCOUNT expected = refcount_references(refcount);
139 REFCOUNT desired;
140
141 do {
142 if(!REFCOUNT_VALID(expected))
143 fatal("REFCOUNT %d is invalid (detected at %s(), called from %s())", expected, __FUNCTION__, func);
144
145 if(expected < 0)
146 return false;
147
148 desired = REFCOUNT_DELETED + expected;
149 } while(!__atomic_compare_exchange_n(refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
150
151 while(__atomic_load_n(refcount, __ATOMIC_ACQUIRE) != REFCOUNT_DELETED) {
152 tinysleep();
153 }
154
155 return true;
156 }
157
158 #define refcount_acquire_advanced(refcount) refcount_acquire_advanced_with_trace(refcount, __FUNCTION__ )
159 #define refcount_acquire(refcount) refcount_acquire_with_trace(refcount, __FUNCTION__)
160 #define refcount_release(refcount) refcount_release_with_trace(refcount, __FUNCTION__)
161 #define refcount_acquire_for_deletion(refcount) refcount_acquire_for_deletion_with_trace(refcount, __FUNCTION__)
162 #define refcount_release_and_acquire_for_deletion(refcount) refcount_release_and_acquire_for_deletion_with_trace(refcount, __FUNCTION__)
163 #define refcount_release_and_acquire_for_deletion_advanced(refcount) refcount_release_and_acquire_for_deletion_advanced_with_trace(refcount, __FUNCTION__)
164 #define refcount_acquire_for_deletion_and_wait(refcount) refcount_acquire_for_deletion_and_wait_with_trace(refcount, __FUNCTION__)
165
166 #endif //NETDATA_REFCOUNT_H