I noticed something strange in your semaphore::wait function. Your code is:
void wait()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_cv.wait(lock, [&]{ return m_count > 0; });
--m_count;
}
I had to alter the predicate to the following function because your
original deadlocks as-is. I decided to focus attention to your semaphore
in Relacy. This version works, notice the difference wrt the predicate
in the wait function:
_______________________________
class semaphore
{
public:
semaphore(unsigned int count) : m_count(count) {}
//semaphore(const semaphore&&) = delete;
//semaphore(semaphore&&) = delete;
//semaphore& operator = (const semaphore&) = delete;
//semaphore& operator = (semaphore&&) = delete;
//~semaphore() = default;
void post()
{
//std::unique_lock<std::mutex> lock(m_mutex);
m_mutex.lock($);
++VAR(m_count);
m_cv.notify_one($);
m_mutex.unlock($);
}
void wait()
{
//std::unique_lock<std::mutex> lock(m_mutex);
//m_cv.wait(lock, [&] { return m_count > 0; });
m_mutex.lock($);
while (VAR(m_count) == 0)
{
m_cv.wait(m_mutex, $);
}
--VAR(m_count);
m_mutex.unlock($);
}
private:
std::mutex m_mutex;
std::condition_variable m_cv;
//unsigned int m_count;
VAR_T(unsigned int) m_count;
};
_______________________________
Fwiw, here is my entire Relacy unit test:
_______________________________
// Queue Test...
//_______________________________________________
//#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 5
class semaphore
{
public:
semaphore(unsigned int count) : m_count(count) {}
//semaphore(const semaphore&&) = delete;
//semaphore(semaphore&&) = delete;
//semaphore& operator = (const semaphore&) = delete;
//semaphore& operator = (semaphore&&) = delete;
//~semaphore() = default;
void post()
{
//std::unique_lock<std::mutex> lock(m_mutex);
m_mutex.lock($);
++VAR(m_count);
m_cv.notify_one($);
m_mutex.unlock($);
}
void wait()
{
//std::unique_lock<std::mutex> lock(m_mutex);
//m_cv.wait(lock, [&] { return m_count > 0; });
m_mutex.lock($);
while (VAR(m_count) == 0)
{
m_cv.wait(m_mutex, $);
}
--VAR(m_count);
m_mutex.unlock($);
}
private:
std::mutex m_mutex;
std::condition_variable m_cv;
//unsigned int m_count;
VAR_T(unsigned int) m_count;
};
// Relacy Stack Test...
struct ct_qtest_test
: rl::test_suite<ct_qtest_test, THREADS>
{
semaphore g_sem;
VAR_T(unsigned int) g_shared;
ct_qtest_test() : g_sem(1) {}
void before()
{
VAR(g_shared) = 0;
}
void after()
{
RL_ASSERT(VAR(g_shared) == 0);
}
void consumer(unsigned int tidx)
{
g_sem.wait();
--VAR(g_shared);
g_sem.post();
}
void producer(unsigned int tidx)
{
g_sem.wait();
++VAR(g_shared);
g_sem.post();
}
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_qtest_test>(p);
}
return 0;
}
_______________________________
This works without any problems.