@cryptotaxi247 / netdata-1 / commits / 0bb4003a7

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