-
Notifications
You must be signed in to change notification settings - Fork 1
/
atomic.h
345 lines (312 loc) · 12.7 KB
/
atomic.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
/*! \defgroup _atomic Атомарные операции
атомарные операции для Cortex-M3 и Intel Core
\see [ARM IHI 0053C] ARM® C Language Extensions, Release 2.0
Встроенные инструкции __builtin_ работают на всех платформах Intel, для тех платформ,
где не определены может использоваться внешняя библиотека
вызов подменяется на __sync_bool_compare_and_swap_4
Для ARMv7 надо переписать с использованием инструкций эксклюзивного доступа
\see ARM Cortex™-M Programming Guide to Memory Barrier Instructions Application Note 321
uint32_t __swp(uint32_t x, volatile uint32_t *p) {
uint32_t v;
// use LDREX/STREX intrinsics not specified by ACLE
do v = __ldrex(p); while (__strex(x, p));
return v;
}
or alternatively,
uint32_t __swp(uint32_t x, uint32_t *p) {
uint32_t v;
// use IA-64/GCC atomic builtins
do v = *p; while (!__sync_bool_compare_and_swap(p, v, x));
return v;
}
\todo ISO/IEC 9899:2011 определяет <stdatomic.h> if __STDC_VERSION__ > 201112L
\todo GCC Built-in
\see https://www.iso.org/obp/ui/#iso:std:iso-iec:9899:ed-3:v1:en
\{
*/
#ifndef ATOMIC_H
#define ATOMIC_H
// атомарные операции производим с целым типом
typedef volatile int atomic_t;
#define ACCESS_ONCE(x) (*(volatile __typeof__(x) *)&(x))
#define compiler_barrier() asm volatile("": : :"memory")
#if (defined(__arm__) && __ARM_ARCH >= 6)
#include "board.h"
#if (defined(__CORTEX_M) && __CORTEX_M == 0)
static inline int atomic_int_get(volatile int *ptr)
{
__sync_synchronize();
return *ptr;
}
static inline void* atomic_pointer_get(volatile void **ptr)
{
__sync_synchronize();
return (void*)(*ptr);
}
static inline int atomic_int_compare_and_exchange(volatile int *ptr, int oldval, int newval)
{
int result;
__disable_irq();
result = (*ptr == oldval);
if (result) {
*ptr = newval;
__DSB();
}
__enable_irq();
return result;
}
static inline int atomic_pointer_compare_and_exchange(volatile void **ptr, void *oldval, void *newval)
{
int result;
__disable_irq();
result = (*ptr == oldval);
if (result) {
*ptr = newval;
__DSB();
}
__enable_irq();
return result;
}
#else
/*! \brief Очищает привязку к ячейке памяти выполненную командой LDREX (atomic_int_get)
*/
__attribute__(( always_inline ))
static inline void atomic_free()
{
__ASM volatile ("clrex" ::: "memory");
}
/*! \brief атомарно читает переменную по заданному указателю
Функцию следует использовать в сочетании с функцией \ref atomic_int_compare_and_exchange
Вместе они образуют пару LDREX/STREX
\param ptr указатель на переменную.
\return значение переменной
*/
__attribute__(( always_inline ))
static inline int atomic_int_get(volatile int *ptr) {
int result;
__ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*ptr) ); // \see CMSIS/core_cmIntr.h
return result;
}
__attribute__(( always_inline ))
static inline short int atomic_short_get(volatile short int *ptr) {
short int result;
__ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*ptr) ); // \see CMSIS/core_cmIntr.h
return result;
}
__attribute__(( always_inline ))
static inline uint8_t atomic_uint8_get(volatile uint8_t *ptr) {
uint8_t result;
__ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*ptr) ); // \see cmsis_gcc.h
return result;
}
/*! \brief атомарно читает переменную типа указатель по заданному указателю
Функцию следует использовать в сочетании с функцией \ref atomic_int_compare_and_exchange
Вместе они образуют пару LDREX/STREX
\param ptr указатель на переменную.
\return значение переменной
*/
__attribute__(( always_inline ))
static inline void* atomic_pointer_get(volatile void **ptr)
{
void* result;
__ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*ptr) ); // \see CMSIS/core_cmIntr.h
return (void*)result;
}
/*! \brief производит сравнение и замену. Если значение переменной по указанному адресу изменилось,
то операция замены не выполняется.
\param [in] ptr - указатель на атомарную переменную
\param [in] oldval - старое значение переменной
\param [in] newval - новое значение переменной
\return TRUE если операция сохранения прошла успешно, обмен произведен
*/
__attribute__(( always_inline ))
static inline int atomic_int_compare_and_exchange(volatile int *ptr, int oldval, int newval)
{
int result;
__ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" (newval) );
// __DMB();
return result==0;
}
__attribute__(( always_inline ))
static inline short int atomic_short_compare_and_exchange(volatile short int *ptr, short int oldval, short int newval)
{
short int result;
__ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" (newval) );
// __DMB();
return result==0;
}
__attribute__(( always_inline ))
static inline uint8_t atomic_uint8_compare_and_exchange(volatile uint8_t *ptr, uint8_t oldval, uint8_t newval)
{
uint8_t result;
__ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" (newval) );
// __DMB();
return result==0;
}
/*! \brief производит сравнение и замену атомарного указателя. Если значение переменной по указанному адресу изменилось,
то операция замены не выполняется.
\param [in] ptr - указатель на атомарную переменную
\param [in] oldval - старое значение переменной
\param [in] newval - новое значение переменной
\return TRUE если операция сохранения прошла успешно, обмен произведен
*/
__attribute__(( always_inline ))
static inline int atomic_pointer_compare_and_exchange(volatile void *ptr, void* oldval, void* newval)
{
int result;
__ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*(int*)ptr) : "r" (newval) );
// __DMB();
return result==0;
}
#endif
#define atomic_mb __DSB
#if 0
static inline uint32_t __attribute__((always_inline, nodebug))
__swp(uint32_t x, volatile uint32_t *p) {
uint32_t v;
do v = __builtin_arm_ldrex(p); while (__builtin_arm_strex(x, p));
return v;
}
#endif
#else // для платформы Intel
#define atomic_mb __sync_synchronize
static inline void atomic_free()
{
}
static inline int atomic_int_get(volatile int *v)
{
__sync_synchronize();// см исходники Glib
return (*v);
}
static inline void* atomic_pointer_get(volatile void **v)
{
__sync_synchronize();// см исходники Glib
return (void*)*v;
}
static inline int atomic_int_compare_and_exchange(volatile int *ptr, int oldval, int newval)
{
// return __atomic_compare_exchange_4(ptr, &oldval, newval, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
return __sync_bool_compare_and_swap(ptr, oldval, newval);// returns true if the comparison is successful and newval was written
}
static inline int atomic_pointer_compare_and_exchange(volatile void **ptr, void* oldval, void* newval)
{
// return __atomic_compare_exchange_{4,8}(ptr, &oldval, newval, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
return __sync_bool_compare_and_swap(ptr, oldval, newval);// returns true if the comparison is successful and newval was written
}
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__>=201112L) && !defined(__STDC_NO_ATOMICS__)
//#warning "Атомарные типы определены в <stdatomic.h> version="
#include <stdatomic.h>
#define atomic_pointer_exchange(ptr, val) atomic_exchange(ptr, val)
#define atomic_pointer_store(ptr, val) atomic_store(ptr, val)
#else
/*! \brief присвоить значение переменной
\param [in] ptr указатель на атомарную переменную
\param [in] newval значение переменной
\return значение переменной до выполнения операции
\todo переиментовать в _exchange
*/
static inline int atomic_exchange(volatile int* ptr, int newval)
{
int value;
do {
value = atomic_int_get(ptr);
} while(!atomic_int_compare_and_exchange(ptr,value,newval));
return value;
}
/*! \brief замена атомарной переменной
используется для замены указателей в списках
\param [in] atomic - атомарная переменная
\param [in] newptr - новое значение указателя
\return значение переменной до выполнения операции
*/
static inline void* atomic_pointer_exchange(volatile void** atomic, void* newptr)
{
void* oldptr;
do {
oldptr = atomic_pointer_get(atomic);
} while(!atomic_pointer_compare_and_exchange(atomic,oldptr,newptr));
return oldptr;
}
static inline void atomic_pointer_store(volatile void** atomic, void* newptr)
{
void* oldptr;
do {
oldptr = atomic_pointer_get(atomic);
} while(!atomic_pointer_compare_and_exchange(atomic,oldptr,newptr));
}
/*! \brief атомарно добавить число к переменной
\param [in] ptr указатель на атомарную переменную
\param [in] val довесок
\return значение переменной до выполнения операции
*/
static inline int atomic_fetch_add(volatile int* ptr, int operand)
{
int value;
do {
value = atomic_int_get(ptr);
} while(!atomic_int_compare_and_exchange(ptr,value,value+operand));
return value;
}
/*! \brief атомарно вычесть операнд
\note Oперации atomic_fetch_add/_sub могут быть использованы для счетчика указателей на объект и в механизме блокировок
\see [C11] stdatomic.h
*/
static inline int atomic_fetch_sub(volatile int* ptr, int operand)
{
int value;
do {
value = atomic_int_get(ptr);
} while(!atomic_int_compare_and_exchange(ptr,value,value-operand));
return value;
}
/*! \brief атомарно установить биты по маске
\param [in] ptr указатель на атомарную переменную
\param [in] mask маска, 1 - биты, которые надо установить, 0 - биты без изменения
\return значение переменной до выполнения операции
*/
static inline int atomic_fetch_or(volatile int* ptr, unsigned int mask)
{
int value;
do {
value = atomic_int_get(ptr);
} while(!atomic_int_compare_and_exchange(ptr,value,value|mask));
return value;
}
/*! \brief атомарно чистить биты по маске
\param [in] ptr указатель на атомарную переменную
\param [in] mask маска, 0 - биты, которые надо оставить без изменения, 1 - биты надо очистить
\return значение переменной до выполнения операции
*/
static inline int atomic_fetch_nand(volatile int* ptr, unsigned int mask)
{
int value;
do {
value = atomic_int_get(ptr);
} while(!atomic_int_compare_and_exchange(ptr,value,value & ~mask));
return value;
}
/*! \brief атомарно чистить биты по маске
\param [in] ptr указатель на атомарную переменную
\param [in] mask маска, 1 - биты, которые надо оставить без изменения, 0 - биты надо очистить
\return значение переменной до выполнения операции
*/
static inline int atomic_fetch_and(volatile int* ptr, unsigned int mask)
{
int value;
do {
value = atomic_int_get(ptr);
} while(!atomic_int_compare_and_exchange(ptr,value,value & mask));
return value;
}
static inline int atomic_fetch_xor(volatile int* ptr, unsigned int mask)
{
int value;
do {
value = atomic_int_get(ptr);
} while(!atomic_int_compare_and_exchange(ptr,value,value ^ mask));
return value;
}
#endif
/*! \} */
#endif// ATOMIC_H