Wrt the use case above, here is a crude eventcount (called waitset)
implementation that uses Relacy Race Detector, but its easy to turn into
pure C++11. I put the standalone membars in some macros, sorry for that!
#define mb_relaxed std::memory_order_relaxed
#define mb_consume std::memory_order_consume
#define mb_acquire std::memory_order_acquire
#define mb_release std::memory_order_release
#define mb_acq_rel std::memory_order_acq_rel
#define mb_seq_cst std::memory_order_seq_cst
#define mb_relaxed_fence() std::atomic_thread_fence(mb_relaxed)
#define mb_consume_fence() std::atomic_thread_fence(mb_consume)
#define mb_acquire_fence() std::atomic_thread_fence(mb_acquire)
#define mb_release_fence() std::atomic_thread_fence(mb_release)
#define mb_acq_rel_fence() std::atomic_thread_fence(mb_acq_rel)
#define mb_seq_cst_fence() std::atomic_thread_fence(mb_seq_cst)
class waitset
{
std::mutex m_mutex;
std::condition_variable m_cond;
std::atomic<bool> m_waitbit;
VAR_T(unsigned) m_waiters;
public:
waitset()
: m_waitbit(false),
m_waiters(0)
{
}
~waitset()
{
bool waitbit = m_waitbit.load(mb_relaxed);
unsigned waiters = VAR(m_waiters);
RL_ASSERT(! waitbit && ! waiters);
}
private:
void prv_signal(bool waitbit, bool broadcast)
{
if (! waitbit) return;
m_mutex.lock($);
unsigned waiters = VAR(m_waiters);
if (waiters < 2 || broadcast)
{
m_waitbit.store(false, mb_relaxed);
}
m_mutex.unlock($);
if (waiters)
{
if (! broadcast)
{
m_cond.notify_one($);
}
else
{
m_cond.notify_all($);
}
}
}
public:
unsigned wait_begin()
{
m_mutex.lock($);
m_waitbit.store(true, mb_relaxed);
mb_seq_cst_fence();
return 0;
}
bool wait_try_begin(unsigned& key)
{
if (! m_mutex.try_lock($)) return false;
m_waitbit.store(true, mb_relaxed);
mb_seq_cst_fence();
return true;
}
void wait_cancel(unsigned key)
{
unsigned waiters = VAR(m_waiters);
if (! waiters)
{
m_waitbit.store(false, mb_relaxed);
}
m_mutex.unlock($);
}
void wait_commit(unsigned key)
{
++VAR(m_waiters);
m_cond.wait(m_mutex, $);
if (! --VAR(m_waiters))
{
m_waitbit.store(false, mb_relaxed);
}
m_mutex.unlock($);
}
public:
void signal()
{
mb_seq_cst_fence();
bool waitbit = m_waitbit.load(std::memory_order_relaxed);
prv_signal(waitbit, false);
}
void broadcast()
{
mb_seq_cst_fence();
bool waitbit = m_waitbit.load(std::memory_order_relaxed);
prv_signal(waitbit, true);
}
};