I would like to modify a global variable which is shared by different tasks and IRQ contexts in a RTOS. Therefore I need to modify this variable atomically. In my current implementation, I have been using enable_irq/disable_irq functions to modify the statement atomically.
extern int g_var;
void set_bit_atomic(int mask)
{
disable_irq();
g_var |= mask;
enable_irq();
}
I've found the __sync_bool_compare_and_swap
function in GCC documentation as a helper for atomic operations.
My current toolchain is KEIL MDK, and I would like to switch to the approach shown below,
void set_bit_atomic(int mask)
{
volatile int tmp;
do {
tmp = g_var;
} while (!__sync_bool_compare_and_swap(&g_var, tmp, tmp | mask));
}
How can I write __sync_bool_compare_and_swap
function in ARMv4 command set(as inline assembly)?
I have found a similar implementation for __kernel_cmpxchg
function in Linux kernel source.
It has been written for ARMv5 and earlier, and It seems to work for ARM7TDMI (ARMv4).
1: ldr r3, [r2] @ load current val
subs r3, r3, r0 @ compare with oldval
2: streq r1, [r2] @ store newval if eq
rsbs r0, r3, #0 @ set return val and C flag
bx lr @ or "mov pc, lr" if no thumb support
Details can be found at this link.
There are two important issues that I would like to warn,
1- __kernel_cmpxchg
returns 0 when swap occurred, while __sync_bool_compare_and_swap
function returns true.
2- function prototypes are different.
typedef int (*__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
#define __kernel_cmpxchg ((__kernel_cmpxchg_t)0xffff0fc0)
bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)
Therefore I had to change the usage as below,
void set_bit_atomic(int mask)
{
volatile int tmp;
do {
tmp = g_var;
} while (my_compare_and_swap(tmp, tmp | mask, &g_var));
}
Caveat: This code does not work properly without kernel support. See the comments below.
来源:https://stackoverflow.com/questions/18475347/how-to-compare-and-swap-atomically-in-arm7