{ | { | ||||
return new T{std::forward<Args>(args)...}; | return new T{std::forward<Args>(args)...}; | ||||
} | } | ||||
template <typename T, typename U> | |||||
inline bool operator == (const counted_ptr<T>& _Left, const counted_ptr<U>& _Right) | |||||
{ | |||||
return _Left.get() == _Right.get(); | |||||
} | |||||
template <typename T> | |||||
inline bool operator == (const counted_ptr<T>& _Left, std::nullptr_t) | |||||
{ | |||||
return _Left.get() == nullptr; | |||||
} | |||||
template <typename T> | |||||
inline bool operator == (std::nullptr_t, const counted_ptr<T>& _Left) | |||||
{ | |||||
return _Left.get() == nullptr; | |||||
} | |||||
template <typename T> | |||||
inline bool operator != (const counted_ptr<T>& _Left, std::nullptr_t) | |||||
{ | |||||
return _Left.get() != nullptr; | |||||
} | |||||
template <typename T> | |||||
inline bool operator != (std::nullptr_t, const counted_ptr<T>& _Left) | |||||
{ | |||||
return _Left.get() != nullptr; | |||||
} | |||||
} | } | ||||
return (bool)_coro; | return (bool)_coro; | ||||
} | } | ||||
void state_event_t::on_notify() | |||||
void state_event_t::on_cancel() noexcept | |||||
{ | { | ||||
*_value = true; | |||||
scoped_lock<lock_type> lock_(_mtx); | |||||
assert(this->_scheduler != nullptr); | |||||
if (this->_coro) | |||||
this->_scheduler->add_generator(this); | |||||
if (_value != nullptr) | |||||
{ | |||||
*_value = false; | |||||
_value = nullptr; | |||||
} | |||||
this->_coro = nullptr; | |||||
} | } | ||||
void state_event_t::on_cancel() noexcept | |||||
bool state_event_t::on_notify() | |||||
{ | { | ||||
*_value = false; | |||||
this->_coro = nullptr; | |||||
scoped_lock<lock_type> lock_(_mtx); | |||||
if (_value != nullptr) | |||||
{ | |||||
*_value = true; | |||||
_value = nullptr; | |||||
assert(this->_scheduler != nullptr); | |||||
if (this->_coro) | |||||
this->_scheduler->add_generator(this); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
bool state_event_t::on_timeout() | |||||
{ | |||||
scoped_lock<lock_type> lock_(_mtx); | |||||
if (_value != nullptr) | |||||
{ | |||||
*_value = false; | |||||
_value = nullptr; | |||||
assert(this->_scheduler != nullptr); | |||||
if (this->_coro) | |||||
this->_scheduler->add_generator(this); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
event_v2_impl::event_v2_impl(bool initially) noexcept | |||||
: _counter(initially ? 1 : 0) | |||||
{ | |||||
} | |||||
event_v2_impl::~event_v2_impl() | |||||
{ | |||||
for (; _wait_awakes.try_pop() != nullptr;); | |||||
} | } | ||||
void event_v2_impl::signal_all() noexcept | void event_v2_impl::signal_all() noexcept | ||||
_counter.store(0, std::memory_order_release); | _counter.store(0, std::memory_order_release); | ||||
state_event_t* state; | |||||
counted_ptr<state_event_t> state; | |||||
for (; (state = _wait_awakes.try_pop()) != nullptr;) | for (; (state = _wait_awakes.try_pop()) != nullptr;) | ||||
{ | { | ||||
state->on_notify(); | |||||
(void)state->on_notify(); | |||||
} | } | ||||
} | } | ||||
{ | { | ||||
scoped_lock<lock_type> lock_(_lock); | scoped_lock<lock_type> lock_(_lock); | ||||
state_event_t* state = _wait_awakes.try_pop(); | |||||
if (state == nullptr) | |||||
_counter.fetch_add(1, std::memory_order_acq_rel); | |||||
else | |||||
state->on_notify(); | |||||
counted_ptr<state_event_t> state; | |||||
for (; (state = _wait_awakes.try_pop()) != nullptr;) | |||||
{ | |||||
if (state->on_notify()) | |||||
return; | |||||
} | |||||
_counter.fetch_add(1, std::memory_order_acq_rel); | |||||
} | } | ||||
} | } | ||||
event_t::~event_t() | event_t::~event_t() | ||||
{ | { | ||||
} | } | ||||
event_t::timeout_awaiter event_t::wait_until_(const clock_type::time_point& tp) const noexcept | |||||
{ | |||||
return { _event, tp }; | |||||
} | |||||
} | } | ||||
} | } |
struct event_t | struct event_t | ||||
{ | { | ||||
using event_impl_ptr = std::shared_ptr<detail::event_v2_impl>; | using event_impl_ptr = std::shared_ptr<detail::event_v2_impl>; | ||||
using clock_type = std::chrono::system_clock; | |||||
event_t(bool initially = false); | event_t(bool initially = false); | ||||
~event_t(); | ~event_t(); | ||||
struct [[nodiscard]] awaiter; | struct [[nodiscard]] awaiter; | ||||
awaiter operator co_await() const noexcept; | awaiter operator co_await() const noexcept; | ||||
awaiter wait() const noexcept; | awaiter wait() const noexcept; | ||||
struct [[nodiscard]] timeout_awaiter; | |||||
template<class _Rep, class _Period> | |||||
timeout_awaiter wait_for(const std::chrono::duration<_Rep, _Period>& dt) const noexcept; | |||||
template<class _Clock, class _Duration> | |||||
timeout_awaiter wait_until(const std::chrono::time_point<_Clock, _Duration>& tp) const noexcept; | |||||
private: | private: | ||||
event_impl_ptr _event; | event_impl_ptr _event; | ||||
timeout_awaiter wait_until_(const clock_type::time_point& tp) const noexcept; | |||||
}; | }; | ||||
} | } | ||||
} | } |
//所以,还得用锁结构来实现(等待实现,今日不空)。 | //所以,还得用锁结构来实现(等待实现,今日不空)。 | ||||
struct event_v2_impl : public std::enable_shared_from_this<event_v2_impl> | struct event_v2_impl : public std::enable_shared_from_this<event_v2_impl> | ||||
{ | { | ||||
event_v2_impl(bool initially) noexcept | |||||
: _counter(initially ? 1 : 0) | |||||
{} | |||||
event_v2_impl(bool initially) noexcept; | |||||
~event_v2_impl(); | |||||
bool is_signaled() const noexcept | bool is_signaled() const noexcept | ||||
{ | { | ||||
static constexpr bool USE_SPINLOCK = true; | static constexpr bool USE_SPINLOCK = true; | ||||
using lock_type = std::conditional_t<USE_SPINLOCK, spinlock, std::deque<std::recursive_mutex>>; | using lock_type = std::conditional_t<USE_SPINLOCK, spinlock, std::deque<std::recursive_mutex>>; | ||||
using wait_queue_type = intrusive_link_queue<state_event_t>; | |||||
using wait_queue_type = intrusive_link_queue<state_event_t, counted_ptr<state_event_t>>; | |||||
friend struct state_event_t; | friend struct state_event_t; | ||||
virtual void resume() override; | virtual void resume() override; | ||||
virtual bool has_handler() const noexcept override; | virtual bool has_handler() const noexcept override; | ||||
void on_notify(); | |||||
void on_cancel() noexcept; | void on_cancel() noexcept; | ||||
bool on_notify(); | |||||
bool on_timeout(); | |||||
//将自己加入到通知链表里 | //将自己加入到通知链表里 | ||||
template<class _PromiseT, typename = std::enable_if_t<is_promise_v<_PromiseT>>> | template<class _PromiseT, typename = std::enable_if_t<is_promise_v<_PromiseT>>> | ||||
void on_await_suspend(coroutine_handle<_PromiseT> handler) noexcept | |||||
scheduler_t* on_await_suspend(coroutine_handle<_PromiseT> handler) noexcept | |||||
{ | { | ||||
_PromiseT& promise = handler.promise(); | _PromiseT& promise = handler.promise(); | ||||
auto* parent_state = promise.get_state(); | auto* parent_state = promise.get_state(); | ||||
this->_scheduler = sch; | this->_scheduler = sch; | ||||
this->_coro = handler; | this->_coro = handler; | ||||
return sch; | |||||
} | } | ||||
public: | public: | ||||
typedef spinlock lock_type; | |||||
//为浸入式单向链表提供的next指针 | //为浸入式单向链表提供的next指针 | ||||
state_event_t* _next = nullptr; | |||||
counted_ptr<state_event_t> _next = nullptr; | |||||
private: | |||||
//std::atomic<bool*> _value; | |||||
bool* _value; | bool* _value; | ||||
mutable lock_type _mtx; | |||||
}; | }; | ||||
} | } | ||||
namespace event_v2 | namespace event_v2 | ||||
{ | { | ||||
inline void event_t::signal_all() const noexcept | |||||
{ | |||||
_event->signal_all(); | |||||
} | |||||
inline void event_t::signal() const noexcept | |||||
{ | |||||
_event->signal(); | |||||
} | |||||
inline void event_t::reset() const noexcept | |||||
{ | |||||
_event->reset(); | |||||
} | |||||
struct [[nodiscard]] event_t::awaiter | struct [[nodiscard]] event_t::awaiter | ||||
{ | { | ||||
awaiter(event_impl_ptr evt) noexcept | awaiter(event_impl_ptr evt) noexcept | ||||
{ | { | ||||
return (_value = _event->try_wait_one()); | return (_value = _event->try_wait_one()); | ||||
} | } | ||||
template<class _PromiseT, typename = std::enable_if_t<is_promise_v<_PromiseT>>> | template<class _PromiseT, typename = std::enable_if_t<is_promise_v<_PromiseT>>> | ||||
bool await_suspend(coroutine_handle<_PromiseT> handler) | bool await_suspend(coroutine_handle<_PromiseT> handler) | ||||
{ | { | ||||
return false; | return false; | ||||
_state = new detail::state_event_t(_value); | _state = new detail::state_event_t(_value); | ||||
_state->on_await_suspend(handler); | |||||
(void)_state->on_await_suspend(handler); | |||||
_event->add_wait_list(_state.get()); | _event->add_wait_list(_state.get()); | ||||
return true; | return true; | ||||
} | } | ||||
bool await_resume() noexcept | bool await_resume() noexcept | ||||
{ | { | ||||
return _value; | return _value; | ||||
bool _value = false; | bool _value = false; | ||||
}; | }; | ||||
inline void event_t::signal_all() const noexcept | |||||
inline event_t::awaiter event_t::operator co_await() const noexcept | |||||
{ | { | ||||
_event->signal_all(); | |||||
return { _event }; | |||||
} | } | ||||
inline void event_t::signal() const noexcept | |||||
inline event_t::awaiter event_t::wait() const noexcept | |||||
{ | { | ||||
_event->signal(); | |||||
return { _event }; | |||||
} | } | ||||
inline void event_t::reset() const noexcept | |||||
struct [[nodiscard]] event_t::timeout_awaiter | |||||
{ | { | ||||
_event->reset(); | |||||
} | |||||
timeout_awaiter(event_impl_ptr evt, clock_type::time_point tp) noexcept | |||||
: _event(std::move(evt)) | |||||
, _tp(tp) | |||||
{ | |||||
} | |||||
inline event_t::awaiter event_t::operator co_await() const noexcept | |||||
bool await_ready() noexcept | |||||
{ | |||||
return (_value = _event->try_wait_one()); | |||||
} | |||||
template<class _PromiseT, typename = std::enable_if_t<is_promise_v<_PromiseT>>> | |||||
bool await_suspend(coroutine_handle<_PromiseT> handler) | |||||
{ | |||||
scoped_lock<detail::event_v2_impl::lock_type> lock_(_event->_lock); | |||||
if (_event->try_wait_one()) | |||||
return false; | |||||
_state = new detail::state_event_t(_value); | |||||
scheduler_t* sch = _state->on_await_suspend(handler); | |||||
_event->add_wait_list(_state.get()); | |||||
(void)sch->timer()->add(_tp, [_state=_state](bool canceld) | |||||
{ | |||||
if (!canceld) | |||||
_state->on_timeout(); | |||||
}); | |||||
return true; | |||||
} | |||||
bool await_resume() noexcept | |||||
{ | |||||
return _value; | |||||
} | |||||
private: | |||||
std::shared_ptr<detail::event_v2_impl> _event; | |||||
counted_ptr<detail::state_event_t> _state; | |||||
clock_type::time_point _tp; | |||||
bool _value = false; | |||||
}; | |||||
template<class _Rep, class _Period> | |||||
inline event_t::timeout_awaiter event_t::wait_for(const std::chrono::duration<_Rep, _Period>& dt) const noexcept | |||||
{ | { | ||||
return { _event }; | |||||
return wait_until_(clock_type::now() + std::chrono::duration_cast<clock_type::duration>(dt)); | |||||
} | } | ||||
inline event_t::awaiter event_t::wait() const noexcept | |||||
template<class _Clock, class _Duration> | |||||
inline event_t::timeout_awaiter event_t::wait_until(const std::chrono::time_point<_Clock, _Duration>& tp) const noexcept | |||||
{ | { | ||||
return { _event }; | |||||
return wait_until_(std::chrono::time_point_cast<clock_type::duration>(tp)); | |||||
} | } | ||||
} | } | ||||
} | } |
RESUMEF_NS | RESUMEF_NS | ||||
{ | { | ||||
template<class _Node, class _Sty = uint32_t> | |||||
template<class _Node, class _Nodeptr = _Node*, class _Sty = uint32_t> | |||||
struct intrusive_link_queue | struct intrusive_link_queue | ||||
{ | { | ||||
using node_type = _Node; | using node_type = _Node; | ||||
using node_ptr_type = _Nodeptr; | |||||
using size_type = _Sty; | using size_type = _Sty; | ||||
public: | public: | ||||
intrusive_link_queue(); | intrusive_link_queue(); | ||||
auto size() const noexcept->size_type; | auto size() const noexcept->size_type; | ||||
bool empty() const noexcept; | bool empty() const noexcept; | ||||
void push_back(node_type* node) noexcept; | |||||
auto try_pop() noexcept->node_type*; | |||||
void push_back(node_ptr_type node) noexcept; | |||||
auto try_pop() noexcept->node_ptr_type; | |||||
private: | private: | ||||
node_type* _head; | |||||
node_type* _tail; | |||||
node_ptr_type _head; | |||||
node_ptr_type _tail; | |||||
#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE | #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE | ||||
std::atomic<size_type> m_count; | std::atomic<size_type> m_count; | ||||
#endif | #endif | ||||
}; | }; | ||||
template<class _Node, class _Sty> | |||||
intrusive_link_queue<_Node, _Sty>::intrusive_link_queue() | |||||
template<class _Node, class _Nodeptr, class _Sty> | |||||
intrusive_link_queue<_Node, _Nodeptr, _Sty>::intrusive_link_queue() | |||||
: _head(nullptr) | : _head(nullptr) | ||||
, _tail(nullptr) | , _tail(nullptr) | ||||
#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE | #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE | ||||
{ | { | ||||
} | } | ||||
template<class _Node, class _Sty> | |||||
auto intrusive_link_queue<_Node, _Sty>::size() const noexcept->size_type | |||||
template<class _Node, class _Nodeptr, class _Sty> | |||||
auto intrusive_link_queue<_Node, _Nodeptr, _Sty>::size() const noexcept->size_type | |||||
{ | { | ||||
#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE | #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE | ||||
return m_count.load(std::memory_order_acquire); | return m_count.load(std::memory_order_acquire); | ||||
#endif // _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE | #endif // _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE | ||||
} | } | ||||
template<class _Node, class _Sty> | |||||
bool intrusive_link_queue<_Node, _Sty>::empty() const noexcept | |||||
template<class _Node, class _Nodeptr, class _Sty> | |||||
bool intrusive_link_queue<_Node, _Nodeptr, _Sty>::empty() const noexcept | |||||
{ | { | ||||
return _head == nullptr; | return _head == nullptr; | ||||
} | } | ||||
template<class _Node, class _Sty> | |||||
void intrusive_link_queue<_Node, _Sty>::push_back(node_type* node) noexcept | |||||
template<class _Node, class _Nodeptr, class _Sty> | |||||
void intrusive_link_queue<_Node, _Nodeptr, _Sty>::push_back(node_ptr_type node) noexcept | |||||
{ | { | ||||
assert(node != nullptr); | assert(node != nullptr); | ||||
#endif | #endif | ||||
} | } | ||||
template<class _Node, class _Sty> | |||||
auto intrusive_link_queue<_Node, _Sty>::try_pop() noexcept->node_type* | |||||
template<class _Node, class _Nodeptr, class _Sty> | |||||
auto intrusive_link_queue<_Node, _Nodeptr, _Sty>::try_pop() noexcept->node_ptr_type | |||||
{ | { | ||||
if (_head == nullptr) | if (_head == nullptr) | ||||
return nullptr; | return nullptr; | ||||
node_type* node = _head; | |||||
node_ptr_type node = _head; | |||||
_head = node->_next; | _head = node->_next; | ||||
node->_next = nullptr; | node->_next = nullptr; | ||||
struct spinlock | struct spinlock | ||||
{ | { | ||||
static const size_t MAX_ACTIVE_SPIN = 1000; | |||||
static const size_t MAX_YIELD_SPIN = 4000; | |||||
static const size_t MAX_ACTIVE_SPIN = 4000; | |||||
static const size_t MAX_YIELD_SPIN = 8000; | |||||
static const int FREE_VALUE = 0; | static const int FREE_VALUE = 0; | ||||
static const int LOCKED_VALUE = 1; | static const int LOCKED_VALUE = 1; | ||||
else | else | ||||
{ | { | ||||
std::this_thread::sleep_for(dt); | std::this_thread::sleep_for(dt); | ||||
dt *= 2; | |||||
dt = (std::min)(std::chrono::milliseconds{ 128 }, dt * 2); | |||||
} | } | ||||
} | } | ||||
#include "librf.h" | #include "librf.h" | ||||
using namespace resumef; | using namespace resumef; | ||||
using namespace std::chrono; | |||||
//非协程的逻辑线程,或异步代码,可以通过event_t通知到协程,并且不会阻塞协程所在的线程。 | //非协程的逻辑线程,或异步代码,可以通过event_t通知到协程,并且不会阻塞协程所在的线程。 | ||||
static std::thread async_set_event_all(const event_v2::event_t & e, std::chrono::milliseconds dt) | static std::thread async_set_event_all(const event_v2::event_t & e, std::chrono::milliseconds dt) | ||||
}); | }); | ||||
} | } | ||||
static future_t<> resumable_wait_event(event_v2::event_t e, int idx) | static future_t<> resumable_wait_event(event_v2::event_t e, int idx) | ||||
{ | { | ||||
if (co_await e) | if (co_await e) | ||||
std::cout << "[" << idx << "]event signal!" << std::endl; | std::cout << "[" << idx << "]event signal!" << std::endl; | ||||
else | else | ||||
std::cout << "time out!" << std::endl; | |||||
std::cout << "[" << idx << "]time out!" << std::endl; | |||||
} | } | ||||
static void test_notify_all() | |||||
static future_t<> resumable_wait_timeout(event_v2::event_t e, milliseconds dt, int idx) | |||||
{ | { | ||||
using namespace std::chrono; | |||||
if (co_await e.wait_for(dt)) | |||||
std::cout << "[" << idx << "]event signal!" << std::endl; | |||||
else | |||||
std::cout << "[" << idx << "]time out!" << std::endl; | |||||
} | |||||
{ | |||||
event_v2::event_t evt; | |||||
go resumable_wait_event(evt, 0); | |||||
go resumable_wait_event(evt, 1); | |||||
go resumable_wait_event(evt, 2); | |||||
static void test_notify_all() | |||||
{ | |||||
event_v2::event_t evt; | |||||
go resumable_wait_event(evt, 0); | |||||
go resumable_wait_event(evt, 1); | |||||
go resumable_wait_event(evt, 2); | |||||
auto tt = async_set_event_all(evt, 100ms); | |||||
this_scheduler()->run_until_notask(); | |||||
auto tt = async_set_event_all(evt, 100ms); | |||||
this_scheduler()->run_until_notask(); | |||||
tt.join(); | |||||
} | |||||
tt.join(); | |||||
} | } | ||||
//目前还没法测试在多线程调度下,是否线程安全 | //目前还没法测试在多线程调度下,是否线程安全 | ||||
} | } | ||||
} | } | ||||
static void test_wait_all_timeout() | |||||
{ | |||||
using namespace std::chrono; | |||||
srand((int)time(nullptr)); | |||||
event_v2::event_t evts[10]; | |||||
std::vector<std::thread> vtt; | |||||
for(size_t i = 0; i < _countof(evts); ++i) | |||||
{ | |||||
go resumable_wait_timeout(evts[i], 100ms, (int)i); | |||||
vtt.emplace_back(async_set_event_one(evts[i], 1ms * (50 + i * 10))); | |||||
} | |||||
this_scheduler()->run_until_notask(); | |||||
for (auto& tt : vtt) | |||||
tt.join(); | |||||
} | |||||
void resumable_main_event_v2() | void resumable_main_event_v2() | ||||
{ | { | ||||
test_notify_all(); | test_notify_all(); | ||||
test_notify_one(); | test_notify_one(); | ||||
std::cout << std::endl; | std::cout << std::endl; | ||||
test_wait_all_timeout(); | |||||
std::cout << std::endl; | |||||
} | } |