REFCOUNT: use only compare-and-exchange (#19411)
* use only compare-and-exchange for refcount * differentiate memory ordering when deleting or releasing
Costa Tsaousis committed
Jan 15, 2025 at 20:04 UTC
0bb4003a722e63981abdab4de4e9eabbefe4ec11
1 file changed
+32
-7
src/libnetdata/atomics/refcount.h
+32
-7
@@ -66,12 +66,21 @@ static inline bool WARNUNUSED refcount_acquire_with_trace(REFCOUNT *refcount, co
66
67
// returns the number of references remaining
68
static inline REFCOUNT refcount_release_with_trace(REFCOUNT *refcount, const char *func __maybe_unused) {
69
- REFCOUNT rc = refcount_decrement(refcount);
69
+ REFCOUNT expected, desired;
70
71
- if(!REFCOUNT_VALID(rc))
72
- fatal("REFCOUNT %d is invalid (detected at %s(), called from %s())", rc, __FUNCTION__, func);
71
+ do {
72
+ expected = refcount_references(refcount);
73
+ if(!REFCOUNT_VALID(expected))
74
+ fatal("REFCOUNT %d is invalid (detected at %s(), called from %s())", expected, __FUNCTION__, func);
75
+
76
+// // the following is a valid case when using refcount_acquire_for_deletion_and_wait_with_trace()
77
+// if(expected <= 0)
78
+// fatal("REFCOUNT cannot release a refcount of %d (detected at %s(), called from %s())", expected, __FUNCTION__, func);
79
+
80
+ desired = expected - 1;
81
+ } while(!__atomic_compare_exchange_n(refcount, &expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
82
74
- return rc;
83
+ return desired;
84
}
85
86
// returns true when the item can be deleted, false when the item is currently referenced
@@ -89,10 +98,26 @@ static inline bool WARNUNUSED refcount_acquire_for_deletion_with_trace(REFCOUNT
98
}
99
100
static inline bool WARNUNUSED refcount_release_and_acquire_for_deletion_with_trace(REFCOUNT *refcount, const char *func __maybe_unused) {
92
- if(refcount_release_with_trace(refcount, func) == 0)
93
- return refcount_acquire_for_deletion_with_trace(refcount, func);
101
+ REFCOUNT expected, desired;
102
95
- return false;
103
+ do {
104
+ expected = refcount_references(refcount);
105
+ if (!REFCOUNT_VALID(expected))
106
+ fatal("REFCOUNT %d is invalid (detected at %s(), called from %s())", expected, __FUNCTION__, func);
107
+
108
+ if (expected == 1) {
109
+ // we can get it for deletion
110
+ desired = REFCOUNT_DELETED;
111
+ if (__atomic_compare_exchange_n(refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
112
+ return true;
113
+ }
114
+ else {
115
+ // we can only release it
116
+ desired = expected - 1;
117
+ if (__atomic_compare_exchange_n(refcount, &expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED))
118
+ return false;
119
+ }
120
+ } while (true);
121
}
122
123
// this sleeps for 1 nanosecond (posix systems), or Sleep(0) on Windows