0
votes

I think I mostly understand the semantics of the various memory_order flags in the C++ atomic library.

However, I'm confused about the following situation:

Suppose we have two threads - Thread A, which is a "main execution" thread, and Thread B, which is some arbitrary thread that is part of a thread pool where tasks can be scheduled and run.

If I perform a "read-write-update" atomic operation using std::memory_order_acq_rel, then perform a non-atomic write on a boolean variable, is the non-atomic write immediately visible to other threads? I would think the answer is no, unless the other threads also access the atomic variable that did the "read-write-update" operation.

So, for example, given a global std::atomic_flag variable X, a global bool value B, and a thread pool object THREADPOOL that has a member function dispatch, which will execute arbitrary function handlers in another thread:

if (!X.test_and_set(std::memory_order_acq_rel)
{
   if (SOME_CONDITION) B = true;
   THREADPOOL.dispatch([]() { 
      // This executes in Thread B
      if (B) { /* do something */ } // are we guaranteed to see changes to B?
   });
}

So in this example, the code inside the lambda function will be executed in a different thread. Is that thread necessarily going to see the (non-atomic) update to B made in the first thread? Note that the second thread does not access the atomic_flag, so my understanding is that changes to B will not necessarily be seen in the second thread.

Is my understanding correct here? And if so, would using std::memory_order_seq_cst change that?

2

2 Answers

3
votes

Correct implementation of dispatch method in THREADPOOL should provide happens-before relation between all operations executed by the caller before this method call and all operations executed by the function(lambda in your case), passed to the method.

So, auxiliary thread, executed your lambda function, will definitely see value of B, assigned by the main thread.

Without happens-before order, the only way to garantee immediate visibility of variable modification is to use std::memory_order_seq_cst for both modification and reading. See, e.g., this question.

3
votes

No memory order specification makes future memory accesses visible. At most, they prevent them from becoming visible before the atomic access is visible.

If you want to ensure a particular access does become visible, you must either enforce a particular memory ordering on that access or you must have a future access that uses memory ordering to ensure it is sequenced after the access you want to make visible.

All atomic operations are atomic. Memory ordering only allows you to do three things:

  1. Establish ordering of this atomic operation with respect to prior operations, atomic or not -- this operation is guaranteed to come after them.

  2. Establish ordering of this operation with respect to future operations, atomic or not -- this operation is guaranteed to come before them.

  3. Establish ordering with other atomic operations.

None of these ensure future non-atomic operations occur "soon" or become visible at any particular time.