mirror of
https://github.com/LizardByte/Sunshine.git
synced 2024-11-18 11:10:04 +00:00
512 lines
10 KiB
C++
512 lines
10 KiB
C++
//
|
|
// Created by loki on 6/10/19.
|
|
//
|
|
|
|
#ifndef SUNSHINE_THREAD_SAFE_H
|
|
#define SUNSHINE_THREAD_SAFE_H
|
|
|
|
#include <atomic>
|
|
#include <condition_variable>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <vector>
|
|
|
|
#include "utility.h"
|
|
|
|
namespace safe {
|
|
template<class T>
|
|
class event_t {
|
|
public:
|
|
using status_t = util::optional_t<T>;
|
|
|
|
template<class... Args>
|
|
void raise(Args &&...args) {
|
|
std::lock_guard lg { _lock };
|
|
if(!_continue) {
|
|
return;
|
|
}
|
|
|
|
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)... };
|
|
}
|
|
|
|
_cv.notify_all();
|
|
}
|
|
|
|
// pop and view shoud not be used interchangebly
|
|
status_t pop() {
|
|
std::unique_lock ul { _lock };
|
|
|
|
if(!_continue) {
|
|
return util::false_v<status_t>;
|
|
}
|
|
|
|
while(!_status) {
|
|
_cv.wait(ul);
|
|
|
|
if(!_continue) {
|
|
return util::false_v<status_t>;
|
|
}
|
|
}
|
|
|
|
auto val = std::move(_status);
|
|
_status = util::false_v<status_t>;
|
|
return val;
|
|
}
|
|
|
|
// pop and view shoud not be used interchangebly
|
|
template<class Rep, class Period>
|
|
status_t pop(std::chrono::duration<Rep, Period> delay) {
|
|
std::unique_lock ul { _lock };
|
|
|
|
if(!_continue) {
|
|
return util::false_v<status_t>;
|
|
}
|
|
|
|
while(!_status) {
|
|
if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
|
|
return util::false_v<status_t>;
|
|
}
|
|
}
|
|
|
|
auto val = std::move(_status);
|
|
_status = util::false_v<status_t>;
|
|
return val;
|
|
}
|
|
|
|
// pop and view shoud not be used interchangebly
|
|
const status_t &view() {
|
|
std::unique_lock ul { _lock };
|
|
|
|
if(!_continue) {
|
|
return util::false_v<status_t>;
|
|
}
|
|
|
|
while(!_status) {
|
|
_cv.wait(ul);
|
|
|
|
if(!_continue) {
|
|
return util::false_v<status_t>;
|
|
}
|
|
}
|
|
|
|
return _status;
|
|
}
|
|
|
|
bool peek() {
|
|
return _continue && (bool)_status;
|
|
}
|
|
|
|
void stop() {
|
|
std::lock_guard lg { _lock };
|
|
|
|
_continue = false;
|
|
|
|
_cv.notify_all();
|
|
}
|
|
|
|
void reset() {
|
|
std::lock_guard lg { _lock };
|
|
|
|
_continue = true;
|
|
|
|
_status = util::false_v<status_t>;
|
|
}
|
|
|
|
[[nodiscard]] bool running() const {
|
|
return _continue;
|
|
}
|
|
|
|
private:
|
|
bool _continue { true };
|
|
status_t _status { util::false_v<status_t> };
|
|
|
|
std::condition_variable _cv;
|
|
std::mutex _lock;
|
|
};
|
|
|
|
template<class T>
|
|
class alarm_raw_t {
|
|
public:
|
|
using status_t = util::optional_t<T>;
|
|
|
|
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:
|
|
using status_t = util::optional_t<T>;
|
|
|
|
queue_t(std::uint32_t max_elements = 32) : _max_elements { max_elements } {}
|
|
|
|
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() {
|
|
return _continue && !_queue.empty();
|
|
}
|
|
|
|
template<class Rep, class Period>
|
|
status_t pop(std::chrono::duration<Rep, Period> delay) {
|
|
std::unique_lock ul { _lock };
|
|
|
|
if(!_continue) {
|
|
return util::false_v<status_t>;
|
|
}
|
|
|
|
while(_queue.empty()) {
|
|
if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
|
|
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 };
|
|
|
|
if(!_continue) {
|
|
return util::false_v<status_t>;
|
|
}
|
|
|
|
while(_queue.empty()) {
|
|
_cv.wait(ul);
|
|
|
|
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;
|
|
};
|
|
|
|
template<class T>
|
|
class shared_t {
|
|
public:
|
|
using element_type = T;
|
|
|
|
using construct_f = std::function<int(element_type &)>;
|
|
using destruct_f = std::function<void(element_type &)>;
|
|
|
|
struct ptr_t {
|
|
shared_t *owner;
|
|
|
|
ptr_t() : owner { nullptr } {}
|
|
explicit ptr_t(shared_t *owner) : owner { owner } {}
|
|
|
|
ptr_t(ptr_t &&ptr) noexcept : owner { ptr.owner } {
|
|
ptr.owner = nullptr;
|
|
}
|
|
|
|
ptr_t(const ptr_t &ptr) noexcept : owner { ptr.owner } {
|
|
if(!owner) {
|
|
return;
|
|
}
|
|
|
|
auto tmp = ptr.owner->ref();
|
|
tmp.owner = nullptr;
|
|
}
|
|
|
|
ptr_t &operator=(const ptr_t &ptr) noexcept {
|
|
if(!ptr.owner) {
|
|
release();
|
|
|
|
return *this;
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
operator bool() const {
|
|
return owner != nullptr;
|
|
}
|
|
|
|
void release() {
|
|
std::lock_guard lg { owner->_lock };
|
|
|
|
if(!--owner->_count) {
|
|
owner->_destruct(*get());
|
|
(*this)->~element_type();
|
|
}
|
|
|
|
owner = nullptr;
|
|
}
|
|
|
|
element_type *get() const {
|
|
return reinterpret_cast<element_type *>(owner->_object_buf.data());
|
|
}
|
|
|
|
element_type *operator->() {
|
|
return reinterpret_cast<element_type *>(owner->_object_buf.data());
|
|
}
|
|
};
|
|
|
|
template<class FC, class FD>
|
|
shared_t(FC &&fc, FD &&fd) : _construct { std::forward<FC>(fc) }, _destruct { std::forward<FD>(fd) } {}
|
|
[[nodiscard]] ptr_t ref() {
|
|
std::lock_guard lg { _lock };
|
|
|
|
if(!_count) {
|
|
new(_object_buf.data()) element_type;
|
|
if(_construct(*reinterpret_cast<element_type *>(_object_buf.data()))) {
|
|
return ptr_t { nullptr };
|
|
}
|
|
}
|
|
|
|
++_count;
|
|
|
|
return ptr_t { this };
|
|
}
|
|
|
|
private:
|
|
construct_f _construct;
|
|
destruct_f _destruct;
|
|
|
|
std::array<std::uint8_t, sizeof(element_type)> _object_buf;
|
|
|
|
std::uint32_t _count;
|
|
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)
|
|
};
|
|
}
|
|
|
|
using signal_t = event_t<bool>;
|
|
|
|
class mail_raw_t;
|
|
using mail_t = std::shared_ptr<mail_raw_t>;
|
|
|
|
void cleanup(mail_raw_t *);
|
|
template<class T>
|
|
class post_t : public T {
|
|
public:
|
|
template<class... Args>
|
|
post_t(mail_t mail, Args &&...args) : T(std::forward<Args>(args)...), mail { std::move(mail) } {}
|
|
|
|
mail_t mail;
|
|
|
|
~post_t() {
|
|
cleanup(mail.get());
|
|
}
|
|
};
|
|
|
|
template<class T>
|
|
inline auto lock(const std::weak_ptr<void> &wp) {
|
|
return std::reinterpret_pointer_cast<typename T::element_type>(wp.lock());
|
|
}
|
|
|
|
class mail_raw_t : public std::enable_shared_from_this<mail_raw_t> {
|
|
public:
|
|
template<class T>
|
|
using event_t = std::shared_ptr<post_t<event_t<T>>>;
|
|
|
|
template<class T>
|
|
using queue_t = std::shared_ptr<post_t<queue_t<T>>>;
|
|
|
|
template<class T>
|
|
event_t<T> event(const std::string_view &id) {
|
|
std::lock_guard lg { mutex };
|
|
|
|
auto it = id_to_post.find(id);
|
|
if(it != std::end(id_to_post)) {
|
|
return lock<event_t<T>>(it->second);
|
|
}
|
|
|
|
auto post = std::make_shared<typename event_t<T>::element_type>(shared_from_this());
|
|
id_to_post.emplace(std::pair<std::string, std::weak_ptr<void>> { std::string { id }, post });
|
|
|
|
return post;
|
|
}
|
|
|
|
template<class T>
|
|
queue_t<T> queue(const std::string_view &id) {
|
|
std::lock_guard lg { mutex };
|
|
|
|
auto it = id_to_post.find(id);
|
|
if(it != std::end(id_to_post)) {
|
|
return lock<queue_t<T>>(it->second);
|
|
}
|
|
|
|
auto post = std::make_shared<typename queue_t<T>::element_type>(shared_from_this(), 32);
|
|
id_to_post.emplace(std::pair<std::string, std::weak_ptr<void>> { std::string { id }, post });
|
|
|
|
return post;
|
|
}
|
|
|
|
void cleanup() {
|
|
std::lock_guard lg { mutex };
|
|
|
|
for(auto it = std::begin(id_to_post); it != std::end(id_to_post); ++it) {
|
|
auto &weak = it->second;
|
|
|
|
if(weak.expired()) {
|
|
id_to_post.erase(it);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::mutex mutex;
|
|
|
|
std::map<std::string, std::weak_ptr<void>, std::less<>> id_to_post;
|
|
};
|
|
|
|
inline void cleanup(mail_raw_t *mail) {
|
|
mail->cleanup();
|
|
}
|
|
} // namespace safe
|
|
|
|
#endif //SUNSHINE_THREAD_SAFE_H
|