Skip to content

Instantly share code, notes, and snippets.

@cjxgm
Last active August 29, 2015 14:07
Show Gist options
  • Save cjxgm/cc872efe0d8ee539d2e0 to your computer and use it in GitHub Desktop.
Save cjxgm/cc872efe0d8ee539d2e0 to your computer and use it in GitHub Desktop.
a single-reader single-writer wait-free ring buffer
#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