On 2/19/2021 3:01 PM, Bonita Montero wrote:
Check this out:
https://vorbrodt.blog/2019/02/14/read-write-mutex/
On older rwmutex I invented. Its simple, and is starvation free wrt
reads and writes. Here is a simple Relacy test unit:
__________________________________
// Chris M. Thomassons RW Mutex
//#define RL_DEBUGBREAK_ON_ASSERT
//#define RL_MSVC_OUTPUT
//#define RL_FORCE_SEQ_CST
#include <relacy/relacy_std.hpp>
#include <cstdio>
#include <cstddef>
#if ! defined (NDEBUG)
# define DBG_PRINTF(e) std::printf e
#else
# define DBG_PRINTF(e) ((void)0)
#endif
struct semaphore
{
HANDLE m_waitset;
semaphore(LONG count)
{
m_waitset = CreateSemaphore(nullptr, count, LONG_MAX, nullptr);
}
~semaphore()
{
CloseHandle(m_waitset);
}
void post(LONG count = 1)
{
ReleaseSemaphore(m_waitset, count, nullptr);
}
void wait()
{
WaitForSingleObject(m_waitset, INFINITE);
}
};
class ct_rw_fast_mutex
{
public:
ct_rw_fast_mutex()
: m_wrstate(1), m_count(INT_MAX), m_rdwake(0),
m_rdwset(0), m_wrwset(0), m_wrmtx(0) {}
void read_lock()
{
if (m_count.fetch_add(-1, std::memory_order_acquire) < 1)
m_rdwset.wait();
}
void read_unlock()
{
if (m_count.fetch_add(1, std::memory_order_release) < 0)
if (m_rdwake.fetch_add(-1, std::memory_order_acq_rel) == 1)
m_wrwset.post();
}
void write_lock()
{
if (m_wrstate.fetch_sub(1, std::memory_order_acquire) < 1)
m_wrmtx.wait();
int count = m_count.fetch_add(-INT_MAX, std::memory_order_acquire);
if (count < INT_MAX)
{
int rdwake = m_rdwake.fetch_add(INT_MAX - count,
std::memory_order_acquire);
if (rdwake + INT_MAX - count)
m_wrwset.wait();
}
}
void write_unlock()
{
int count = m_count.fetch_add(INT_MAX, std::memory_order_release);
if (count < 0)
m_rdwset.post(-count);
if (m_wrstate.fetch_add(1, std::memory_order_release) < 0)
m_wrmtx.post();
}
private:
std::atomic<int> m_wrstate;
std::atomic<int> m_count;
std::atomic<int> m_rdwake;
semaphore m_rdwset;
semaphore m_wrwset;
semaphore m_wrmtx;
};
#define ITERS 16
#define WRITERS 3
#define READERS 5
#define THREADS (WRITERS + READERS)
struct proxy_test
: rl::test_suite<proxy_test, THREADS>
{
ct_rw_fast_mutex m_rwmutex;
VAR_T(int) m_shared;
proxy_test() : m_shared(0) {}
~proxy_test() { RL_ASSERT(!VAR(m_shared)); }
void thread(unsigned int tidx)
{
if (tidx < READERS)
{
// readers
for (unsigned long i = 0; i < ITERS; ++i)
{
m_rwmutex.read_lock();
int shared = VAR(m_shared);
m_rwmutex.read_unlock();
RL_ASSERT(shared > -1);
}
}
else
{
// writers
for (unsigned long i = 0; i < ITERS; ++i)
{
m_rwmutex.write_lock();
++VAR(m_shared);
m_rwmutex.write_unlock();
m_rwmutex.write_lock();
--VAR(m_shared);
m_rwmutex.write_unlock();
}
}
}
};
int main()
{
{
rl::test_params p;
p.iteration_count = 10000000;
p.execution_depth_limit = 10000;
//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<proxy_test>(p);
}
return 0;
}
__________________________________
Can you run it?