Must I call atomic load/store explicitly?
Asked Answered
T

3

91

C++11 introduced the std::atomic<> template library. The standard specifies the store() and load() operations to atomically set / get a variable shared by more than one thread.

My question is: are assignment and access operations also atomic? Namely, given

std::atomic<bool> stop(false);
  • Is !stop.load() equivalent to !stop?
  • Is stop.store(true) equivalent to stop = true?
Teece answered 17/9, 2013 at 13:7 Comment(1)
stop.load(std::memory_order_relaxed) and stop.store(true, std::memory_order_relaxed); should be fine here, as Serge says. You just need the store to be seen promptly, and relaxed still guarantees that. You only need a stronger ordering if you need to synchronize other data.Antimere
J
73

Are assignment and access operations for non-reference types also atomic?

Yes, they are. atomic<T>::operator T and atomic<T>::operator= are equivalent to atomic<T>::load and atomic<T>::store respectively. All the operators are implemented in the atomic class such that they will use atomic operations as you would expect.

I'm not sure what you mean about "non-reference" types? Not sure how reference types are relevant here.

Joyless answered 17/9, 2013 at 13:35 Comment(3)
Equivalent to the versions of load and store without a consistency control parameter, that is.Page
@user1131467 - thanks. Removed the 'reference', well, reference ;-)Teece
@BenVoigt All it means is that the most stringent memory order is used: std::memory_order_seq_cst. This prevents one from shooting oneself in the foot, but is possibly a premature pessimization.Lithography
U
42

You can do both, but the advantage of load()/store() is that they allow to specify memory order. It is important sometimes for performance, where you can specify std::memory_order_relaxed while atomic<T>::operator T and atomic<T>::operator= would use the most safe and slow std::memory_order_seq_cst. Sometimes it is important for correctness and readability of your code: although the default std::memory_order_seq_cst is most safe thus most likely to be correct, it is not immediately clear for the reader what kind of operation (acquire/release/consume) you are doing, or whether you are doing such operation at all (to answer: isn't relaxed order sufficient here?).

Unmoor answered 31/5, 2017 at 14:54 Comment(0)
T
0

Yes, they are equivalent. The standard says in [atomics.ref.ops]:

T operator=(T desired) const noexcept;

Effects: Equivalent to:

store(desired);
return desired;

and

T load(memory_order order = memory_order::seq_cst) const noexcept;

Effects: Equivalent to: return load();

Note that both = and the conversion function use std::memory_order::seq_cst as a consequence, which is the safest memory order, but not the fastest due to the additional synchronization needed.

Why do we need both?

This is in part to create compatibility with C atomics. C11 added the _Atomic qualifier, so you could write code such as:

_Atomic(int) x;
x = 0;

And exactly the same code would work nowadays in C++. An explicit load and store is only necessary if you care about the memory order for performance reasons.

However, it is good practice to use load and store with a memory order consistently. This way, you can always express which memory order you intended to use. The operator overloads don't express this intent.

Transatlantic answered 18/9, 2023 at 11:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.