There is a thread discussing the definition of atomic object. The most popular interpretations are the followings:
- By an "atomic object" we understand an object whose public interface exposes only atomic operations, i.e. all operations you can do with that object are atomic.
- The C++ has the standard
std::atomic<T>
class template which fits the above descriptions. - The C++ standard imposes a set of rules on operations and effects of operations on atomic objects ([intro.races]). If all operations on an object satisfy those rules, then that object is atomic.
I see two possible interpretations for the atomic object:
- An
std::atomic<T>
typed object - An underlying
T
typed object managed by anstd::atomic<T>
typed object
Based on the comments and in my understanding "atomic object" should refer an object with std::atomic<T>
type - so the first interpretation is the correct one -, but it has serious consequences, like in the following context:
C++20 standard 6.9.2.1 (15):
If an operation
A
that modifies an atomic objectM
happens before an operationB
that modifiesM
, thenA
shall be earlier thanB
in the modification order ofM
.
There is an std::atomic
operation with the following footprint:
void notify_one() noexcept;
As it is not a const
function it modifies the std::atomic
object, but it doesn't modify the underlying T
object. Does notify_one
modify the atomic object M
? (Does M
refer the std::atomic<T>
typed object, or its underlying T
object?). If it does refer the std::atomic
typed object then C++20 standard 6.9.2.1 (15) can be applied for the notify_one
operations.
It is crucial to know to be able to answer questions like C++20: How is the returning from atomic::wait() guaranteed by the standard?.
The standard is confusing however, e.g. here, it weakens my reasoning and says atomic object is the managed object (at least in the case of atomic_ref
):
An
atomic_ref
object applies atomic operations ([atomics.general]) to the object referenced by*ptr
such that, for the lifetime ([basic.life]) of theatomic_ref
object, the object referenced by*ptr
is an atomic object ([intro.races]).
UPDATE
How could this interpretation affect wait/notify
(C++20: How is the returning from atomic::wait() guaranteed by the standard?)? Let's suppose notify
modifies the atomic object. That modification would be simply an indication that a wait
should be woken up - and nothing to do with the current value managed by the atomic
(so basically a futex wake that modifies the object, stores a flag, or whatever). As both store
and notify
are operations
that modifies an atomic object
M
, thenA
shall be earlier thanB
in the modification order ofM
and
this effectively makes the cache coherence guarantee provided by most hardware available to C++ atomic operations.
So whenever notify
is called after a store
, the effect of that store
operation is guaranteed to be observable on each and every waiting thread when that thread observes notify
(as notify
is later than store
in the total order). If there is a waiting operation "M
is eligible to be unblocked by a call to an atomic notifying operation" then notify
must unblock it, and the value set by store
has to be observable by the unblocked wait
as an implication of 6.9.2.1 (15):
If an operation
A
that modifies an atomic objectM
happens before an operationB
that modifiesM
, thenA
shall be earlier thanB
in the modification order ofM
.
and 6.9.2.1 (4):
All modifications to a particular atomic object
M
occur in some particular total order, called the modification order ofM
.