Sunshine/sunshine/thread_safe.h

432 lines
8.4 KiB
C
Raw Normal View History

//
// Created by loki on 6/10/19.
//
2019-12-11 18:06:52 +00:00
#ifndef SUNSHINE_THREAD_SAFE_H
#define SUNSHINE_THREAD_SAFE_H
2020-02-08 15:26:38 +00:00
#include <atomic>
2021-05-17 19:21:57 +00:00
#include <condition_variable>
2020-02-13 21:24:24 +00:00
#include <functional>
2021-05-17 19:21:57 +00:00
#include <mutex>
#include <vector>
#include "utility.h"
namespace safe {
2019-12-11 18:06:52 +00:00
template<class T>
class event_t {
2021-05-18 11:36:12 +00:00
public:
using status_t = util::optional_t<T>;
2019-12-11 18:06:52 +00:00
2021-05-17 19:21:57 +00:00
template<class... Args>
2019-12-11 18:06:52 +00:00
void raise(Args &&...args) {
std::lock_guard lg { _lock };
if(!_continue) {
return;
}
2021-05-17 19:21:57 +00:00
if constexpr(std::is_same_v<std::optional<T>, status_t>) {
_status = std::make_optional<T>(std::forward<Args>(args)...);
}
else {
_status = status_t { std::forward<Args>(args)... };
}
2019-12-11 18:06:52 +00:00
_cv.notify_all();
}
2020-01-19 23:22:13 +00:00
// pop and view shoud not be used interchangebly
2019-12-11 18:06:52 +00:00
status_t pop() {
2021-05-17 19:21:57 +00:00
std::unique_lock ul { _lock };
2019-12-11 18:06:52 +00:00
2021-05-17 19:21:57 +00:00
if(!_continue) {
2019-12-11 18:06:52 +00:00
return util::false_v<status_t>;
}
2021-05-17 19:21:57 +00:00
while(!_status) {
2019-12-11 18:06:52 +00:00
_cv.wait(ul);
2021-05-17 19:21:57 +00:00
if(!_continue) {
2019-12-11 18:06:52 +00:00
return util::false_v<status_t>;
}
}
auto val = std::move(_status);
2021-05-17 19:21:57 +00:00
_status = util::false_v<status_t>;
2019-12-11 18:06:52 +00:00
return val;
}
2020-02-08 15:26:38 +00:00
// pop and view shoud not be used interchangebly
template<class Rep, class Period>
status_t pop(std::chrono::duration<Rep, Period> delay) {
2021-05-17 19:21:57 +00:00
std::unique_lock ul { _lock };
2020-02-08 15:26:38 +00:00
2021-05-17 19:21:57 +00:00
if(!_continue) {
2020-02-08 15:26:38 +00:00
return util::false_v<status_t>;
}
2021-05-17 19:21:57 +00:00
while(!_status) {
if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
2020-02-08 15:26:38 +00:00
return util::false_v<status_t>;
}
}
auto val = std::move(_status);
2021-05-17 19:21:57 +00:00
_status = util::false_v<status_t>;
2020-02-08 15:26:38 +00:00
return val;
}
2020-01-19 23:22:13 +00:00
// pop and view shoud not be used interchangebly
const status_t &view() {
std::unique_lock ul { _lock };
2020-01-19 23:22:13 +00:00
2021-05-17 19:21:57 +00:00
if(!_continue) {
2020-01-19 23:22:13 +00:00
return util::false_v<status_t>;
}
2021-05-17 19:21:57 +00:00
while(!_status) {
2020-01-19 23:22:13 +00:00
_cv.wait(ul);
2021-05-17 19:21:57 +00:00
if(!_continue) {
2020-01-19 23:22:13 +00:00
return util::false_v<status_t>;
}
}
return _status;
}
2019-12-11 18:06:52 +00:00
bool peek() {
std::lock_guard lg { _lock };
2020-02-09 23:33:12 +00:00
return _continue && (bool)_status;
2019-12-11 18:06:52 +00:00
}
void stop() {
2021-05-17 19:21:57 +00:00
std::lock_guard lg { _lock };
2019-12-11 18:06:52 +00:00
_continue = false;
_cv.notify_all();
}
2020-02-09 23:33:12 +00:00
void reset() {
2021-05-17 19:21:57 +00:00
std::lock_guard lg { _lock };
2020-02-09 23:33:12 +00:00
_continue = true;
_status = util::false_v<status_t>;
}
[[nodiscard]] bool running() const {
2019-12-11 18:06:52 +00:00
return _continue;
}
2021-05-17 19:21:57 +00:00
private:
2020-04-11 23:33:17 +00:00
bool _continue { true };
status_t _status { util::false_v<status_t> };
2019-12-11 18:06:52 +00:00
std::condition_variable _cv;
std::mutex _lock;
};
template<class T>
2021-05-18 11:36:12 +00:00
class alarm_raw_t {
public:
using status_t = util::optional_t<T>;
2021-05-18 11:36:12 +00:00
alarm_raw_t() : _status { util::false_v<status_t> } {}
void ring(const status_t &status) {
std::lock_guard lg(_lock);
_status = status;
_cv.notify_one();
}
void ring(status_t &&status) {
std::lock_guard lg(_lock);
_status = std::move(status);
_cv.notify_one();
}
template<class Rep, class Period>
auto wait_for(const std::chrono::duration<Rep, Period> &rel_time) {
std::unique_lock ul(_lock);
return _cv.wait_for(ul, rel_time, [this]() { return (bool)status(); });
}
template<class Rep, class Period, class Pred>
auto wait_for(const std::chrono::duration<Rep, Period> &rel_time, Pred &&pred) {
std::unique_lock ul(_lock);
return _cv.wait_for(ul, rel_time, [this, &pred]() { return (bool)status() || pred(); });
}
template<class Rep, class Period>
auto wait_until(const std::chrono::duration<Rep, Period> &rel_time) {
std::unique_lock ul(_lock);
return _cv.wait_until(ul, rel_time, [this]() { return (bool)status(); });
}
template<class Rep, class Period, class Pred>
auto wait_until(const std::chrono::duration<Rep, Period> &rel_time, Pred &&pred) {
std::unique_lock ul(_lock);
return _cv.wait_until(ul, rel_time, [this, &pred]() { return (bool)status() || pred(); });
}
auto wait() {
std::unique_lock ul(_lock);
_cv.wait(ul, [this]() { return (bool)status(); });
}
template<class Pred>
auto wait(Pred &&pred) {
std::unique_lock ul(_lock);
_cv.wait(ul, [this, &pred]() { return (bool)status() || pred(); });
}
const status_t &status() const {
return _status;
}
status_t &status() {
return _status;
}
void reset() {
_status = status_t {};
}
private:
std::mutex _lock;
std::condition_variable _cv;
status_t _status;
};
template<class T>
using alarm_t = std::shared_ptr<alarm_raw_t<T>>;
template<class T>
alarm_t<T> make_alarm() {
return std::make_shared<alarm_raw_t<T>>();
}
template<class T>
class queue_t {
public:
2021-05-18 11:36:12 +00:00
using status_t = util::optional_t<T>;
queue_t(std::uint32_t max_elements) : _max_elements { max_elements } {}
2021-05-17 19:21:57 +00:00
template<class... Args>
void raise(Args &&...args) {
std::lock_guard ul { _lock };
if(!_continue) {
return;
}
if(_queue.size() == _max_elements) {
_queue.clear();
}
_queue.emplace_back(std::forward<Args>(args)...);
_cv.notify_all();
}
bool peek() {
std::lock_guard lg { _lock };
2020-02-09 23:33:12 +00:00
return _continue && !_queue.empty();
}
2020-02-08 15:26:38 +00:00
template<class Rep, class Period>
status_t pop(std::chrono::duration<Rep, Period> delay) {
std::unique_lock ul { _lock };
2020-02-08 15:26:38 +00:00
2021-05-17 19:21:57 +00:00
if(!_continue) {
2020-02-08 15:26:38 +00:00
return util::false_v<status_t>;
}
2021-05-17 19:21:57 +00:00
while(_queue.empty()) {
if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
2020-02-08 15:26:38 +00:00
return util::false_v<status_t>;
}
}
auto val = std::move(_queue.front());
_queue.erase(std::begin(_queue));
return val;
}
status_t pop() {
std::unique_lock ul { _lock };
2021-05-17 19:21:57 +00:00
if(!_continue) {
return util::false_v<status_t>;
}
2021-05-17 19:21:57 +00:00
while(_queue.empty()) {
_cv.wait(ul);
2021-05-17 19:21:57 +00:00
if(!_continue) {
return util::false_v<status_t>;
}
}
auto val = std::move(_queue.front());
_queue.erase(std::begin(_queue));
return val;
}
std::vector<T> &unsafe() {
return _queue;
}
void stop() {
std::lock_guard lg { _lock };
_continue = false;
_cv.notify_all();
}
[[nodiscard]] bool running() const {
return _continue;
}
private:
bool _continue { true };
std::uint32_t _max_elements;
std::mutex _lock;
std::condition_variable _cv;
std::vector<T> _queue;
};
2020-02-08 15:26:38 +00:00
template<class T>
class shared_t {
public:
using element_type = T;
using construct_f = std::function<int(element_type &)>;
2021-05-17 19:21:57 +00:00
using destruct_f = std::function<void(element_type &)>;
2020-02-08 15:26:38 +00:00
struct ptr_t {
shared_t *owner;
2020-02-08 22:41:27 +00:00
ptr_t() : owner { nullptr } {}
2020-02-08 15:26:38 +00:00
explicit ptr_t(shared_t *owner) : owner { owner } {}
2020-02-08 22:41:27 +00:00
ptr_t(ptr_t &&ptr) noexcept : owner { ptr.owner } {
2020-02-08 15:26:38 +00:00
ptr.owner = nullptr;
}
2020-02-08 22:41:27 +00:00
ptr_t(const ptr_t &ptr) noexcept : owner { ptr.owner } {
if(!owner) {
return;
}
2020-02-08 15:26:38 +00:00
2021-05-17 19:21:57 +00:00
auto tmp = ptr.owner->ref();
2020-02-08 15:26:38 +00:00
tmp.owner = nullptr;
}
ptr_t &operator=(const ptr_t &ptr) noexcept {
2020-02-08 22:41:27 +00:00
if(!ptr.owner) {
release();
return *this;
}
2020-02-08 15:26:38 +00:00
return *this = std::move(*ptr.owner->ref());
}
ptr_t &operator=(ptr_t &&ptr) noexcept {
if(owner) {
release();
}
std::swap(owner, ptr.owner);
return *this;
}
~ptr_t() {
if(owner) {
release();
}
}
2021-05-17 19:21:57 +00:00
operator bool() const {
2020-02-08 15:26:38 +00:00
return owner != nullptr;
}
void release() {
std::lock_guard lg { owner->_lock };
2020-04-11 23:33:17 +00:00
if(!--owner->_count) {
2020-02-08 15:26:38 +00:00
owner->_destruct(*get());
(*this)->~element_type();
}
owner = nullptr;
}
element_type *get() const {
2021-05-17 19:21:57 +00:00
return reinterpret_cast<element_type *>(owner->_object_buf.data());
2020-02-08 15:26:38 +00:00
}
element_type *operator->() {
2021-05-17 19:21:57 +00:00
return reinterpret_cast<element_type *>(owner->_object_buf.data());
2020-02-08 15:26:38 +00:00
}
};
template<class FC, class FD>
2021-05-17 19:21:57 +00:00
shared_t(FC &&fc, FD &&fd) : _construct { std::forward<FC>(fc) }, _destruct { std::forward<FD>(fd) } {}
2020-02-08 15:26:38 +00:00
[[nodiscard]] ptr_t ref() {
2020-04-11 23:33:17 +00:00
std::lock_guard lg { _lock };
2020-02-08 15:26:38 +00:00
if(!_count) {
2020-02-08 15:26:38 +00:00
new(_object_buf.data()) element_type;
2021-05-17 19:21:57 +00:00
if(_construct(*reinterpret_cast<element_type *>(_object_buf.data()))) {
2020-02-08 15:26:38 +00:00
return ptr_t { nullptr };
}
}
++_count;
2020-02-08 15:26:38 +00:00
return ptr_t { this };
}
2021-05-17 19:21:57 +00:00
2020-02-08 15:26:38 +00:00
private:
construct_f _construct;
destruct_f _destruct;
std::array<std::uint8_t, sizeof(element_type)> _object_buf;
2020-04-11 23:33:17 +00:00
std::uint32_t _count;
2020-02-08 15:26:38 +00:00
std::mutex _lock;
};
template<class T, class F_Construct, class F_Destruct>
auto make_shared(F_Construct &&fc, F_Destruct &&fd) {
return shared_t<T> {
std::forward<F_Construct>(fc), std::forward<F_Destruct>(fd)
};
}
2020-02-09 23:33:12 +00:00
using signal_t = event_t<bool>;
2021-05-17 19:21:57 +00:00
} // namespace safe
2019-12-11 18:06:52 +00:00
#endif //SUNSHINE_THREAD_SAFE_H