// // Created by loki on 6/10/19. // #ifndef SUNSHINE_THREAD_SAFE_H #define SUNSHINE_THREAD_SAFE_H #include #include #include #include #include #include "utility.h" namespace safe { template class event_t { public: using status_t = util::optional_t; template void raise(Args &&...args) { std::lock_guard lg { _lock }; if(!_continue) { return; } if constexpr(std::is_same_v, status_t>) { _status = std::make_optional(std::forward(args)...); } else { _status = status_t { std::forward(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; } while(!_status) { _cv.wait(ul); if(!_continue) { return util::false_v; } } auto val = std::move(_status); _status = util::false_v; return val; } // pop and view shoud not be used interchangebly template status_t pop(std::chrono::duration delay) { std::unique_lock ul { _lock }; if(!_continue) { return util::false_v; } while(!_status) { if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) { return util::false_v; } } auto val = std::move(_status); _status = util::false_v; 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; } while(!_status) { _cv.wait(ul); if(!_continue) { return util::false_v; } } return _status; } bool peek() { std::lock_guard lg { _lock }; 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; } [[nodiscard]] bool running() const { return _continue; } private: bool _continue { true }; status_t _status { util::false_v }; std::condition_variable _cv; std::mutex _lock; }; template class alarm_raw_t { public: using status_t = util::optional_t; alarm_raw_t() : _status { util::false_v } {} 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 auto wait_for(const std::chrono::duration &rel_time) { std::unique_lock ul(_lock); return _cv.wait_for(ul, rel_time, [this]() { return (bool)status(); }); } template auto wait_for(const std::chrono::duration &rel_time, Pred &&pred) { std::unique_lock ul(_lock); return _cv.wait_for(ul, rel_time, [this, &pred]() { return (bool)status() || pred(); }); } template auto wait_until(const std::chrono::duration &rel_time) { std::unique_lock ul(_lock); return _cv.wait_until(ul, rel_time, [this]() { return (bool)status(); }); } template auto wait_until(const std::chrono::duration &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 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 using alarm_t = std::shared_ptr>; template alarm_t make_alarm() { return std::make_shared>(); } template class queue_t { public: using status_t = util::optional_t; queue_t(std::uint32_t max_elements) : _max_elements { max_elements } {} template 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)...); _cv.notify_all(); } bool peek() { std::lock_guard lg { _lock }; return _continue && !_queue.empty(); } template status_t pop(std::chrono::duration delay) { std::unique_lock ul { _lock }; if(!_continue) { return util::false_v; } while(_queue.empty()) { if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) { return util::false_v; } } 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; } while(_queue.empty()) { _cv.wait(ul); if(!_continue) { return util::false_v; } } auto val = std::move(_queue.front()); _queue.erase(std::begin(_queue)); return val; } std::vector &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 _queue; }; template class shared_t { public: using element_type = T; using construct_f = std::function; using destruct_f = std::function; 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(owner->_object_buf.data()); } element_type *operator->() { return reinterpret_cast(owner->_object_buf.data()); } }; template shared_t(FC &&fc, FD &&fd) : _construct { std::forward(fc) }, _destruct { std::forward(fd) } {} [[nodiscard]] ptr_t ref() { std::lock_guard lg { _lock }; if(!_count) { new(_object_buf.data()) element_type; if(_construct(*reinterpret_cast(_object_buf.data()))) { return ptr_t { nullptr }; } } ++_count; return ptr_t { this }; } private: construct_f _construct; destruct_f _destruct; std::array _object_buf; std::uint32_t _count; std::mutex _lock; }; template auto make_shared(F_Construct &&fc, F_Destruct &&fd) { return shared_t { std::forward(fc), std::forward(fd) }; } using signal_t = event_t; } // namespace safe #endif //SUNSHINE_THREAD_SAFE_H