Date: Wed, 10 Jun 2026 17:46:37 +0800
Consider this example:
````cpp
#include <atomic>
#include <chrono>
#include <thread>
uint64_t timestamp() {
auto now = std::chrono::steady_clock::now().time_since_epoch();
return
std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
}
int main() {
std::atomic<long int> val = 0;
long int now1, now2;
auto t1 = std::thread([&]() {
val.store(1,relaxed); // #1
now1 = timestamp(); // #2
});
auto t2 = std::thread([&]() {
now2 = timestamp(); // #3
val.load( relaxed ); // #4
});
t1.join();
t2.join();
}
````
This question arises from whether we can determine if a specific execution
outcome is caused by inter-thread latency within the abstract machine. A
possible execution of the above example is that #4 reads 0 even when now1 <
now2.
Both intro.execution p8 <https://eel.is/c++draft/intro.execution#8>
> Given any two evaluations A and B, if A is sequenced before B (or,
equivalently, B is sequenced after A), *then the execution of A shall
precede the execution of B*.
and [stmt.pre] p1
> Except as indicated, statements are executed in sequence
([intro.execution]).
state that the control flow executes expressions in sequential order within
a single thread, provided one evaluation is sequenced before another.
Furthermore, *[time.clock.steady] p1* states:
> Objects of class steady_clock represent clocks for which values of
time_point never decrease as physical time advances and for which values of
time_point advance at a steady rate relative to real time. That is, the
clock may not be adjusted.
and *[time.clock.req] p2* states:
> C1::now(): Returns a time_point object representing the current point in
time.
This implies that calling now() samples a global time point when the
control flow executes it. Since the control flow cannot reach #2 without
first executing #1, #1 must be executed by the control flow at a point in
time no later than the time point returned by #2. The same logic applies to
#3 and #4.
Therefore, when now1 < now2, does it imply that #1 is executed by the
control flow of t1 at a point in time strictly earlier than when #4 is
executed by the control flow of t2, from the perspective of the abstract
machine? (Note that this does not refer to a happens-before relationship,
but rather a temporal comparison of the control flows executing these
expressions.)
As a minor clarification, this is not a question about physical
implementations (which are governed by the "as-if" rule), but rather a
conceptual question about the formal behavior defined by the C++ abstract
machine.
The deduction above is based entirely on existing rules within the
standard, and there seems to be no explicit rule that contradicts this
interpretation. Consequently, this appears to be a gray area in the
specification. If this reasoning is indeed flawed, where exactly does the
flaw lie? Furthermore, are there any specific rules in the standard that
would directly negate this conclusion?
````cpp
#include <atomic>
#include <chrono>
#include <thread>
uint64_t timestamp() {
auto now = std::chrono::steady_clock::now().time_since_epoch();
return
std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
}
int main() {
std::atomic<long int> val = 0;
long int now1, now2;
auto t1 = std::thread([&]() {
val.store(1,relaxed); // #1
now1 = timestamp(); // #2
});
auto t2 = std::thread([&]() {
now2 = timestamp(); // #3
val.load( relaxed ); // #4
});
t1.join();
t2.join();
}
````
This question arises from whether we can determine if a specific execution
outcome is caused by inter-thread latency within the abstract machine. A
possible execution of the above example is that #4 reads 0 even when now1 <
now2.
Both intro.execution p8 <https://eel.is/c++draft/intro.execution#8>
> Given any two evaluations A and B, if A is sequenced before B (or,
equivalently, B is sequenced after A), *then the execution of A shall
precede the execution of B*.
and [stmt.pre] p1
> Except as indicated, statements are executed in sequence
([intro.execution]).
state that the control flow executes expressions in sequential order within
a single thread, provided one evaluation is sequenced before another.
Furthermore, *[time.clock.steady] p1* states:
> Objects of class steady_clock represent clocks for which values of
time_point never decrease as physical time advances and for which values of
time_point advance at a steady rate relative to real time. That is, the
clock may not be adjusted.
and *[time.clock.req] p2* states:
> C1::now(): Returns a time_point object representing the current point in
time.
This implies that calling now() samples a global time point when the
control flow executes it. Since the control flow cannot reach #2 without
first executing #1, #1 must be executed by the control flow at a point in
time no later than the time point returned by #2. The same logic applies to
#3 and #4.
Therefore, when now1 < now2, does it imply that #1 is executed by the
control flow of t1 at a point in time strictly earlier than when #4 is
executed by the control flow of t2, from the perspective of the abstract
machine? (Note that this does not refer to a happens-before relationship,
but rather a temporal comparison of the control flows executing these
expressions.)
As a minor clarification, this is not a question about physical
implementations (which are governed by the "as-if" rule), but rather a
conceptual question about the formal behavior defined by the C++ abstract
machine.
The deduction above is based entirely on existing rules within the
standard, and there seems to be no explicit rule that contradicts this
interpretation. Consequently, this appears to be a gray area in the
specification. If this reasoning is indeed flawed, where exactly does the
flaw lie? Furthermore, are there any specific rules in the standard that
would directly negate this conclusion?
Received on 2026-06-10 09:46:51
