Atomic swap function using gcc atomic embedded kernels

Is this the correct implementation for the general function of an atomic swap? I am looking for a C ++ 03 compatible solution for GCC.

template<typename T> void atomic_swap(T & a, T & b) { static_assert(sizeof(T) <= sizeof(void*), "Maximum size type exceeded."); T * ptr = &a; b =__sync_lock_test_and_set(ptr, b); __sync_lock_release(&ptr); } 

If not, what should I do to fix this?

Also: do you always need __sync_lock_release ? When searching for other codes, I found that this is often not called. Without calling release, my code looks like this:

 template<typename T> void atomic_swap(T & a, T & b) { static_assert(sizeof(T) <= sizeof(void*), "Maximum size type exceeded."); b = __sync_lock_test_and_set(&a, b); } 

PS: Atomic swap in GNU C ++ is a similar question, but it does not answer my question, because the answer provided requires C ++ 11 std::atomic , and it has the signature Data *swap_data(Data *new_data) , which makes no sense for the swap function at all. (It actually reduces the supplied argument to the global variable that was defined before the function.)

+7
source share
1 answer

Remember that this version of swap is not a fully atomic operation. As long as the value of b is atomically copied to a , the value of a can copy another modification to the value of b another thread. In other words, assignment b not atomic with respect to other threads. Thus, you may encounter a situation where a == 1 and b == 2 , and after the built-in gcc you get a == 2 and the return value is 1 , but now another thread changed the value of b - 3 , and you write this value in b with a value of 1 . Thus, although you may have β€œtechnically” swapped the values, you did not do this atomically ... another thread touched the value of b between the return from the built-in gcc atom and the purpose of this return is b . Looking in terms of assembly, you have something like the following:

 lea RAX, qword ptr [RDI] // T * ptr = &a; mov RCX, qword ptr [RSI] // copy out the value referenced by b into a register xchg [RAX], RCX // __sync_lock_test_and_set(&a, b) mov qword ptr [RSI], RCX // place the exchange value back into b (not atomic!!) 

Honestly, you cannot swap atomically without locking from two separate memory locations without a hardware operation, such as DCAS or weak load-related / save-storage, or perhaps using some other method such as transactional memory ( which itself tends to use fine-grained blocking).

Secondly, since your function is written right now, if you want your atomic operation to have both receive and release semantics, yes you will either have to place it in __sync_lock_release , or you are going to need to add a complete memory barrier via __sync_synchronize . Otherwise, it will only get semantics on __sync_lock_test_and_set . However, it does not atomically swap two separate memory cells with each other ...

+9
source

All Articles