Skip to content

Instantly share code, notes, and snippets.

@yizhang82
Created October 7, 2017 20:41
Show Gist options
  • Save yizhang82/500da684837161055978011c5850d296 to your computer and use it in GitHub Desktop.
Save yizhang82/500da684837161055978011c5850d296 to your computer and use it in GitHub Desktop.
portable lock-free reader/writer lock for C++
class rw_spin_lock
{
public:
rw_spin_lock()
{
_readers = 0;
}
public:
void acquire_reader()
{
int retry = 0;
while (true)
{
uint32_t prev_readers = _readers;
if (prev_readers != HAS_WRITER)
{
uint32_t new_readers = prev_readers + 1;
if (_readers.compare_exchange_weak(prev_readers, new_readers))
{
// we've won the race
return;
}
}
retry++;
if (retry > RETRY_THRESHOLD)
{
retry = 0;
this_thread::yield();
}
}
}
void release_reader()
{
int retry = 0;
while (true)
{
uint32_t prev_readers = _readers;
if (prev_readers != HAS_WRITER && prev_readers > 0)
{
uint32_t new_readers = prev_readers - 1;
if (_readers.compare_exchange_weak(prev_readers, new_readers))
{
// we've won the race
return;
}
}
retry++;
if (retry > RETRY_THRESHOLD)
{
retry = 0;
this_thread::yield();
}
}
}
void acquire_writer()
{
int retry = 0;
while (true)
{
uint32_t prev_readers = _readers;
if (prev_readers == 0)
{
if (_readers.compare_exchange_weak(prev_readers, HAS_WRITER))
{
// we've won the race
return;
}
}
retry++;
if (retry > RETRY_THRESHOLD)
{
// save some cpu cycles
retry = 0;
this_thread::yield();
}
}
}
void release_writer()
{
int retry = 0;
while (true)
{
uint32_t prev_readers = _readers;
if (prev_readers == HAS_WRITER)
{
if (_readers.compare_exchange_weak(prev_readers, 0))
{
// we've won the race
return;
}
}
retry++;
if (retry > RETRY_THRESHOLD)
{
// save some cpu cycles
retry = 0;
this_thread::yield();
}
}
}
private:
const uint32_t HAS_WRITER = 0xffffffff;
const int RETRY_THRESHOLD = 100;
std::atomic<uint32_t> _readers;
};
class reader_lock
{
public:
reader_lock(rw_spin_lock &lock) : _lock(lock)
{
_lock.acquire_reader();
}
~reader_lock()
{
_lock.release_reader();
}
private:
rw_spin_lock &_lock;
};
class writer_lock
{
public:
writer_lock(rw_spin_lock &lock) : _lock(lock)
{
_lock.acquire_writer();
}
~writer_lock()
{
_lock.release_writer();
}
private:
rw_spin_lock &_lock;
};
@alreadydone
Copy link

alreadydone commented Nov 15, 2018

I am wondering why you don't just do _readers--; for release_reader and _readers = 0; for release_writer?

Copy link

ghost commented Dec 21, 2018

You should probably throw an assertion at L41. The thread cannot be calling release_reader() when an exclusive writer or no reader has the lock. It should not be possible. Do you agree?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment