Skip to content

Instantly share code, notes, and snippets.

@brugeman
Last active August 29, 2015 14:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brugeman/34b4de3c16f8587582ba to your computer and use it in GitHub Desktop.
Save brugeman/34b4de3c16f8587582ba to your computer and use it in GitHub Desktop.
C++11 port of WriterReaderPhaser
// 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