4

Some colleagues and I are having a discussion about a relaxed atomic boolean that is used to synchronize two threads. We have done some online research and found other samples and snippets dealing with relaxed atomics but we fail to draw any conclusions from those that apply to our example.

The following atomic boolean is shared between two threads.

std::atomic_bool stopping{false};

The first thread enters a loop and only exits once the atomic boolean has been set. Note that the load on each loop iteration is marked relaxed.

// Thread 1
while (!stopping.load(std::memory_order_relaxed))
{
    // ...
}

At some point after the first thread has entered the loop (guaranteed by some other synchronization mechanism), a second thread executes the following statement. Again, note that the store here is marked relaxed.

// Thread 2
stopping.store(true, std::memory_order_relaxed);

Question: from a purely theoretical point of view, is the first thread guaranteed to exit the loop; and why?


As far as we believe to understand relaxed atomics, we think the load operation is not guaranteed to see the modification of the write operation. But other online examples concerning thread-safe counters have made us believe the load is going to pick up the modification after all...

Maarten Bamelis
  • 2,243
  • 19
  • 32
  • IIRC, relaxation allows a compiler/CPU to reoreder statements/instructions (there are no memory barriers), but atomic reads/writes to memory location bound to `stopping` variable are performed. Why should the load operation not see the result of the write operation? – Daniel Langr Dec 12 '18 at 10:31
  • @DanM. Thanks for the link! I am not worried about hoisting in this example, I am even assuming that the load is not hoisted out the loop. My worry here is that the relaxed load never gets to see an up to date value because of its relaxed nature. Would it be possible for the first thread to continuously read an out-of-date value on each loop iteration because the second thread's write is also relaxed? – Maarten Bamelis Dec 12 '18 at 12:56
  • @DanielLangr Are the reads and writes always going to main memory though? Does the relaxed write evict the cached out-of-date value of the first thread? Or does the relaxed nature of the write not require an eviction of out-of-date values in other caches? – Maarten Bamelis Dec 12 '18 at 12:58
  • @MaartenBamelis Cache system is completely transparent from the perspective of running programs. If write to some memory location is cached and another core reads the same memory location, it must read the cached value. This is called cache coherency. – Daniel Langr Dec 12 '18 at 13:28
  • @MaartenBamelis Relaxation of atomic operations does not break atomicity or cache coherence. It merely allows compiler and CPU not to care about preventing reordering. This is important, e.g., if you set an atomic boolean flag that indicates that something has been done before. Then, you must ensure that no reordering happes. In your scenario, there are no reordering issues (as far as we can see). – Daniel Langr Dec 12 '18 at 13:33
  • @MaartenBamelis the answer in the link answers your question. Generally, it's not guaranteed that one thread would ever see (by relaxed load) a result of a operation in another thread. – Dan M. Dec 12 '18 at 14:01
  • @DanM. Would changing the memory order to something like memory_order_acquire and memory_order_release change that? I mean: would that (store(relase)) guarantee that the other thread sees the change (with load(acquire))? – tangens Dec 13 '18 at 11:01
  • @tangens it would if I read/understand https://en.cppreference.com/w/cpp/atomic/memory_order#Release-Acquire_ordering correctly. – Dan M. Dec 13 '18 at 11:10
  • @tangens I disagree with DanM. Whether stores/loads are relaxed has no impact on the discussed topic. It's my opinion, but the same implies from the linked question above. See the quotation in Oliv's answer. There is nothing about relaxation there, it is applied to all atomic operations. – Daniel Langr Dec 14 '18 at 10:37

1 Answers1

3

Thread 1 will go out of the loop after a finite period of time, this is a requirement of the language [basic.exec]/18:

An implementation should ensure that the last value (in modification order) assigned by an atomic or synchronization operation will become visible to all other threads in a finite period of time.

N.B.: If thread 2, latter store false in the atomic, Thread 1 may never see the state true of the atomic.

N.B.2: I have just fall on the title of your question using relaxed atomic to synchronize thread: that is not possible.

Oliv
  • 17,610
  • 1
  • 29
  • 72
  • There is _should ensure_, not _shall ensure_, so technically, there is no guarantee. But that has nothing to do with relaxation of atomic operations. – Daniel Langr Dec 12 '18 at 11:27
  • @DanielLangr You make me curious. Do you know the reason why this requirement had to be nuanced? – Oliv Dec 12 '18 at 11:42
  • Have you read this answer from the question linked by @DanM: https://stackoverflow.com/a/40273121/580083? – Daniel Langr Dec 12 '18 at 11:44
  • @DanielLangr If a thread access the 3rd state in the modification order of an atomic, it has viewed all previous states (even though it did not access those values). So if this paragraph was a requirement hoisting would still be allowed. But in the code above if the boolean has only 2 states, is there any implementation that would make the second state never visible to the second thread??? I can't imagine it. – Oliv Dec 12 '18 at 11:57
  • I can't either, and nobody can't I guess. There is no sensible reason to make such an implementation. It's just "playing with words", since OP asked about guarantees and that _should_ there does not imply such a guarantee. – Daniel Langr Dec 12 '18 at 12:10
  • There is probably no sensible way to define/guarantee how soon the updated value should be seen by another thread and the current vague definition can't provide a strong guarantee for the specific case anyway, i.e. "1 second before the end of the universe" is also a finite time. – Dan M. Dec 13 '18 at 09:55