Last active
August 29, 2015 14:26
-
-
Save brugeman/34b4de3c16f8587582ba to your computer and use it in GitHub Desktop.
C++11 port of WriterReaderPhaser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// C++11 port of WriterReaderPhaser, see | |
// http://stuff-gil-says.blogspot.ru/2014/11/writerreaderphaser-story-about-new.html | |
// Ported by Artur Brugeman and released to the public domain, | |
// as explained at http://creativecommons.org/publicdomain/zero/1.0/ | |
#include <atomic> | |
#include <limits> | |
#include <chrono> | |
#include <stdexcept> | |
#include <mutex> | |
#include <thread> | |
class writer_reader_phaser_t | |
{ | |
public: | |
typedef long long epoch_t; | |
writer_reader_phaser_t () | |
: start_epoch_ (0) | |
, even_end_epoch_ (0) | |
, odd_end_epoch_ (EPOCH_MIN) | |
, reader_lock_ (reader_mtx_, std::defer_lock) | |
{} | |
epoch_t writer_critical_section_enter () | |
{ | |
return start_epoch_.fetch_add (1); | |
} | |
void writer_critical_section_exit (const epoch_t start_epoch) | |
{ | |
if (start_epoch < 0) | |
odd_end_epoch_.fetch_add (1); | |
else | |
even_end_epoch_.fetch_add (1); | |
} | |
void reader_lock () | |
{ | |
reader_lock_.lock (); | |
} | |
void reader_unlock () | |
{ | |
reader_lock_.unlock (); | |
} | |
void flip_phase (const long yield_time_ns = 0) | |
{ | |
if (!reader_lock_.owns_lock ()) | |
throw std::logic_error ("flip_phase() can only be called " | |
"while holding the reader_lock()"); | |
const bool next_phase_is_even = start_epoch_ < 0; | |
const epoch_t initial_state_value = next_phase_is_even ? 0 : EPOCH_MIN; | |
auto & next_end_epoch = next_phase_is_even | |
? even_end_epoch_ | |
: odd_end_epoch_; | |
next_end_epoch = initial_state_value; | |
const auto start_value_at_flip = | |
start_epoch_.exchange (initial_state_value); | |
const auto & end_epoch = next_phase_is_even | |
? odd_end_epoch_ | |
: even_end_epoch_; | |
bool caught_up = false; | |
do | |
{ | |
caught_up = end_epoch == start_value_at_flip; | |
if (!caught_up) | |
{ | |
if (yield_time_ns == 0) | |
std::this_thread::yield (); | |
else | |
std::this_thread::sleep_for ( | |
std::chrono::nanoseconds (yield_time_ns)); | |
} | |
} | |
while (!caught_up); | |
} | |
private: | |
static const auto EPOCH_MIN = std::numeric_limits<epoch_t>::min (); | |
typedef std::atomic<epoch_t> along_t; | |
along_t start_epoch_; | |
along_t even_end_epoch_; | |
along_t odd_end_epoch_; | |
std::mutex reader_mtx_; | |
std::unique_lock<std::mutex> reader_lock_; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment