xref: /nrf52832-nimble/packages/NimBLE-latest/nimble/host/mesh/src/atomic.h (revision 042d53a763ad75cb1465103098bb88c245d95138)
1*042d53a7SEvalZero /* atomic operations */
2*042d53a7SEvalZero 
3*042d53a7SEvalZero /*
4*042d53a7SEvalZero  * Copyright (c) 1997-2015, Wind River Systems, Inc.
5*042d53a7SEvalZero  *
6*042d53a7SEvalZero  * SPDX-License-Identifier: Apache-2.0
7*042d53a7SEvalZero  */
8*042d53a7SEvalZero 
9*042d53a7SEvalZero #ifndef __ATOMIC_H__
10*042d53a7SEvalZero #define __ATOMIC_H__
11*042d53a7SEvalZero 
12*042d53a7SEvalZero #ifdef __cplusplus
13*042d53a7SEvalZero extern "C"
14*042d53a7SEvalZero {
15*042d53a7SEvalZero #endif
16*042d53a7SEvalZero 
17*042d53a7SEvalZero typedef int atomic_t;
18*042d53a7SEvalZero typedef atomic_t atomic_val_t;
19*042d53a7SEvalZero 
20*042d53a7SEvalZero /**
21*042d53a7SEvalZero  * @defgroup atomic_apis Atomic Services APIs
22*042d53a7SEvalZero  * @ingroup kernel_apis
23*042d53a7SEvalZero  * @{
24*042d53a7SEvalZero  */
25*042d53a7SEvalZero 
26*042d53a7SEvalZero /**
27*042d53a7SEvalZero  * @brief Atomic compare-and-set.
28*042d53a7SEvalZero  *
29*042d53a7SEvalZero  * This routine performs an atomic compare-and-set on @a target. If the current
30*042d53a7SEvalZero  * value of @a target equals @a old_value, @a target is set to @a new_value.
31*042d53a7SEvalZero  * If the current value of @a target does not equal @a old_value, @a target
32*042d53a7SEvalZero  * is left unchanged.
33*042d53a7SEvalZero  *
34*042d53a7SEvalZero  * @param target Address of atomic variable.
35*042d53a7SEvalZero  * @param old_value Original value to compare against.
36*042d53a7SEvalZero  * @param new_value New value to store.
37*042d53a7SEvalZero  * @return 1 if @a new_value is written, 0 otherwise.
38*042d53a7SEvalZero  */
atomic_cas(atomic_t * target,atomic_val_t old_value,atomic_val_t new_value)39*042d53a7SEvalZero static inline int atomic_cas(atomic_t *target, atomic_val_t old_value,
40*042d53a7SEvalZero         atomic_val_t new_value)
41*042d53a7SEvalZero {
42*042d53a7SEvalZero     return __atomic_compare_exchange_n(target, &old_value, new_value,
43*042d53a7SEvalZero             0, __ATOMIC_SEQ_CST,
44*042d53a7SEvalZero             __ATOMIC_SEQ_CST);
45*042d53a7SEvalZero }
46*042d53a7SEvalZero 
47*042d53a7SEvalZero /**
48*042d53a7SEvalZero  *
49*042d53a7SEvalZero  * @brief Atomic addition.
50*042d53a7SEvalZero  *
51*042d53a7SEvalZero  * This routine performs an atomic addition on @a target.
52*042d53a7SEvalZero  *
53*042d53a7SEvalZero  * @param target Address of atomic variable.
54*042d53a7SEvalZero  * @param value Value to add.
55*042d53a7SEvalZero  *
56*042d53a7SEvalZero  * @return Previous value of @a target.
57*042d53a7SEvalZero  */
atomic_add(atomic_t * target,atomic_val_t value)58*042d53a7SEvalZero static inline atomic_val_t atomic_add(atomic_t *target, atomic_val_t value)
59*042d53a7SEvalZero {
60*042d53a7SEvalZero     return __atomic_fetch_add(target, value, __ATOMIC_SEQ_CST);
61*042d53a7SEvalZero }
62*042d53a7SEvalZero 
63*042d53a7SEvalZero /**
64*042d53a7SEvalZero  *
65*042d53a7SEvalZero  * @brief Atomic subtraction.
66*042d53a7SEvalZero  *
67*042d53a7SEvalZero  * This routine performs an atomic subtraction on @a target.
68*042d53a7SEvalZero  *
69*042d53a7SEvalZero  * @param target Address of atomic variable.
70*042d53a7SEvalZero  * @param value Value to subtract.
71*042d53a7SEvalZero  *
72*042d53a7SEvalZero  * @return Previous value of @a target.
73*042d53a7SEvalZero  */
74*042d53a7SEvalZero 
atomic_sub(atomic_t * target,atomic_val_t value)75*042d53a7SEvalZero static inline atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value)
76*042d53a7SEvalZero {
77*042d53a7SEvalZero     return __atomic_fetch_sub(target, value, __ATOMIC_SEQ_CST);
78*042d53a7SEvalZero }
79*042d53a7SEvalZero 
80*042d53a7SEvalZero /**
81*042d53a7SEvalZero  *
82*042d53a7SEvalZero  * @brief Atomic increment.
83*042d53a7SEvalZero  *
84*042d53a7SEvalZero  * This routine performs an atomic increment by 1 on @a target.
85*042d53a7SEvalZero  *
86*042d53a7SEvalZero  * @param target Address of atomic variable.
87*042d53a7SEvalZero  *
88*042d53a7SEvalZero  * @return Previous value of @a target.
89*042d53a7SEvalZero  */
90*042d53a7SEvalZero 
atomic_inc(atomic_t * target)91*042d53a7SEvalZero static inline atomic_val_t atomic_inc(atomic_t *target)
92*042d53a7SEvalZero {
93*042d53a7SEvalZero     return atomic_add(target, 1);
94*042d53a7SEvalZero }
95*042d53a7SEvalZero 
96*042d53a7SEvalZero /**
97*042d53a7SEvalZero  *
98*042d53a7SEvalZero  * @brief Atomic decrement.
99*042d53a7SEvalZero  *
100*042d53a7SEvalZero  * This routine performs an atomic decrement by 1 on @a target.
101*042d53a7SEvalZero  *
102*042d53a7SEvalZero  * @param target Address of atomic variable.
103*042d53a7SEvalZero  *
104*042d53a7SEvalZero  * @return Previous value of @a target.
105*042d53a7SEvalZero  */
106*042d53a7SEvalZero 
atomic_dec(atomic_t * target)107*042d53a7SEvalZero static inline atomic_val_t atomic_dec(atomic_t *target)
108*042d53a7SEvalZero {
109*042d53a7SEvalZero     return atomic_sub(target, 1);
110*042d53a7SEvalZero }
111*042d53a7SEvalZero 
112*042d53a7SEvalZero /**
113*042d53a7SEvalZero  *
114*042d53a7SEvalZero  * @brief Atomic get.
115*042d53a7SEvalZero  *
116*042d53a7SEvalZero  * This routine performs an atomic read on @a target.
117*042d53a7SEvalZero  *
118*042d53a7SEvalZero  * @param target Address of atomic variable.
119*042d53a7SEvalZero  *
120*042d53a7SEvalZero  * @return Value of @a target.
121*042d53a7SEvalZero  */
122*042d53a7SEvalZero 
atomic_get(const atomic_t * target)123*042d53a7SEvalZero static inline atomic_val_t atomic_get(const atomic_t *target)
124*042d53a7SEvalZero {
125*042d53a7SEvalZero     return __atomic_load_n(target, __ATOMIC_SEQ_CST);
126*042d53a7SEvalZero }
127*042d53a7SEvalZero 
128*042d53a7SEvalZero /**
129*042d53a7SEvalZero  *
130*042d53a7SEvalZero  * @brief Atomic get-and-set.
131*042d53a7SEvalZero  *
132*042d53a7SEvalZero  * This routine atomically sets @a target to @a value and returns
133*042d53a7SEvalZero  * the previous value of @a target.
134*042d53a7SEvalZero  *
135*042d53a7SEvalZero  * @param target Address of atomic variable.
136*042d53a7SEvalZero  * @param value Value to write to @a target.
137*042d53a7SEvalZero  *
138*042d53a7SEvalZero  * @return Previous value of @a target.
139*042d53a7SEvalZero  */
140*042d53a7SEvalZero 
atomic_set(atomic_t * target,atomic_val_t value)141*042d53a7SEvalZero static inline atomic_val_t atomic_set(atomic_t *target, atomic_val_t value)
142*042d53a7SEvalZero {
143*042d53a7SEvalZero     /* This builtin, as described by Intel, is not a traditional
144*042d53a7SEvalZero      * test-and-set operation, but rather an atomic exchange operation. It
145*042d53a7SEvalZero      * writes value into *ptr, and returns the previous contents of *ptr.
146*042d53a7SEvalZero      */
147*042d53a7SEvalZero     return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST);
148*042d53a7SEvalZero }
149*042d53a7SEvalZero 
150*042d53a7SEvalZero /**
151*042d53a7SEvalZero  *
152*042d53a7SEvalZero  * @brief Atomic clear.
153*042d53a7SEvalZero  *
154*042d53a7SEvalZero  * This routine atomically sets @a target to zero and returns its previous
155*042d53a7SEvalZero  * value. (Hence, it is equivalent to atomic_set(target, 0).)
156*042d53a7SEvalZero  *
157*042d53a7SEvalZero  * @param target Address of atomic variable.
158*042d53a7SEvalZero  *
159*042d53a7SEvalZero  * @return Previous value of @a target.
160*042d53a7SEvalZero  */
161*042d53a7SEvalZero 
atomic_clear(atomic_t * target)162*042d53a7SEvalZero static inline atomic_val_t atomic_clear(atomic_t *target)
163*042d53a7SEvalZero {
164*042d53a7SEvalZero     return atomic_set(target, 0);
165*042d53a7SEvalZero }
166*042d53a7SEvalZero 
167*042d53a7SEvalZero /**
168*042d53a7SEvalZero  *
169*042d53a7SEvalZero  * @brief Atomic bitwise inclusive OR.
170*042d53a7SEvalZero  *
171*042d53a7SEvalZero  * This routine atomically sets @a target to the bitwise inclusive OR of
172*042d53a7SEvalZero  * @a target and @a value.
173*042d53a7SEvalZero  *
174*042d53a7SEvalZero  * @param target Address of atomic variable.
175*042d53a7SEvalZero  * @param value Value to OR.
176*042d53a7SEvalZero  *
177*042d53a7SEvalZero  * @return Previous value of @a target.
178*042d53a7SEvalZero  */
179*042d53a7SEvalZero 
atomic_or(atomic_t * target,atomic_val_t value)180*042d53a7SEvalZero static inline atomic_val_t atomic_or(atomic_t *target, atomic_val_t value)
181*042d53a7SEvalZero {
182*042d53a7SEvalZero     return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST);
183*042d53a7SEvalZero }
184*042d53a7SEvalZero 
185*042d53a7SEvalZero /**
186*042d53a7SEvalZero  *
187*042d53a7SEvalZero  * @brief Atomic bitwise exclusive OR (XOR).
188*042d53a7SEvalZero  *
189*042d53a7SEvalZero  * This routine atomically sets @a target to the bitwise exclusive OR (XOR) of
190*042d53a7SEvalZero  * @a target and @a value.
191*042d53a7SEvalZero  *
192*042d53a7SEvalZero  * @param target Address of atomic variable.
193*042d53a7SEvalZero  * @param value Value to XOR
194*042d53a7SEvalZero  *
195*042d53a7SEvalZero  * @return Previous value of @a target.
196*042d53a7SEvalZero  */
197*042d53a7SEvalZero 
atomic_xor(atomic_t * target,atomic_val_t value)198*042d53a7SEvalZero static inline atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value)
199*042d53a7SEvalZero {
200*042d53a7SEvalZero     return __atomic_fetch_xor(target, value, __ATOMIC_SEQ_CST);
201*042d53a7SEvalZero }
202*042d53a7SEvalZero 
203*042d53a7SEvalZero /**
204*042d53a7SEvalZero  *
205*042d53a7SEvalZero  * @brief Atomic bitwise AND.
206*042d53a7SEvalZero  *
207*042d53a7SEvalZero  * This routine atomically sets @a target to the bitwise AND of @a target
208*042d53a7SEvalZero  * and @a value.
209*042d53a7SEvalZero  *
210*042d53a7SEvalZero  * @param target Address of atomic variable.
211*042d53a7SEvalZero  * @param value Value to AND.
212*042d53a7SEvalZero  *
213*042d53a7SEvalZero  * @return Previous value of @a target.
214*042d53a7SEvalZero  */
215*042d53a7SEvalZero 
atomic_and(atomic_t * target,atomic_val_t value)216*042d53a7SEvalZero static inline atomic_val_t atomic_and(atomic_t *target, atomic_val_t value)
217*042d53a7SEvalZero {
218*042d53a7SEvalZero     return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST);
219*042d53a7SEvalZero }
220*042d53a7SEvalZero 
221*042d53a7SEvalZero /**
222*042d53a7SEvalZero  *
223*042d53a7SEvalZero  * @brief Atomic bitwise NAND.
224*042d53a7SEvalZero  *
225*042d53a7SEvalZero  * This routine atomically sets @a target to the bitwise NAND of @a target
226*042d53a7SEvalZero  * and @a value. (This operation is equivalent to target = ~(target & value).)
227*042d53a7SEvalZero  *
228*042d53a7SEvalZero  * @param target Address of atomic variable.
229*042d53a7SEvalZero  * @param value Value to NAND.
230*042d53a7SEvalZero  *
231*042d53a7SEvalZero  * @return Previous value of @a target.
232*042d53a7SEvalZero  */
233*042d53a7SEvalZero 
atomic_nand(atomic_t * target,atomic_val_t value)234*042d53a7SEvalZero static inline atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value)
235*042d53a7SEvalZero {
236*042d53a7SEvalZero     return __atomic_fetch_nand(target, value, __ATOMIC_SEQ_CST);
237*042d53a7SEvalZero }
238*042d53a7SEvalZero 
239*042d53a7SEvalZero     /**
240*042d53a7SEvalZero      * @brief Initialize an atomic variable.
241*042d53a7SEvalZero      *
242*042d53a7SEvalZero      * This macro can be used to initialize an atomic variable. For example,
243*042d53a7SEvalZero      * @code atomic_t my_var = ATOMIC_INIT(75); @endcode
244*042d53a7SEvalZero      *
245*042d53a7SEvalZero      * @param i Value to assign to atomic variable.
246*042d53a7SEvalZero      */
247*042d53a7SEvalZero #define ATOMIC_INIT(i) (i)
248*042d53a7SEvalZero 
249*042d53a7SEvalZero     /**
250*042d53a7SEvalZero      * @cond INTERNAL_HIDDEN
251*042d53a7SEvalZero      */
252*042d53a7SEvalZero 
253*042d53a7SEvalZero #define ATOMIC_BITS (sizeof(atomic_val_t) * 8)
254*042d53a7SEvalZero #define ATOMIC_MASK(bit) (1 << ((bit) & (ATOMIC_BITS - 1)))
255*042d53a7SEvalZero #define ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / ATOMIC_BITS))
256*042d53a7SEvalZero 
257*042d53a7SEvalZero     /**
258*042d53a7SEvalZero      * INTERNAL_HIDDEN @endcond
259*042d53a7SEvalZero      */
260*042d53a7SEvalZero 
261*042d53a7SEvalZero     /**
262*042d53a7SEvalZero      * @brief Define an array of atomic variables.
263*042d53a7SEvalZero      *
264*042d53a7SEvalZero      * This macro defines an array of atomic variables containing at least
265*042d53a7SEvalZero      * @a num_bits bits.
266*042d53a7SEvalZero      *
267*042d53a7SEvalZero      * @note
268*042d53a7SEvalZero      * If used from file scope, the bits of the array are initialized to zero;
269*042d53a7SEvalZero      * if used from within a function, the bits are left uninitialized.
270*042d53a7SEvalZero      *
271*042d53a7SEvalZero      * @param name Name of array of atomic variables.
272*042d53a7SEvalZero      * @param num_bits Number of bits needed.
273*042d53a7SEvalZero      */
274*042d53a7SEvalZero #define ATOMIC_DEFINE(name, num_bits) \
275*042d53a7SEvalZero 	atomic_t name[1 + ((num_bits) - 1) / ATOMIC_BITS]
276*042d53a7SEvalZero 
277*042d53a7SEvalZero     /**
278*042d53a7SEvalZero      * @brief Atomically test a bit.
279*042d53a7SEvalZero      *
280*042d53a7SEvalZero      * This routine tests whether bit number @a bit of @a target is set or not.
281*042d53a7SEvalZero      * The target may be a single atomic variable or an array of them.
282*042d53a7SEvalZero      *
283*042d53a7SEvalZero      * @param target Address of atomic variable or array.
284*042d53a7SEvalZero      * @param bit Bit number (starting from 0).
285*042d53a7SEvalZero      *
286*042d53a7SEvalZero      * @return 1 if the bit was set, 0 if it wasn't.
287*042d53a7SEvalZero      */
288*042d53a7SEvalZero     static inline int
atomic_test_bit(const atomic_t * target,int bit)289*042d53a7SEvalZero     atomic_test_bit(const atomic_t *target, int bit)
290*042d53a7SEvalZero     {
291*042d53a7SEvalZero         atomic_val_t val = atomic_get(ATOMIC_ELEM(target, bit));
292*042d53a7SEvalZero 
293*042d53a7SEvalZero         return (1 & (val >> (bit & (ATOMIC_BITS - 1))));
294*042d53a7SEvalZero     }
295*042d53a7SEvalZero 
296*042d53a7SEvalZero     /**
297*042d53a7SEvalZero      * @brief Atomically test and clear a bit.
298*042d53a7SEvalZero      *
299*042d53a7SEvalZero      * Atomically clear bit number @a bit of @a target and return its old value.
300*042d53a7SEvalZero      * The target may be a single atomic variable or an array of them.
301*042d53a7SEvalZero      *
302*042d53a7SEvalZero      * @param target Address of atomic variable or array.
303*042d53a7SEvalZero      * @param bit Bit number (starting from 0).
304*042d53a7SEvalZero      *
305*042d53a7SEvalZero      * @return 1 if the bit was set, 0 if it wasn't.
306*042d53a7SEvalZero      */
307*042d53a7SEvalZero     static inline int
atomic_test_and_clear_bit(atomic_t * target,int bit)308*042d53a7SEvalZero     atomic_test_and_clear_bit(atomic_t *target, int bit)
309*042d53a7SEvalZero     {
310*042d53a7SEvalZero         atomic_val_t mask = ATOMIC_MASK(bit);
311*042d53a7SEvalZero         atomic_val_t old;
312*042d53a7SEvalZero 
313*042d53a7SEvalZero         old = atomic_and(ATOMIC_ELEM(target, bit), ~mask);
314*042d53a7SEvalZero 
315*042d53a7SEvalZero         return (old & mask) != 0;
316*042d53a7SEvalZero     }
317*042d53a7SEvalZero 
318*042d53a7SEvalZero     /**
319*042d53a7SEvalZero      * @brief Atomically set a bit.
320*042d53a7SEvalZero      *
321*042d53a7SEvalZero      * Atomically set bit number @a bit of @a target and return its old value.
322*042d53a7SEvalZero      * The target may be a single atomic variable or an array of them.
323*042d53a7SEvalZero      *
324*042d53a7SEvalZero      * @param target Address of atomic variable or array.
325*042d53a7SEvalZero      * @param bit Bit number (starting from 0).
326*042d53a7SEvalZero      *
327*042d53a7SEvalZero      * @return 1 if the bit was set, 0 if it wasn't.
328*042d53a7SEvalZero      */
329*042d53a7SEvalZero     static inline int
atomic_test_and_set_bit(atomic_t * target,int bit)330*042d53a7SEvalZero     atomic_test_and_set_bit(atomic_t *target, int bit)
331*042d53a7SEvalZero     {
332*042d53a7SEvalZero         atomic_val_t mask = ATOMIC_MASK(bit);
333*042d53a7SEvalZero         atomic_val_t old;
334*042d53a7SEvalZero 
335*042d53a7SEvalZero         old = atomic_or(ATOMIC_ELEM(target, bit), mask);
336*042d53a7SEvalZero 
337*042d53a7SEvalZero         return (old & mask) != 0;
338*042d53a7SEvalZero     }
339*042d53a7SEvalZero 
340*042d53a7SEvalZero     /**
341*042d53a7SEvalZero      * @brief Atomically clear a bit.
342*042d53a7SEvalZero      *
343*042d53a7SEvalZero      * Atomically clear bit number @a bit of @a target.
344*042d53a7SEvalZero      * The target may be a single atomic variable or an array of them.
345*042d53a7SEvalZero      *
346*042d53a7SEvalZero      * @param target Address of atomic variable or array.
347*042d53a7SEvalZero      * @param bit Bit number (starting from 0).
348*042d53a7SEvalZero      *
349*042d53a7SEvalZero      * @return N/A
350*042d53a7SEvalZero      */
351*042d53a7SEvalZero     static inline void
atomic_clear_bit(atomic_t * target,int bit)352*042d53a7SEvalZero     atomic_clear_bit(atomic_t *target, int bit)
353*042d53a7SEvalZero     {
354*042d53a7SEvalZero         atomic_val_t mask = ATOMIC_MASK(bit);
355*042d53a7SEvalZero 
356*042d53a7SEvalZero         atomic_and(ATOMIC_ELEM(target, bit), ~mask);
357*042d53a7SEvalZero     }
358*042d53a7SEvalZero 
359*042d53a7SEvalZero     /**
360*042d53a7SEvalZero      * @brief Atomically set a bit.
361*042d53a7SEvalZero      *
362*042d53a7SEvalZero      * Atomically set bit number @a bit of @a target.
363*042d53a7SEvalZero      * The target may be a single atomic variable or an array of them.
364*042d53a7SEvalZero      *
365*042d53a7SEvalZero      * @param target Address of atomic variable or array.
366*042d53a7SEvalZero      * @param bit Bit number (starting from 0).
367*042d53a7SEvalZero      *
368*042d53a7SEvalZero      * @return N/A
369*042d53a7SEvalZero      */
370*042d53a7SEvalZero     static inline void
atomic_set_bit(atomic_t * target,int bit)371*042d53a7SEvalZero     atomic_set_bit(atomic_t *target, int bit)
372*042d53a7SEvalZero     {
373*042d53a7SEvalZero         atomic_val_t mask = ATOMIC_MASK(bit);
374*042d53a7SEvalZero 
375*042d53a7SEvalZero         atomic_or(ATOMIC_ELEM(target, bit), mask);
376*042d53a7SEvalZero     }
377*042d53a7SEvalZero 
378*042d53a7SEvalZero /**
379*042d53a7SEvalZero  * @}
380*042d53a7SEvalZero  */
381*042d53a7SEvalZero 
382*042d53a7SEvalZero #ifdef __cplusplus
383*042d53a7SEvalZero }
384*042d53a7SEvalZero #endif
385*042d53a7SEvalZero 
386*042d53a7SEvalZero #endif /* __ATOMIC_H__ */
387