|
1 | 1 | /* This file implements atomic counters using c11 _Atomic, __atomic or __sync
|
2 | 2 | * macros if available, otherwise we will throw an error when compile.
|
3 | 3 | *
|
4 |
| - * The exported interface is composed of three macros: |
| 4 | + * The exported interface is composed of the following macros: |
5 | 5 | *
|
6 | 6 | * atomicIncr(var,count) -- Increment the atomic counter
|
7 | 7 | * atomicGetIncr(var,oldvalue_var,count) -- Get and increment the atomic counter
|
| 8 | + * atomicIncrGet(var,newvalue_var,count) -- Increment and get the atomic counter new value |
8 | 9 | * atomicDecr(var,count) -- Decrement the atomic counter
|
9 | 10 | * atomicGet(var,dstvar) -- Fetch the atomic counter value
|
10 | 11 | * atomicSet(var,value) -- Set the atomic counter value
|
11 | 12 | * atomicGetWithSync(var,value) -- 'atomicGet' with inter-thread synchronization
|
12 | 13 | * atomicSetWithSync(var,value) -- 'atomicSet' with inter-thread synchronization
|
13 |
| - * |
| 14 | + * |
| 15 | + * Atomic operations on flags. |
| 16 | + * Flag type can be int, long, long long or their unsigned counterparts. |
| 17 | + * The value of the flag can be 1 or 0. |
| 18 | + * |
| 19 | + * atomicFlagGetSet(var,oldvalue_var) -- Get and set the atomic counter value |
| 20 | + * |
| 21 | + * NOTE1: __atomic* and _Atomic implementations can be actually elaborated to support any value by changing the |
| 22 | + * hardcoded new value passed to __atomic_exchange* from 1 to @param count |
| 23 | + * i.e oldvalue_var = atomic_exchange_explicit(&var, count). |
| 24 | + * However, in order to be compatible with the __sync functions family, we can use only 0 and 1. |
| 25 | + * The only exchange alternative suggested by __sync is __sync_lock_test_and_set, |
| 26 | + * But as described by the gnu manual for __sync_lock_test_and_set(): |
| 27 | + * https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html |
| 28 | + * "A target may support reduced functionality here by which the only valid value to store is the immediate constant 1. The exact value |
| 29 | + * actually stored in *ptr is implementation defined." |
| 30 | + * Hence, we can't rely on it for a any value other than 1. |
| 31 | + * We eventually chose to implement this method with __sync_val_compare_and_swap since it satisfies functionality needed for atomicFlagGetSet |
| 32 | + * (if the flag was 0 -> set to 1, if it's already 1 -> do nothing, but the final result is that the flag is set), |
| 33 | + * and also it has a full barrier (__sync_lock_test_and_set has acquire barrier). |
| 34 | + * |
| 35 | + * NOTE2: Unlike other atomic type, which aren't guaranteed to be lock free, c11 atmoic_flag does. |
| 36 | + * To check whether a type is lock free, atomic_is_lock_free() can be used. |
| 37 | + * It can be considered to limit the flag type to atomic_flag to improve performance. |
| 38 | + * |
14 | 39 | * Never use return value from the macros, instead use the AtomicGetIncr()
|
15 | 40 | * if you need to get the current value and increment it atomically, like
|
16 | 41 | * in the following example:
|
|
93 | 118 | #define atomicGetIncr(var,oldvalue_var,count) do { \
|
94 | 119 | oldvalue_var = atomic_fetch_add_explicit(&var,(count),memory_order_relaxed); \
|
95 | 120 | } while(0)
|
| 121 | +#define atomicIncrGet(var, newvalue_var, count) \ |
| 122 | + newvalue_var = atomicIncr(var,count) + count |
96 | 123 | #define atomicDecr(var,count) atomic_fetch_sub_explicit(&var,(count),memory_order_relaxed)
|
97 | 124 | #define atomicGet(var,dstvar) do { \
|
98 | 125 | dstvar = atomic_load_explicit(&var,memory_order_relaxed); \
|
|
103 | 130 | } while(0)
|
104 | 131 | #define atomicSetWithSync(var,value) \
|
105 | 132 | atomic_store_explicit(&var,value,memory_order_seq_cst)
|
| 133 | +#define atomicFlagGetSet(var,oldvalue_var) \ |
| 134 | + oldvalue_var = atomic_exchange_explicit(&var,1,memory_order_relaxed) |
106 | 135 | #define REDIS_ATOMIC_API "c11-builtin"
|
107 | 136 |
|
108 | 137 | #elif !defined(__ATOMIC_VAR_FORCE_SYNC_MACROS) && \
|
|
111 | 140 | /* Implementation using __atomic macros. */
|
112 | 141 |
|
113 | 142 | #define atomicIncr(var,count) __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED)
|
| 143 | +#define atomicIncrGet(var, newvalue_var, count) \ |
| 144 | + newvalue_var = __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED) |
114 | 145 | #define atomicGetIncr(var,oldvalue_var,count) do { \
|
115 | 146 | oldvalue_var = __atomic_fetch_add(&var,(count),__ATOMIC_RELAXED); \
|
116 | 147 | } while(0)
|
|
124 | 155 | } while(0)
|
125 | 156 | #define atomicSetWithSync(var,value) \
|
126 | 157 | __atomic_store_n(&var,value,__ATOMIC_SEQ_CST)
|
| 158 | +#define atomicFlagGetSet(var,oldvalue_var) \ |
| 159 | + oldvalue_var = __atomic_exchange_n(&var,1,__ATOMIC_RELAXED) |
127 | 160 | #define REDIS_ATOMIC_API "atomic-builtin"
|
128 | 161 |
|
129 | 162 | #elif defined(HAVE_ATOMIC)
|
130 | 163 | /* Implementation using __sync macros. */
|
131 | 164 |
|
132 | 165 | #define atomicIncr(var,count) __sync_add_and_fetch(&var,(count))
|
| 166 | +#define atomicIncrGet(var, newvalue_var, count) \ |
| 167 | + newvalue_var = __sync_add_and_fetch(&var,(count)) |
133 | 168 | #define atomicGetIncr(var,oldvalue_var,count) do { \
|
134 | 169 | oldvalue_var = __sync_fetch_and_add(&var,(count)); \
|
135 | 170 | } while(0)
|
|
149 | 184 | ANNOTATE_HAPPENS_BEFORE(&var); \
|
150 | 185 | while(!__sync_bool_compare_and_swap(&var,var,value,__sync_synchronize)); \
|
151 | 186 | } while(0)
|
| 187 | +#define atomicFlagGetSet(var,oldvalue_var) \ |
| 188 | + oldvalue_var = __sync_val_compare_and_swap(&var,0,1) |
152 | 189 | #define REDIS_ATOMIC_API "sync-builtin"
|
153 | 190 |
|
154 | 191 | #else
|
|
0 commit comments