- made up by Intel for the Itanium ABI
- legacy
- all sequentially consistent
- operate on arbitrary integer-based types
- made up by GCC
- take memorder args
- operate on arbitrary integer-based types
- operate on
_Atomic type
types - by default sequentially consistent, but have
_explicit
versions which take memorder arguments - better than builtins
- operate on
std::atomic<T>
types - these aren't functions but methods
- operate on
std::sync::atomic::AtomicSomething
types - take memorder arguments
Atomic operation | __sync builtin | __atomic builtin | C11 | C++14 | Rust |
---|---|---|---|---|---|
fence | __sync_synchronize | __atomic_thread/signal_fence1 | atomic_thread/signal_fence1 | std::atomic_thread/signal_fence1 | fence, compiler_fence (only for reordering by the compiler) |
atomic compare and swap | __sync_bool/val_compare_and_swap (the “bool” version returns true if the comparison is true and newval is written. The “val” version returns the contents of *ptr before the operation) | __atomic_compare_exchange | atomic_compare_exchange_weak/strong2 | compare_exchange_weak/strong2 | compare_and_swap/exchange (returns new value), compare_exchange (returns Result<new_value> ), compare_exchange_weak2 |
atomic swap | None. Only in clang: __sync_swap | __atomic_exchange | atomic_exchange | exchange | swap |
atomic add | __sync_fetch_and_add, __sync_add_and_fetch34 | __atomic_fetch_add, __atomic_add_fetch34 | atomic_fetch_add. Also: sub, or, xor, and | operator+= Also: -=, &=, |=, ^= (but also fetch_add equivalent to the operators) | fetch_add (Also: sub, or, xor, and, nand, max, min), or fetch_update with modification function |
load | None | __atomic_load | atomic_load | operatorT5 | load |
store | None | __atomic_store | atomic_store | operator= | store |
flag operations (redundant, fairly useless...) | __sync_lock_test_and_set (atomic swap, may be restricted to only some values on some backends for historical reasons), __sync_lock_release (atomic store 0) - Have acquire and release barriers respectively, not seq_cst! | __atomic_test_and_set (atomic swap some_magic_value, bool return value), __atomic_clear (atomic store 0). Only for bool and char | atomic_flag_test_and_set (atomic swap true), atomic_flag_clear (atomic store 0). Only for atomic_flag type | std::atomic_flag methods: test_and_set, clear, test | None. There's probably enough cool types in std::sync that you don't need these |
wait, notify | None | None | None | std::atomic_flag methods: wait, notify_one, notify_all | atomic_wait::{wait, wake_one, wake_all}; |
lock-free check | None | __atomic_always_lock_free/__atomic_is_lock_free | atomic_is_lock_free | is_lock_free | None |
type initialization | No special atomic type | No special atomic type | atomic_init | constructor | new |
Just about every builtin has a version with _N (where N is type size in bytes) suffix, which returns old value, instead of void, and also takes the value argument by value, not by reference. These are expected to be implemented by external libraries (in our case compiler-rt) to be deferred the operation to by the compiler by lowering an operation into a libcall. These _N suffix functions are not really meant to be manually called, except it might be better, since they have proper return values, instead of writing return values (like the result of a load) to a pointer argument. To illustrate: type __atomic_load_n (type *ptr, int memorder)
VS void __atomic_load (type *ptr, type *ret, int memorder)
Footnotes
-
thread: between threads. signal: between thread and signal handlers (not very applicable) ↩ ↩2 ↩3
-
The weak forms of the functions are allowed to fail spuriously. When a compare-and-exchange is in a loop, the weak version will yield better performance on some platforms ↩ ↩2 ↩3
-
One returns the old value, the other returns the new value ↩ ↩2
-
Also: sub, or, and, xor, nand. Only in clang: max, umax, min, umin. __sync min, max, umin, umax: only exist for int (not overloaded for char etc) ↩ ↩2
-
This means you can just cast it to the underlying type, and that will load the current value:
std::atomic<int> a = 1; int b = (int)a;
. But there's still store and load methods (equivalent to the operators) ↩