|
|
@ -0,0 +1,534 @@ |
|
|
|
#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
|