2

After reading "Concurrency in action" I could not find an answer to one question - are there guarantees in the standard for reading side effects when we have one store(release) and many loads(acquire) on one atomic variable? Suppose we have:

int i{};
atomic<bool> b{};

void writer(){
 i=42;
 b.store(true,memory_order_release);
}

void reader(){
 while(!b.load(memory_order_acquire))
    this_thread::yield();
 assert(i==42);
}
//---------------------
thread t1{writer},t2{reader},t3{reader};

If we had only one reader, all ok, but can we have failed assertion in t2 or t3 thread?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Despise
  • 98
  • 2
  • 6
  • Either one of the `reader`s could run before `writer` – Jeffrey Oct 13 '21 at 11:40
  • According to `https://en.cppreference.com/w/cpp/atomic/memory_order` both `memory_order_acquire` and `memory_order_release` only guarantee synchronised access to the `std::stomic` being read/written. ie read/writes of other variables (`i`) are not synchronised. – Richard Critten Oct 13 '21 at 11:54
  • 1
    @АлексейНеудачин my point is that even if every variable was synchronized (they're not), this assert would still fire because `reader` might run first. So, OP should come up with a better test. – Jeffrey Oct 13 '21 at 14:04
  • @Richard Critten, mutex has release/acquire semantics, but it takes an alternation of release and acquire between some 2 threads each time. – Despise Oct 13 '21 at 14:18
  • 3
    @Jeffrey it'll not fire up because thread will stop on `while` – Алексей Неудачин Oct 13 '21 at 15:15
  • @Despise am not sure what your point is - there is no std::mutex in the above code and `atomic` is probably lock free implementation. You can check your implementation using [std::atomic::is_lock_free](https://en.cppreference.com/w/cpp/atomic/atomic/is_lock_free) – Richard Critten Oct 13 '21 at 16:33
  • @Richard Critten, I meant that the acquire and release of an atomic variable is the standard implementation of a spinlock – Despise Oct 13 '21 at 16:43
  • I am almost 100% sure that the assertion cannot fail here. But don't know the correct reasoning, since all those (inter-thread-)happen-before and synchronize-with relations in C++ are terribly counter-intuitive. As was mentioned, if this didn't work, we wouldn't be able to implement spinlock (which needs to be able to synchronize non-atomic memory reads/writes). – Daniel Langr Oct 13 '21 at 18:43
  • Somewhat related, and maybe thinking in the same direction as your question: https://stackoverflow.com/questions/69382626/does-a-single-load-synchronize-with-multiple-stores/69407098#69407098 – Nate Eldredge Oct 14 '21 at 04:42

1 Answers1

6

This is a text-book example of a happens-before relationship between the store to i in writer and the load from i in both reader threads.

That multiple readers are involved does not matter. The store to b synchronizes with all readers that observe the updated value (which will eventually happen thanks to the loop).

I think the quote you're looking for is:

An atomic operation A that performs a release operation on an atomic object M synchronizes with an atomic operation B that performs an acquire operation on M and takes its value from any side effect in the release sequence headed by A.

It does not say that this is limited to a single load operation

LWimsey
  • 6,189
  • 2
  • 25
  • 53
  • 2
    I'm just guessing that the querent might be thinking that a release store only has enough synchronization "mojo" to sync with one reader, and that reader would use up its ability to sync with anything else. If so, it might help to understand that's not how it works. In real implementations, a release operation in a writer orders access to coherent shared cache/memory wrt. earlier loads/stores in that thread. Acquire is similar for the read side, just ordering this CPU core's access to coherent shared state. https://preshing.com/20120913/acquire-and-release-semantics/ – Peter Cordes Oct 13 '21 at 20:48
  • 1
    (This mental model is different from how ISO C++ formalism defines things. In ISO C++ there is not much of a guarantee of a coherent shared state existing, other than the coherency guarantees on single objects. ISO C++ *only* defines things in terms of syncs-with guaranteeing visibility, so if nobody's looking then a release store can in theory be optimized away or relaxed by the as-if rule. But in practice compilers don't try to prove that an atomic object has no acquire-or-stronger readers.) – Peter Cordes Oct 13 '21 at 20:51