Fwiw, a timed wait on an eventcount works as easily as spinning on it wrt not waiting on a kernel waitset at all. The predicate is the user algorithm itself. So, imagine if the waits never actually block, but spin around. Yet, everything still works. Fwiw, the following code, that should compile with Relacy, is the most simplistic eventcount I can imagine:
// Simple Event Count
// For Academic and Experimental things...
// Beginner, Moderate?
// In Good Ol' Relacy! Nice as always.
//_______________________________________________
//#define RL_DEBUGBREAK_ON_ASSERT
//#define RL_MSVC_OUTPUT
//#define RL_FORCE_SEQ_CST
//#define RL_GC
#include <relacy/relacy_std.hpp>
#include <iostream>
// Simple macro based redirection of the verbose std membars.
#define CT_MB_ACQ std::memory_order_acquire
#define CT_MB_REL std::memory_order_release
#define CT_MB_RLX std::memory_order_relaxed
#define CT_MB_ACQ_REL std::memory_order_acq_rel
#define CT_MB_SEQ_CST std::memory_order_seq_cst
#define CT_MB(mp_0) std::atomic_thread_fence(mp_0)
// Some global vars directing the show...
#define WORKERS 5
#define THREADS (WORKERS)
// old school
struct ct_ecount
{
std::mutex m_mutex;
std::condition_variable m_cond;
std::atomic<unsigned int> m_waitbit;
ct_ecount() : m_waitbit(0) {}
void broadcast()
{
CT_MB(CT_MB_SEQ_CST);
unsigned int waitbit = m_waitbit.load(CT_MB_RLX);
if (! waitbit) return;
m_mutex.lock($);
m_waitbit.store(0, CT_MB_RLX);
m_cond.notify_all($);
m_mutex.unlock($);
}
void wait_begin()
{
m_mutex.lock($);
m_waitbit.store(1, CT_MB_RLX);
CT_MB(CT_MB_SEQ_CST);
}
void wait_cancel()
{
m_mutex.unlock($);
}
void wait_commit()
{
m_cond.wait(m_mutex, $);
m_mutex.unlock($);
}
};
// Relacy Multex Test...
struct ct_ecount_test
: rl::test_suite<ct_ecount_test, THREADS>
{
std::atomic<unsigned int> m_signal;
ct_ecount g_ecount;
ct_ecount_test() : m_signal(0) {}
void thread(unsigned int tidx)
{
if (tidx < 2)
{
if (m_signal.fetch_add(1, CT_MB_RLX) == 1)
{
g_ecount.broadcast();
}
}
else
{
unsigned int signal = 0;
while ((signal = m_signal.load(CT_MB_RLX)) != 2)
{
g_ecount.wait_begin();
signal = m_signal.load(CT_MB_RLX);
if (signal == 2)
{
g_ecount.wait_cancel();
break;
}
g_ecount.wait_commit();
}
}
}
};
// Test away... Or fly? Humm...
int main()
{
{
rl::test_params p;
p.iteration_count = 5000000;
//p.execution_depth_limit = 33333;
//p.search_type = rl::sched_bound;
//p.search_type = rl::fair_full_search_scheduler_type;
//p.search_type = rl::fair_context_bound_scheduler_type;
rl::simulate<ct_ecount_test>(p);
}
return 0;
}
think if g_ecount.wait_commit(); was timed... It would still work as is. So, yes an eventcount can easily handle timed waits.