Difference between std::atomic and std::condition_variable wait, notify_* methods
Asked Answered
A

2

23

I was looking through 'Atomic operations library' and came across a new c++20 feature of atomic 'wait' and 'notify_' methods. I am curious on what the differences are in regards to std::condition_variable's 'wait' and 'notify_' methods.

Airlift answered 12/7, 2020 at 9:56 Comment(1)
The difference is implementation defined. There might be no difference at all besides the interface. I presume the atomic version will be implemented via a more lightweight scheme or something or on the contrary it might perform more spinning or whatever it does in the beginning.Lazulite
B
19

std:atomic wait, notify_all and notify_one methods are similar to methods of conditional variables. They allow the implementation of the logic that previously required conditional variable by using much more efficient and lightweight atomic variables without using a mutex.

The wait function blocks the thread until the value of the atomic object becomes different from a specific value. It takes an argument to compare with the value of the atomic object. And it repeatedly performs:

  • If the values are equal, it blocks the thread until notified by notify_one or notify_all, or the thread is unblocked spuriously.
  • Otherwise, returns.

NOTE: wait is guaranteed to return only if the value has changed, even if underlying implementation unblocks spuriously.


You can find the implementation here: https://github.com/ogiroux/atomic_wait/.

The strategy is chosen this way, by platform:

  • Linux: default to futex (with table), fallback to futex (no table) -> CVs -> timed backoff -> spin.
  • Mac: default to CVs (table), fallback to timed backoff -> spin.
  • Windows: default to futex (no table), fallback to timed backoff -> spin.
  • CUDA: default to timed backoff, fallback to spin. (This is not all checked in in this tree.)
  • Unidentified platform: default to spin.
Boatyard answered 12/7, 2020 at 11:56 Comment(1)
An implementation, I would say. Whereas it is the implementiation of proposal, as a C++2a feature it is implemented in STL implementations. Real implementation can be more sophisticated (it is useful to have CV fallback on Windows too), or less (this implementation checks before notifying futex, whereas it is not mandatory).Cholinesterase
C
10

There's a difference in regard to the whole usage pattern.

condition_variable waiting requires mutex lock. The same mutex lock should be used before notifying:

std::mutex mtx;
std::condition_variable cv;

bool condition();
void change_condition();

...

std::unique_lock<std::mutex> lock(mtx);
while (!condition())
{
   cv.wait(lock);
}

...

std::unique_lock<std::mutex> lock(mtx);
change_condition();
lock.unlock();
cv.notify_one();

Now if you have atomic with condition variable, you still need lock:

std::mutex mtx;
std::condition_variable cv;

std::atomic<bool> condition;

...

std::unique_lock<std::mutex> lock(mtx);
while (!condition.load())
{
   cv.wait(lock);
}

...

std::unique_lock<std::mutex> lock(mtx);
condition.store(true);
lock.unlock();
cv.notify_one();

Atomic by itself does not need a protection with lock, so it can be modified not under lock. However, mutex lock is still needed to synchronize with waiting and avoid lost wakeup. The alternative to waking thread is the following:

condition.store(true);
std::unique_lock<std::mutex> lock(mtx);
lock.unlock();
cv.notify_one();

The mutex locking cannot be omitted, even on notifier side.

(And you cannot get away with condiion_variable_any and "null mutex" that does nothing in its lock / unlock).


Now, atomic wait. Besides no spurious wakeups, mentioned in the other answer, no mutex is needed:


std::atomic<bool> condition;

...

condition.wait(false);

...

condition.store(true);
condition.notify_one();
Cholinesterase answered 13/7, 2020 at 6:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.