A bunch of random code samples
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

534 lignes
13 KiB

#pragma once
#include <mutex>
#include <algorithm>
#include <functional>
#include <memory>
#include <thread>
#include <optional>
namespace _details {
template<typename T>
using optional = std::optional<T>; //< Configuration constant on which optional to use to implement the rest of this file
auto nullopt = std::nullopt; //< Configuration constant on which optional to use to implement the rest of this file
struct awaiter {
void operator()(int duration) {
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
}
};
struct defer_impl {
std::function<void(void)> fn;
~defer_impl() {
fn();
}
};
#ifdef defer
#warning "A defer macro is already defined and will be undefined after this file"
#undef defer
#endif
#define defer(name, h) _details::defer_impl _defer_slice ## name {[&](){(h);}};
struct _do_not_use_t {
};
constexpr _do_not_use_t do_not_use{};
}
namespace container {
template<typename T>
class circular_buffer_iterator {
struct M {
T *buffer;
size_t capacity;
size_t index;
};
M m;
explicit circular_buffer_iterator(M members)
: m{members} {}
public:
using difference_type = size_t;
using value_type = T;
using pointer = T *;
using reference = T &;
using iterator_category = std::bidirectional_iterator_tag;
circular_buffer_iterator(T *buffer, size_t capacity, size_t index)
: m{M{
.buffer = buffer,
.capacity = capacity,
.index = index
}} {}
circular_buffer_iterator &operator++() {
m.index = (m.index + 1) bitand (m.capacity - 1);
return *this;
}
circular_buffer_iterator operator++(int) {
M oth = m;
m.index = (m.index + 1) bitand (m.capacity - 1);
return circular_buffer_iterator{oth};
}
circular_buffer_iterator &operator--() {
m.index = (m.index - 1) bitand (m.capacity - 1);
return *this;
}
circular_buffer_iterator operator--(int) {
M oth = m;
m.index = (m.index - 1) bitand (m.capacity - 1);
return circular_buffer_iterator{oth};
}
T &operator*() {
return m.buffer[m.index];
}
bool operator==(const circular_buffer_iterator &oth) {
return m == oth.m;
}
bool operator!=(const circular_buffer_iterator &oth) {
return m.buffer != oth.m.buffer or m.index != oth.m.index or m.capacity != oth.m.capacity;
}
};
template<typename T>
class circular_buffer {
struct M {
std::unique_ptr<char[]> buffer;
size_t capacity;
size_t start;
size_t end;
};
M m;
M null_members() {
return M{
.buffer = nullptr,
.capacity = 0,
.start = 0,
.end = 0
};
}
static size_t validate_capacity(size_t new_capacity) {
if (new_capacity < 2) return 2;
if ((new_capacity bitand (new_capacity - 1))) {
new_capacity = new_capacity & (new_capacity >> 1);
new_capacity = new_capacity & (new_capacity >> 2);
new_capacity = new_capacity & (new_capacity >> 4);
new_capacity = new_capacity & (new_capacity >> 8);
new_capacity = new_capacity & (new_capacity >> 16);
new_capacity = new_capacity & (new_capacity >> 31);
return new_capacity + 1;
}
return new_capacity;
}
explicit circular_buffer(M s) : m(std::move(s)) {}
public:
circular_buffer()
: m{null_members()} {}
circular_buffer(const circular_buffer &oth)
: m(null_members()) {
if (oth.m.capacity == 0) return;
reserve(oth.m.capacity);
for (const auto &elem: oth) {
push_back(elem);
}
}
circular_buffer(circular_buffer &&oth) noexcept
: m{std::exchange(oth.m, null_members())} {}
circular_buffer &operator=(const circular_buffer &oth) {
if (this == &oth) return *this;
while (pop_front());
reserve(oth.m.capacity);
for (const auto &elem: oth) {
push_back(elem);
}
};
circular_buffer &operator=(circular_buffer &&oth) noexcept {
std::swap(m, oth.m);
}
circular_buffer_iterator<T> begin() {
return circular_buffer_iterator<T>(reinterpret_cast<T *>(m.buffer.get()), m.capacity, m.start);
}
circular_buffer_iterator<T> end() {
return circular_buffer_iterator<T>(reinterpret_cast<T *>(m.buffer.get()), m.capacity, m.end);
}
circular_buffer_iterator<const T> begin() const {
return circular_buffer_iterator<const T>{
m.buffer.get(), m.capacity, m.start
};
}
circular_buffer_iterator<const T> end() const {
return circular_buffer_iterator<const T>{
m.buffer.get(), m.capacity, m.end
};
}
void reserve(size_t new_capacity) {
new_capacity = validate_capacity(new_capacity);
if (new_capacity <= m.capacity) return;
circular_buffer temp{M{
.buffer = std::make_unique<char[]>(new_capacity * sizeof(T)),
.capacity = new_capacity,
.start = 0,
.end = 0
}};
_details::optional <T> value = pop_front();
while (value) {
temp.push_back(std::move(value.value()));
value = pop_front();
}
std::swap(m, temp.m);
}
void push_front(T value) {
if (m.capacity == 0) reserve(2);
size_t new_start = (m.start - 1) bitand (m.capacity - 1);
if (new_start == m.end) {
reserve(m.capacity << 1);
}
new_start = (m.start - 1) bitand (m.capacity - 1);
::new(reinterpret_cast<T *>(m.buffer.get()) + new_start) T(value);
m.start = new_start;
}
_details::optional <T> pop_front() noexcept {
if (m.start == m.end) return _details::nullopt;
_details::optional <T> ret = std::move(*begin());
(*begin()).~T();
m.start = (m.start + 1) bitand (m.capacity - 1);
return ret;
}
void push_back(T value) {
if (m.capacity == 0) reserve(2);
size_t new_end = (m.end + 1) bitand (m.capacity - 1);
if (new_end == m.start) {
reserve(m.capacity << 1);
}
new_end = (m.end + 1) bitand (m.capacity - 1);
::new(reinterpret_cast<T *>(m.buffer.get()) + m.end) T(value);
m.end = new_end;
}
_details::optional <T> pop_back() noexcept {
if (m.start == m.end) return _details::nullopt;
auto last = --end();
_details::optional <T> ret = std::move(*last);
(*last).~T();
m.end = (m.end - 1) bitand (m.capacity - 1);
return ret;
}
bool empty() const {
return m.start == m.end;
}
~circular_buffer() {
while (not empty()) {
pop_front();
}
}
};
}
namespace _details {
template<typename T>
class channel_impl {
mutable std::mutex lock;
container::circular_buffer<T> data;
int active_writer = 0;
int active_reader = 0;
public:
struct reader_t{};
struct writer_t{};
static constexpr reader_t reader{};
static constexpr writer_t writer{};
void close(reader_t) {
std::lock_guard<std::mutex> d{lock};
active_reader -= 1;
}
void close(writer_t) {
std::lock_guard<std::mutex> d{lock};
active_writer -=1;
}
void register_one(reader_t, _details::_do_not_use_t) {
std::lock_guard<std::mutex> d{lock};
active_reader += 1;
}
void register_one(writer_t, _details::_do_not_use_t) {
std::lock_guard<std::mutex> d{lock};
active_writer += 1;
}
bool closed(reader_t) {
std::lock_guard<std::mutex> d{lock};
return active_reader == 0;
}
bool closed(writer_t) {
std::lock_guard<std::mutex> d{lock};
return active_writer == 0;
}
bool push(const T& elem) {
std::lock_guard<std::mutex> d{lock};
if(active_reader == 0) return true;
if(active_writer == 0) return false;
data.push_front(elem);
return true;
}
bool push(T&& elem) {
std::lock_guard<std::mutex> d{lock};
if(active_reader == 0) return true;
if(active_writer == 0) return false;
data.push_front(elem);
return true;
}
_details::optional<T> pop() {
std::lock_guard<std::mutex> d{lock};
if(active_reader == 0) return _details::nullopt;
return data.pop_back();
}
bool empty() const {
return active_writer == 0 && data.empty();
}
};
}
/**
* This is a read only channel of its specified template parameter type
* @tparam T The type of element that is transmitted through the channel
*/
template<typename T>
class channel_r {
std::shared_ptr<_details::channel_impl<T>> impl;
public:
/**
* This constructor should **NOT** be used
*/
channel_r(_details::_do_not_use_t, std::shared_ptr<_details::channel_impl<T>> _impl)
: impl(_impl) {
impl->register_one(_details::channel_impl<T>::reader, _details::do_not_use);
}
/**
* Copy constructor for channel receiver
*/
channel_r(const channel_r& oth) {
if(oth.impl.get() == impl.get()) return;
impl = oth.impl;
if(impl) impl->register_one(_details::channel_impl<T>::reader, _details::do_not_use);
}
/**
* Obtains a value from the queue
* @return either a value if one is available in the queue, or no value
*/
_details::optional<T> pop() {
return impl->pop();
}
/**
* Verifies if the queue is empty and has no sender available
* @return true if the queue will never contain new elements
* @return false if the queue may at some point in the future contain new elements
*/
bool empty() const {
return impl->empty();
}
~channel_r() {
impl->close(_details::channel_impl<T>::reader);
}
/**
* Verifies if the writer side is still open
* @return true if the writer side is closed
* @return false if the writer side is still open
*/
bool closed() {
return impl->closed(_details::channel_impl<T>::writer);
}
};
/**
* This is a write only channel of its specified template parameter type
* @tparam T The type of element that is transmitted through the channel
*/
template<typename T>
class channel_w {
std::shared_ptr<_details::channel_impl<T>> impl;
public:
/**
* This constructor should **NOT** be used
*/
channel_w(_details::_do_not_use_t, std::shared_ptr<_details::channel_impl<T>> _impl)
: impl(_impl) {
impl->register_one(_details::channel_impl<T>::writer, _details::do_not_use);
}
/**
* Copy constructor for channel sender
*/
channel_w(const channel_w& oth) {
if(oth.impl.get() == impl.get()) return;
impl = oth.impl;
if(impl) impl->register_one(_details::channel_impl<T>::writer, _details::do_not_use);
}
/**
* Sends an element through the channel
* @param elem the element to send
* @return true is the element was successfully pushed
*/
bool push(const T& elem) {
return impl->push(elem);
}
/**
* Sends an element through the channel
* @param elem the element to send
* @return true is the element was successfully pushed
*/
bool push(T&& elem) {
return impl->push(std::forward<T>(elem));
}
~channel_w() {
impl->close(_details::channel_impl<T>::writer);
}
/**
* Verifies if the reader side is still open
* @return true if the reader side is closed
* @return false if the reader side is still open
*/
bool closed() {
return impl->closed(_details::channel_impl<T>::reader);
}
};
/**
* Creates a pipe made of a reader channel and a writer channel allowing to transmit objects of the specified type between units of concurrency
* @tparam T The type to allow transfers of
* @return a pair `[writer, reader]` that are connected
*/
template<typename T>
std::pair<channel_w<T>, channel_r<T>> make_pipe() {
auto shared_ch = std::make_shared<_details::channel_impl<T>>();
return {{_details::do_not_use, shared_ch}, {_details::do_not_use, shared_ch}};
}
template<typename T, typename awaiter = _details::awaiter>
class promise;
/**
* This class allows instantiation and manipulation of the receiving side of a promise
* @tparam T The type that will be received
* @tparam awaiter A type that has an `operator()(int)` that represent how this object must wait
*/
template<typename T, typename awaiter = _details::awaiter>
class future {
channel_r<T> conduit;
friend class promise<T, awaiter>;
future(channel_r<T>&& c) : conduit(c) {};
public:
/**
* Waits for the future to be set
*/
void wait() {
for(;;) {
if (conduit.closed()) return;
awaiter{}(30);
}
}
/**
* Obtains the received value.
*
* @note This method should never be called before wait, and should not be called more than once
* @return
*/
_details::optional<T> get() {
return conduit.pop();
}
};
/**
* This class allows to represent waiting for a value from one unit of concurrency into another one.
* @tparam T The type of the value that will be transmitted
* @tparam awaiter A type that has an `operator()(int)` that represent how `future`s created by this object must wait
*/
template<typename T, typename awaiter>
class promise {
_details::optional<channel_w<T>> conduit;
_details::optional<channel_r<T>> future_conduit;
public:
/**
* Initializes a valid promise
*/
promise() {
auto pipe = make_pipe<T>();
conduit = std::move(pipe.first);
future_conduit = std::move(pipe.second);
}
/**
* Obtains the receiving end of this promise
*
* @note This method should never be called more than once
* @return A future that can be awaited from
*/
future<T, awaiter> get_future() {
defer(cleanup, future_conduit = _details::nullopt);
return future<T, awaiter> {
std::move(future_conduit.value())
};
}
/**
* Sets the value of the future to the provided value
*
* @note This method and methods sharing the same name should never be called more than once
* @param value The value to transmit
*/
void set_value(T&& value) {
conduit.value().push(std::forward<T>(value));
conduit = _details::nullopt;
}
/**
* Sets the value of the future to the provided value
*
* @note This method and methods sharing the same name should never be called more than once
* @param value The value to transmit
*/
void set_value(const T& value) {
conduit.value().push(value);
conduit = _details::nullopt;
}
};
#undef defer