FrozenHeart
FrozenHeart

Reputation: 20746

C++11 atomic classes and operations -- am I right

Am I right in the following assumptions:

Also where can I find a useful info about std::memory_order and how to use it properly?

Upvotes: 5

Views: 380

Answers (1)

tclamb
tclamb

Reputation: 1469

Let's go through one by one.


  • I don't need to explicitly synchronize an access to the std::atomic<T> objects from different threads on any platform with my own synchronization objects

Yes, 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 platform

Correct. 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 actually

Yes, these are just typedefs 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 standard

Correct, 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.


  • Also where can I find a useful info about 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

Related Questions