std::memory_order for std::atomic<T>::wait

Viewed 265

According to cppreference, in C++20 there are wait, notify_one, notify_all in std::atomic<T>. Looks like they make std::atomic<T> usable as a futex.

I'm asking why does wait accept std::memory_order as a parameter. As I always need to check if waking from wait is not spurious, I'll specify the memory order in corresponding load:

  std::atomic<bool> x;

  while (x.load(std::memory_order_acquire) == false)
  {
     x.wait(false, std::memory_order_acquire);
  }

Or should I specify std::memory_order_relaxed for wait? Are there scenarios with wait not followed by load?

2 Answers

All atomic operations on the same object will always have a single order, and the order within a single thread is included in that. That is, relaxed cannot reorder atomic operations on the same object within the same thread.

The relaxed memory order is the order relative to other memory operations besides that specific atomic one. So relaxed is fine for checking for the wait itself, but when you want to actually read data which written by the releasing thread that isn't contained in the atomic value, you'll need a stronger memory order in order to ensure their visibility. So your outer loop should use a more appropriate condition for acquiring visibility, should it be necessary.

I posted this issue to GitHub for this proposal, and got a response that std::atomic::wait is meant to be implemented on a futex with logic over it, specifically to mask spurious wakes.

So, cppreference.com is wrong about this:

These functions are allowed to unblock spuriously, i.e. return due to reasons other than value change or notification.

And while loop is superfluous in my example, I should use just:

std::atomic<bool> x;

x.wait(false, std::memory_order_acquire);
Related