Reputation: 20746
Am I right in the following assumptions:
std::atomic<T>
objects from different threads on any platform with my own synchronization objectsstd::atomic<T>
operations could be lock-free or non-lock-free, dependent on the platformstd::atomic_bool
and std::atomic<bool>
(and the other types like these) are the same things actuallystd::atomic_flag
is the only class that guarantees platform-independent lock-free operations by the standardAlso where can I find a useful info about std::memory_order
and how to use it properly?
Upvotes: 5
Views: 380
Reputation: 1469
Let's go through one by one.
std::atomic<T>
objects from different threads on any platform with my own synchronization objectsYes, atomic
objects are fully synchronized on all of their accessor methods.
The only time a data race can occur with access to the atomic types is during construction, but it involves constructing an atomic object A
, passing its address to another thread via an atomic pointer using memory_order_relaxed
to intentionally work around the sequential consistency of std::atomic
, and then accessing A
from that second thread. So, don't do that? :)
Speaking of construction, there are three ways to initialize your atomic types:
// Method 1: constructor
std::atomic<int> my_int(5);
// Method 2: atomic_init
std::atomic<int> my_int; // must be default constructed
std::atomic_init(&my_int, 5); // only allowed once
// Method 3: ATOMIC_VAR_INIT
// may be implemented using locks even if std::atomic<int> is lock-free
std::atomic<int> my_int = ATOMIC_VAR_INIT(5);
Using either of the two latter methods, the same data race possibility applies.
std::atomic<T>
operations could be lock-free or non-lock-free, dependent on the platformCorrect. For all integral types, there are macros you can check that tell you whether a given atomic
specialization is not, sometimes, or always lock-free. The value of the macros is 0, 1, or 2 respectively for these three cases. The full list of macros is taken from §29.4 of the standard, where unspecified
is their stand-in for "0, 1, or 2":
#define ATOMIC_BOOL_LOCK_FREE unspecified
#define ATOMIC_CHAR_LOCK_FREE unspecified
#define ATOMIC_CHAR16_T_LOCK_FREE unspecified
#define ATOMIC_CHAR32_T_LOCK_FREE unspecified
#define ATOMIC_WCHAR_T_LOCK_FREE unspecified
#define ATOMIC_SHORT_LOCK_FREE unspecified
#define ATOMIC_INT_LOCK_FREE unspecified
#define ATOMIC_LONG_LOCK_FREE unspecified
#define ATOMIC_LLONG_LOCK_FREE unspecified
#define ATOMIC_POINTER_LOCK_FREE unspecified
Note that these defines apply to both the unsigned and signed variants of the corresponding types.
In the case that the #define
is 1, you have to check at runtime. This is accomplished as follows:
std::atomic<int> my_int;
if (my_int.is_lock_free()) {
// do lock-free stuff
}
if (std::atomic_is_lock_free(&my_int)) {
// also do lock-free stuff
}
std::atomic_bool
and std::atomic<bool>
(and the other types like these) are the same things actuallyYes, these are just typedef
s for your convenience. The full list is found in Table 194 of the standard:
Named type | Integral argument type
----------------+-----------------------
atomic_char | char
atomic_schar | signed char
atomic_uchar | unsigned char
atomic_short | short
atomic_ushort | unsigned short
atomic_int | int
atomic_uint | unsigned int
atomic_long | long
atomic_ulong | unsigned long
atomic_llong | long long
atomic_ullong | unsigned long long
atomic_char16_t | char16_t
atomic_char32_t | char32_t
atomic_wchar_t | wchar_t
std::atomic_flag
is the only class that guarantees platform-independent lock-free operations by the standardCorrect, as guaranteed by §29.7/2 in the standard.
Note that there's no guarantee on the initialization state of atomic_flag
unless you initialize it with the macro as follows:
std::atomic_flag guard = ATOMIC_FLAG_INIT; // guaranteed to be initialized cleared
There is a similar macro for the other atomic types,
The standard does not specify if atomic_flag
could experience the same data race other atomic types could have during construction.
std::memory_order
and how to use it properly?As suggested by @WhozCraig, cppreference.com has the best reference.
And as @erenon suggests, Boost.Atomic has a great essay on how to use memory fences for lock-free programming.
Upvotes: 6