Fwiw, I just coded up a Relacy test unit for it. Works like a charm.
Here is the code, using standalone membars to show you exactly where
they need to be:
____________________________
// Mutex Relacy Test...
// Algorithm By: Alex Terekhov
// Relacy Impl By: Chris M. Thomasson
//_______________________________________________
//#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...
// PRODUCERS must equal CONSUMERS for this test
#define PRODUCERS 3
#define CONSUMERS 3
#define THREADS (PRODUCERS + CONSUMERS)
#define ITERS 2
// bin-sema
struct ct_auto_reset_event
{
VAR_T(bool) m_state;
std::mutex m_mutex;
std::condition_variable m_cond;
ct_auto_reset_event() : m_state(false) {}
void signal()
{
{
m_mutex.lock($);
VAR(m_state) = true;
m_mutex.unlock($);
}
m_cond.notify_one($);
}
void wait()
{
m_mutex.lock($);
while (VAR(m_state) == false) m_cond.wait(m_mutex, $);
VAR(m_state) = false; // auto-reset
m_mutex.unlock($);
}
};
// just a layer over an auto-reset event
struct ct_fast_mutex
{
std::atomic<unsigned int> m_state;
ct_auto_reset_event m_waitset;
ct_fast_mutex() : m_state(0) {}
void lock()
{
if (m_state.exchange(1, std::memory_order_relaxed))
{
while (m_state.exchange(2, std::memory_order_relaxed))
{
m_waitset.wait();
}
}
std::atomic_thread_fence(std::memory_order_acquire);
}
void unlock()
{
std::atomic_thread_fence(std::memory_order_release);
if (m_state.exchange(0, std::memory_order_relaxed) == 2)
{
m_waitset.signal();
}
}
};
// Relacy Stack Test...
struct ct_fast_mutex_test
: rl::test_suite<ct_fast_mutex_test, THREADS>
{
VAR_T(unsigned int) g_count;
ct_fast_mutex g_mutex;
ct_fast_mutex_test() : g_count(0) {}
void before()
{
}
void after()
{
RL_ASSERT(VAR(g_count) == 0);
}
void consumer(unsigned int tidx)
{
for (unsigned int i = 0; i < ITERS; ++i)
{
g_mutex.lock();
--VAR(g_count);
g_mutex.unlock();
}
}
void producer(unsigned int tidx)
{
for (unsigned int i = 0; i < ITERS; ++i)
{
g_mutex.lock();
++VAR(g_count);
g_mutex.unlock();
}
}
void thread(unsigned int tidx)
{
if (tidx < PRODUCERS)
{
producer(tidx);
}
else
{
consumer(tidx);
}
}
};
// Test away... Or fly? Humm...
int main()
{
{
rl::test_params p;
p.iteration_count = 10000000;
//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_fast_mutex_test>(p);
}
return 0;
}
____________________________
:^)