our_dick/include/util/ring_buffer.hpp
2020-08-21 20:39:43 -07:00

124 lines
3.5 KiB
C++

/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OUR_DICK_UTIL_RING_BUFFER_HPP
#define OUR_DICK_UTIL_RING_BUFFER_HPP
#include <vector> //vector (duh)
#include <cstdlib> //size_t
#include <atomic> //atomic (duh)
#ifdef __cpp_lib_hardware_interference_size
#include <new> //hardware_destructive_interference_size
#endif
#ifndef __cpp_aligned_new
//TODO: custom aligned allocator
#error "Require aligned new allocation"
#endif
namespace util{
template<typename T>
class mpmc_ring_buffer
{
public:
using value_type = T;
using size_type = size_t;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using rvalue_reference = value_type&&;
using const_reference = const value_type&;
private:
#ifdef __cpp_lib_hardware_interference_size
static constexpr size_t cacheline_size = std::hardware_destructive_interference_size;
#else
//Best guess
static constexpr size_t cacheline_size = 64;
#endif
class slot
{
public:
static constexpr size_type active_bit = 1;
private:
//ensure no false sharing with previous slot in queue
alignas(cacheline_size) std::atomic<size_type> m_turn = {0};
alignas(alignof(value_type)) unsigned char m_data[sizeof(value_type)] = {};
//ensure no false sharing with following data
char cachline_padding[cacheline_size - (sizeof(m_data) + sizeof(m_turn))];
public:
slot() = default;
slot(const slot& s);
slot(slot&& s);
~slot();
template<typename... Args>
void construct(Args&&... args);
void destruct();
const_reference get()const&;
reference get()&;
rvalue_reference get()&&;
std::atomic<size_type>& turn();
const std::atomic<size_type>& turn()const;
};
private:
std::vector<slot> m_slots;
//keep head and tail on separate cache lines to prevent thread thrashing
alignas(cacheline_size) std::atomic<size_type> m_head;
alignas(cacheline_size) std::atomic<size_type> m_tail;
public:
explicit mpmc_ring_buffer(size_type capacity);
//copy/move NOT thread safe! requires external locking mechanism!
mpmc_ring_buffer(const mpmc_ring_buffer& m);
constexpr mpmc_ring_buffer(mpmc_ring_buffer&& m);
~mpmc_ring_buffer() = default;
mpmc_ring_buffer& operator=(const mpmc_ring_buffer& m);
constexpr mpmc_ring_buffer& operator=(mpmc_ring_buffer&& m);
//NOT thread safe! requires external locking mechanism!
void resize(size_type newcap);
//NOT thread safe! requires external locking mechanism!
void clear();
template<typename... Args>
void emplace(Args&&... args);
template<typename... Args>
bool try_emplace(Args&&... args);
void push(const_reference t);
void push(rvalue_reference t);
bool try_push(const_reference t);
bool try_push(rvalue_reference t);
void pop(reference t);
bool try_pop(reference t);
private:
constexpr size_type rotation_cnt(size_type t);
};
}
#include "ring_buffer.tpp"
#endif