If you use GCC or the Intel Compiler Collection, then you can use the atomic built-in functions for this. The C++11-style builtin atomic operations will probably be standardized in a decade or so, but until they become more widely available, I'm sticking with the legacy-style __sync ones. (I think also Pathscale and Portland Group C compilers provide these, but I'm too lazy to check, sorry.)
Note that you need to use the atomic built-ins for all accesses to the variable; it is not enough to do it atomically on one side. (The reason being that the non-atomic side can do the read/write in two separate parts, with the atomic access in between.)
For example, to obtain the value of the integer variable atomically, I use
Code:
value = __sync_fetch_and_or(&variable, 0);
Clearing is similarly trivial (as is setting or removing individual bits from the variable). This clears the variable, but returns the value it had:
Code:
old = __sync_fetch_and_and(&variable, 0);
The link shows the instructions to add to or substract from the variable, and so on.
Setting the variable to a fixed value is a bit more complicated. I've found the best option to be the compare-and-swap loop. Usually it does not loop at all:
Code:
/* Set variable = value, saving old value in old */
do {
old = variable;
} while (!__sync_bool_compare_and_swap(&variable, old, value));
If you are wondering, the access in the body is not atomic, and it does not need to be. The atomic call compares the current value of variable to the old value atomically, only changing it (in the same atomic operation) to the new value value if it matches. If the access in the body of the loop did not match, no harm done: the atomic call will just return 0 (false), causing another iteration of the loop.
The compare-and-swap can be used to access floats and doubles and pointers atomically too, on most architectures:
Code:
if (sizeof (variable) == sizeof (unsigned long) {
do {
old = variable;
/* Note: you can recompute new value here, using old */
} while (!__sync_bool_compare_and_swap((unsigned long *)&variable, (unsigned long)old, (unsigned long)value);
} else
if (sizeof (variable) == sizeof (unsigned int) {
do {
old = variable;
/* Note: you can recompute new value here, using old */
} while (!__sync_bool_compare_and_swap((unsigned int *)&variable, (unsigned int)old, (unsigned int)value);
} else {
/* Not supported! */
}
while just getting the value is simpler:
Code:
if (sizeof (variable) == sizeof (unsigned long))
*((unsigned long)&variable) = __sync_fetch_and_or((unsigned long)&variable, 0UL);
else
if (sizeof (variable) == sizeof (unsigned int))
*((unsigned int)&variable) = __sync_fetch_and_or((unsigned int)&variable, 0U);
else
/* Not supported! */
Because normal Linux architectures are all either ILP32 (32-bit) or LP64 (64-bit), the above has always worked for me in Linux, on any architecture I've tried it on. I've successfully used the above to manipulate both pointers and floating-point values atomically.
More complicated structures can be read atomically, if they have two generation counters. When modified, one generation counter is increased first. After modifying the structure contents, the second generation counter is also increased. (Memory write barriers or __sync_synchronize() may be needed after increasing the first counter and before increasing the second counter, to make sure the stores are not reordered over the atomic increments.) Any reader will simply read the first generation counter first, then copy the structure contents, and then the second counter. If the two counters mismatch, the read operation must be redone. (Memory read barriers may be needed; I tend to treat both read and modify the same.)