One the path to raw C++ that compiles with normal systems.
This is a Relacy test unit! Working wrt my decent rw-mutex, well,
sometimes these exotic atomic and membars can be used to implement your
favorite sync primitive!
C++ Relacy Test Unit
_______________________________
// Unbounded Wait-Free Reader Writer Mutex
// by Chris M. Thomasson Experiment
// In relacy at:
//
http://www.1024cores.net/home/relacy-race-detector
#include <relacy/relacy_std.hpp>
#include <cstdio>
#include <climits>
#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_fence(mb) std::atomic_thread_fence(mb)
struct cpp_sem
{
VAR_T(int) m_count;
std::mutex m_mutex;
std::condition_variable m_cond;
cpp_sem(int count = 0) : m_count(count)
{
RL_ASSERT(count > -1);
}
void sub(int c)
{
RL_ASSERT(c > 0);
m_mutex.lock($);
while (VAR(m_count) < c) m_cond.wait(m_mutex, $);
VAR(m_count) -= c;
m_mutex.unlock($);
}
void add(int c)
{
RL_ASSERT(c > 0);
m_mutex.lock($);
VAR(m_count) += c;
m_mutex.unlock($);
if (c < 2)
{
m_cond.notify_one($);
}
else
{
m_cond.notify_all($);
}
}
};
struct ct_rw_mutex
{
std::atomic<long> m_count;
std::atomic<long> m_rdwake;
std::atomic<long> m_wrlockset_state;
cpp_sem m_wrlockset;
cpp_sem m_wrwset;
cpp_sem m_rdwset;
ct_rw_mutex()
: m_count(INT_MAX),
m_rdwake(0),
m_wrlockset_state(1),
m_wrwset(0),
m_rdwset(0)
{
}
~ct_rw_mutex()
{
RL_ASSERT(m_count.load(mb_relaxed) == INT_MAX);
RL_ASSERT(m_rdwake.load(mb_relaxed) == 0);
}
void wrlock()
{
if (m_wrlockset_state.fetch_sub(1, mb_relaxed) < 1)
{
m_wrlockset.sub(1);
}
mb_fence(mb_acquire);
int count = m_count.fetch_add(-INT_MAX, mb_relaxed);
if (count < INT_MAX)
{
if (m_rdwake.fetch_add(INT_MAX - count, mb_relaxed) +
INT_MAX - count)
{
m_wrwset.sub(1);
}
}
mb_fence(mb_seq_cst);
}
void wrunlock()
{
mb_fence(mb_release);
int count = m_count.fetch_add(INT_MAX, mb_relaxed);
if (m_wrlockset_state.fetch_add(1, mb_relaxed) < 0)
{
m_wrlockset.add(1);
}
// Release Readers after mutex unlock,
// Totally Experimental, works in Relacy! ;^)
if (count < 0)
{
m_rdwset.add(-count);
}
}
void rdlock()
{
int count = m_count.fetch_sub(1, mb_relaxed);
if (count < 1)
{
m_rdwset.sub(1);
}
mb_fence(mb_acquire);
}
void rdunlock()
{
mb_fence(mb_release);
int count = m_count.fetch_add(1, mb_relaxed);
if (count < 0)
{
mb_fence(mb_seq_cst);
if (m_rdwake.fetch_sub(1, mb_relaxed) == 1)
{
m_wrwset.add(1);
}
}
}
};
// Single producer consumer spin queue
#define PRODUCERS 3
#define CONSUMERS 3
#define READERS 4
#define THREADS (PRODUCERS + CONSUMERS + READERS)
#define N 3
#define START 4
static_assert(
PRODUCERS == CONSUMERS && N > 0,
"This test wants the number of producers"
"and consumers to be equal:"
"Also, N needs to be greater than zero; damn it!"
);
struct ct_rw_mutex_test
: rl::test_suite<ct_rw_mutex_test, THREADS>
{
ct_rw_mutex g_rw_mutex;
VAR_T(int) g_shared;
ct_rw_mutex_test() : g_shared(START) {}
void after()
{
RL_ASSERT(VAR(g_shared) == START);
}
void thread_producer(unsigned int tidx)
{
//std::printf("thread_producer::%u\n", tidx);
for (unsigned int i = 0; i < N; ++i)
{
g_rw_mutex.wrlock();
++VAR(g_shared);
g_rw_mutex.wrunlock();
}
}
void thread_consumer(unsigned int tidx)
{
//std::printf("thread_consumer::%u\n", tidx);
for (unsigned int i = 0; i < N; ++i)
{
g_rw_mutex.wrlock();
--VAR(g_shared);
g_rw_mutex.wrunlock();
}
}
void thread_reader(unsigned int tidx)
{
//std::printf("thread_reader::%u\n", tidx);
for (unsigned int i = 0; i < N; ++i)
{
g_rw_mutex.rdlock();
int shared = VAR(g_shared);
g_rw_mutex.rdunlock();
// double check our read range!
RL_ASSERT(
shared > (-CONSUMERS * N + START) &&
shared < (PRODUCERS * N + START)
);
}
}
void thread(unsigned int tidx)
{
if (tidx < PRODUCERS)
{
thread_producer(tidx);
}
else if (tidx < PRODUCERS + CONSUMERS)
{
thread_consumer(tidx);
}
else
{
thread_reader(tidx);
}
}
};
int main()
{
{
rl::test_params p;
p.iteration_count = 14000; // for existing proxy gc code
p.execution_depth_limit = 33333;
p.search_type = rl::random_scheduler_type;
//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_rw_mutex_test>(p);
}
std::puts("\nTest Complete!\n");
std::getchar();
return 0;
}
_______________________________
Will have normal std c++ in a day or two. Posted here, for all of the
wonderful contributors and readers of this fine group.