Last active
August 29, 2015 14:07
-
-
Save cjxgm/cc872efe0d8ee539d2e0 to your computer and use it in GitHub Desktop.
a single-reader single-writer wait-free ring buffer
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
#include <array> | |
#include <type_traits> | |
#include <stdexcept> | |
#include <thread> | |
#include <chrono> | |
#include <string> | |
#include <iostream> | |
using std::cin; | |
using std::cout; | |
using std::cerr; | |
using std::endl; | |
namespace tue | |
{ | |
namespace mixin | |
{ | |
struct no_copy_ctor { no_copy_ctor() = default; no_copy_ctor(no_copy_ctor const&) = delete; }; | |
struct no_copy_assign { no_copy_assign& operator=(no_copy_assign const&) = delete; }; | |
struct non_copyable : no_copy_ctor, no_copy_assign {}; | |
struct no_move_ctor { no_move_ctor() = default; no_move_ctor(no_move_ctor &&) = delete; }; | |
struct no_move_assign { no_move_assign& operator=(no_move_assign &&) = delete; }; | |
struct non_movable : no_move_ctor, no_move_assign {}; | |
struct non_transferable : non_copyable, non_movable {}; | |
} | |
// single-reader single-writer wait-free ring buffer | |
// usage: see the implementation of member function "push" and "pop", | |
// or a more real-world-like example below in the main function | |
template <class T, int Capacity=32> | |
struct ring : mixin::non_transferable | |
{ | |
static constexpr auto capacity = Capacity; | |
using value_type = T; | |
static constexpr bool is_pod = std::is_pod<value_type>(); | |
static_assert(is_pod, "T must be a POD."); | |
using value_ref = value_type &; | |
using value_rref = value_type &&; | |
using value_cref = value_type const&; | |
using container_type = std::array<value_type, capacity>; | |
bool full() const noexcept { return (used == capacity); } | |
bool empty() const noexcept { return !used; } | |
// evil when empty() | |
value_cref get() const noexcept { return con[head]; } | |
void use() noexcept | |
{ | |
if (try_clear()) | |
return; | |
head = (head + 1) % capacity; | |
used--; | |
} | |
// evil when full() | |
value_ref alloc() noexcept { return con[tail]; } | |
void fill() noexcept | |
{ | |
if (try_clear()) | |
return; | |
tail = (tail + 1) % capacity; | |
used++; | |
} | |
void clear() noexcept { to_clear = true; } | |
void push(value_cref x) noexcept | |
{ | |
alloc() = std::move(x); | |
fill(); | |
} | |
value_type pop() const noexcept | |
{ | |
auto result = get(); | |
use(); | |
return std::move(result); | |
} | |
// throws when full | |
void safe_push(value_cref x) | |
{ | |
if (full()) throw std::out_of_range{"ring full"}; | |
push(x); | |
} | |
// throws when empty | |
value_type safe_pop() const | |
{ | |
if (empty()) throw std::out_of_range{"ring empty"}; | |
return pop(); | |
} | |
private: | |
container_type con; | |
int head{0}; | |
int tail{0}; | |
int used{0}; | |
bool to_clear{false}; | |
bool try_clear() noexcept | |
{ | |
if (!to_clear) return false; | |
head = tail = used = 0; | |
to_clear = false; | |
return true; | |
} | |
}; | |
template <int BufSize=1024, int RingCap=32> | |
using ringbuffer = ring<std::array<int, BufSize>, RingCap>; | |
} | |
int main() | |
{ | |
tue::ringbuffer<1> ring; | |
bool cont = true; | |
using namespace std::literals; | |
std::thread reader{[&] { | |
while (cont) { | |
if (ring.empty()) std::cerr << "lag:empty" << endl; | |
else { | |
auto& buf = ring.get(); | |
std::cout << "got: " << buf[0] << endl; | |
ring.use(); | |
} | |
std::this_thread::sleep_for(300ms); | |
} | |
}}; | |
std::thread writer{[&] { | |
int i=0; | |
while (cont) { | |
while (cont && !ring.full()) { | |
auto& buf = ring.alloc(); | |
buf[0] = i++; | |
std::cout << "wrote: " << buf[0] << endl; | |
std::this_thread::sleep_for(20ms * (i % 40 + 1)); | |
ring.fill(); | |
} | |
while (cont && ring.full()) { | |
std::cerr << "lag:full" << endl; | |
std::this_thread::sleep_for(1000ms); | |
} | |
} | |
}}; | |
std::thread io{[&] { | |
std::string cmd; | |
while (true) { | |
std::getline(std::cin, cmd); | |
if (cmd == "q" || cmd == "quit" || !std::cin) { | |
cont = false; | |
break; | |
} | |
else ring.clear(); | |
} | |
std::cout << "exiting..."; | |
std::cout.flush(); | |
}}; | |
reader.join(); | |
writer.join(); | |
io.join(); | |
std::cout << std::endl; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment