Within one thread, steady_clock::now() is guaranteed to return monotonically increasing values. How does this interact with memory ordering and reads observed by multiple threads?
atomic<int> arg{0};
steady_clock::time_point a, b, c, d;
int e;
thread t1([&](){
    a = steady_clock::now();
    arg.store(1, memory_order_release);
    b = steady_clock::now();
});
thread t2([&](){
    c = steady_clock::now();
    e = arg.load(memory_order_acquire);
    d = steady_clock::now();
});
t1.join();
t2.join()
assert(a <= b);
assert(c <= d);
Here's the important bit:
if (e) {
    assert(a <= d);
} else {
    assert(c <= b);
}
Can these assert ever fail? Or have I misunderstood something about acquire release memory order?
What follows is mostly an explanation and elaboration of my code example.
Thread t1 writes to the atomic arg. It also records the current time before and after the write in a and b respectively. steady_clock guarantees that a <= b.
Thread t2 reads from the atomic arg and saves the value read in e. It also records the current time before and after the read in c and d respectively. steady_clock guarantees that c <= d.
Both threads are then joined. At this point e could be 0 or 1.
If e is 0, then t2 read the value before t1 wrote it. Does this also imply that c = now() in t2 happened before b = now() in t1?
If e is 1 then t1 wrote the value before t2 read it. Does this also imply that a = now() in t1 happened before d = now() in t2?
Here are some existing questions that don't answer what I'm asking:
Is there any std::chrono thread safety guarantee even with multicore context?
I'm not asking whether now() is thread-safe. I know it is.
Is steady_clock monotonic across threads?
This one is much closer, but that example uses mutex. Can I make the same assumptions about memory orderings weaker than seq_cst?
                        
The question is unfortunately incomplete. Acquire and release memory orders prevent reordering in one direction only. Consider
t1:aindeed gets assigned beforearg.store. But the compiler is free to reorder the assignment tobto happen before as well.Similarly, in
t2:dindeed gets assigned afterarg.load. But the compiler is free to reorder the assignment tocto happen after as well.Therefore
assert(c <= b)may fail.There is however one caveat. This is only true if
steady_clock::now()is known to have no observable effects. However, on platforms where I have looked at its implementation, this results in a call to some opaque library function. Compilers must assume that such calls could have observable effects (such as modifying a volatile variable or terminating the program), and therefore will not reorder the calls.The question would be valid if the important asserts consisted solely of
or if both memory orders were
acq_relorseq_cst.So, given that either the memory orders are corrected or the assert is checked only when
e != 0, I can be sure that the relevant calls tosteady_clock::now()actually happen in the correct order, even across threads.But does this also imply that the values returned are consistent with that order across both
t1andt2, rather than just withint1andt2in isolation?I don't know, which is why I won't accept this answer.