As far as I can tell, they can use the same mechanism, but don't have to. The implementation I typically use is libstdc++ and its atomic::notify_one() is definitely different.
The condition_variable is fairly opaque, implemented as __condvar in the bits/std_mutex.h header. Its notify_one() just calls a pthread wrapper:
void
notify_one() noexcept
{
int __e __attribute__((__unused__)) = __gthread_cond_signal(&_M_cond);
__glibcxx_assert(__e == 0);
}
whereas notify_all() instead calls __gthread_cond_broadcast.
This contrasts with the atomics, which are much more involved, but end up using a sequence of functions for notifying threads, starting with __atomic_notify_address:
template<typename _Tp>
void
__atomic_notify_address(const _Tp* __addr, bool __all) noexcept
{
__detail::__bare_wait __w(__addr);
__w._M_notify(__all);
}
__waiter_base::_M_notify:
void
_M_notify(bool __all, bool __bare = false)
{
if (_M_laundered())
{
__atomic_fetch_add(_M_addr, 1, __ATOMIC_SEQ_CST);
__all = true;
}
_M_w._M_notify(_M_addr, __all, __bare);
}
__waiter_pool_base::_M_notify:
void
_M_notify(const __platform_wait_t* __addr, bool __all, bool __bare) noexcept
{
if (!(__bare || _M_waiting()))
return;
#ifdef _GLIBCXX_HAVE_PLATFORM_WAIT
__platform_notify(__addr, __all);
#else
if (__all)
_M_cv.notify_all();
else
_M_cv.notify_one();
#endif
}
So if there is no _GLIBCXX_HAVE_PLATFORM_WAIT, libstdc++ will simply use a condition variable to notify either one or all threads. Otherwise, it's implemented using a futex, and may also wake (up to?) INT_MAX threads, see __platform_notify:
template<typename _Tp>
void
__platform_notify(const _Tp* __addr, bool __all) noexcept
{
syscall (SYS_futex, static_cast<const void*>(__addr),
static_cast<int>(__futex_wait_flags::__wake_private),
__all ? INT_MAX : 1);
}
std::atomic::wait()
is a naive spin-wait loop that just keeps reading the variable. If that's what any of your waiters are doing (either temporarily before callingfutex
, or just on a simplistic implementation), they'll wake up on their own. Andnotify_one()
would be a no-op in a truly simplistic implementation without a fallback to OS-assisted sleep/wake. The standard clearly wanted to allow such an implementation, but it's still an interesting question whether any mainstream implementation is like that. Or can wake multiple in other ways. – Fenella