1

i tested this code a lot and its assertion was never failed
but can its assertion fail sometimes ?

#include <iostream>
#include <thread>
#include <atomic>
#include <chrono>
#include <cassert>

std::atomic<bool> x{false};
std::atomic<int> y{0};

void write_x() {
    x.store(true, std::memory_order_release);
}

void write_y() {
    y.store(1, std::memory_order_relaxed);
}

void read_x_then_y() {
    while (!x.load(std::memory_order_acquire));
    std::this_thread::sleep_for(std::chrono::seconds(1)); // allow write_y thread to store 1 in to y befor reading it
    assert(y.load(std::memory_order_relaxed) == 1); // could this assert fail ?
}

int main() {
    std::thread t1{read_x_then_y};
    std::thread t2{write_x};
    std::thread t3{write_y};

    t1.join();
    t2.join();
    t3.join();
}

there is no memory operation before release store in write_x function
so the acquire load in read_x_then_y should never see the store operation happened in write_y function
is it right ?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
AmirCH
  • 49
  • 2
  • 2
    atomics do not give warranties on synchronization between them. They give only warranty that operations on specific value are consistent. For example you can add and subtract on single value and you will not loose information about any of this operations. But you can't depend on relation between atomics. – Marek R Sep 06 '21 at 14:06
  • 1
    Re: "should never see" -- that's not how atomics work. If a result is not required to be visible, that doesn't mean that it's required to not be visible. More generally, atomics promise that if you do X, you'll get the result Y; there is no promise about what happens if you don't do X. – Pete Becker Sep 06 '21 at 14:12
  • @MarekR The C++ sequential consistency flag should ensure that when a thread load a value a a given atomic all the variables read/written before, including other atomics are done (consistently to the view of the given thread). – Jérôme Richard Sep 06 '21 at 15:55
  • 2
    @JérômeRichard: acquire / release gives you that guarantee; seq_cst gives further guarantees, like seq_cst stores becoming visible before later seq_cst loads in the same thread (but *not* guaranteed wrt. non-atomic operations). But yes, the key is "in the same thread", which this code isn't doing except in the reader. That's presumably what Marek meant to say, that operations in different threads don't have any synchronization unless those threads have already synchronized with each other (e.g. spin-wait on `x` in `write_y` might be interesting.) – Peter Cordes Sep 06 '21 at 18:51

1 Answers1

1

In theory yes the assertion can fail. In practice with a 1 second sleep, no unless your system is extremely heavily over-loaded, for example thrashing its swap space.

Spin-wait on x is pointless; there's no synchronization between independent stores by independent writers. If they were both seq_cst then all readers on all cores would be required to agree upon their order, even on a PowerPC system where IRIW reordering is possible (and can happen for acq_rel or weaker). But even seq_cst wouldn't provide any guarantee on which order that was, just that all readers in the same run of the program would agree.

Acquire and release create ordering wrt. other operations in the same thread. https://preshing.com/20120913/acquire-and-release-semantics/

After sleeping for 1 second the stores from both writer threads will be visible unless something is stopping the writer threads from running for that long.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847