#pragma once | #pragma once | ||||
#define LIB_RESUMEF_VERSION 20600 // 2.6.0 | |||||
#define LIB_RESUMEF_VERSION 20700 // 2.7.0 | |||||
#if defined(RESUMEF_MODULE_EXPORT) | #if defined(RESUMEF_MODULE_EXPORT) | ||||
#define RESUMEF_NS export namespace resumef | #define RESUMEF_NS export namespace resumef |
} | } | ||||
inline namespace event_v1 | |||||
namespace event_v1 | |||||
{ | { | ||||
event_t::event_t(intptr_t initial_counter_) | event_t::event_t(intptr_t initial_counter_) | ||||
: _event(std::make_shared<detail::event_impl>(initial_counter_)) | : _event(std::make_shared<detail::event_impl>(initial_counter_)) |
}; | }; | ||||
} | } | ||||
inline namespace event_v1 | |||||
namespace event_v1 | |||||
{ | { | ||||
//提供一种在协程和非协程之间同步的手段。 | //提供一种在协程和非协程之间同步的手段。 |
} | } | ||||
} | } | ||||
namespace event_v2 | |||||
inline namespace event_v2 | |||||
{ | { | ||||
event_t::event_t(bool initially) | event_t::event_t(bool initially) | ||||
:_event(std::make_shared<detail::event_v2_impl>(initially)) | :_event(std::make_shared<detail::event_v2_impl>(initially)) |
struct event_v2_impl; | struct event_v2_impl; | ||||
} | } | ||||
namespace event_v2 | |||||
inline namespace event_v2 | |||||
{ | { | ||||
struct event_t | struct event_t | ||||
{ | { | ||||
void reset() const noexcept; | void reset() const noexcept; | ||||
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; | ||||
timeout_awaiter wait_for(const std::chrono::duration<_Rep, _Period>& dt) const noexcept; | timeout_awaiter wait_for(const std::chrono::duration<_Rep, _Period>& dt) const noexcept; | ||||
template<class _Clock, class _Duration> | template<class _Clock, class _Duration> | ||||
timeout_awaiter wait_until(const std::chrono::time_point<_Clock, _Duration>& tp) const noexcept; | timeout_awaiter wait_until(const std::chrono::time_point<_Clock, _Duration>& tp) const noexcept; | ||||
template<_IteratorT _Iter | |||||
COMMA_RESUMEF_ENABLE_IF(traits::is_iterator_v<_Iter> && std::is_same_v<event_t&, decltype(*std::declval<_Iter>())>) | |||||
> | |||||
static future_t<intptr_t> | |||||
wait_any(_Iter begin_, _Iter end_) | |||||
{ | |||||
when_any_pair idx = co_await when_any(begin_, end_); | |||||
co_return idx.first; | |||||
} | |||||
template<class _Cont | |||||
COMMA_RESUMEF_ENABLE_IF(traits::is_container_v<_Cont>) | |||||
> | |||||
static future_t<intptr_t> | |||||
wait_any(_Cont& cnt_) | |||||
{ | |||||
when_any_pair idx = co_await when_any(std::begin(cnt_), std::end(cnt_)); | |||||
co_return idx.first; | |||||
} | |||||
template<class _Rep, class _Period, _IteratorT _Iter | |||||
COMMA_RESUMEF_ENABLE_IF(traits::is_iterator_v<_Iter> && std::is_same_v<event_t&, decltype(*std::declval<_Iter>())>) | |||||
> | |||||
static future_t<intptr_t> | |||||
wait_any_for(const std::chrono::duration<_Rep, _Period>& dt, _Iter begin_, _Iter end_) | |||||
{ | |||||
auto tidx = co_await when_any(sleep_for(dt), when_any(begin_, end_)); | |||||
if (tidx.first == 0) co_return -1; | |||||
when_any_pair idx = any_cast<when_any_pair>(tidx.second); | |||||
co_return idx.first; | |||||
} | |||||
template<class _Rep, class _Period, _ContainerT _Cont | |||||
COMMA_RESUMEF_ENABLE_IF(traits::is_container_v<_Cont>) | |||||
> | |||||
static future_t<intptr_t> | |||||
wait_any_for(const std::chrono::duration<_Rep, _Period>& dt, _Cont& cont) | |||||
{ | |||||
return wait_any_for(dt, std::begin(cont), std::end(cont)); | |||||
} | |||||
template<_IteratorT _Iter | |||||
COMMA_RESUMEF_ENABLE_IF(traits::is_iterator_v<_Iter> && std::is_same_v<event_t&, decltype(*std::declval<_Iter>())>) | |||||
> | |||||
static future_t<bool> | |||||
wait_all(_Iter begin_, _Iter end_) | |||||
{ | |||||
auto vb = co_await when_all(begin_, end_); | |||||
co_return is_all_succeeded(vb); | |||||
} | |||||
template<class _Cont | |||||
COMMA_RESUMEF_ENABLE_IF(traits::is_container_v<_Cont>) | |||||
> | |||||
static future_t<bool> | |||||
wait_all(_Cont& cnt_) | |||||
{ | |||||
auto vb = co_await when_all(std::begin(cnt_), std::end(cnt_)); | |||||
co_return is_all_succeeded(vb); | |||||
} | |||||
template<class _Rep, class _Period, _IteratorT _Iter | |||||
COMMA_RESUMEF_ENABLE_IF(traits::is_iterator_v<_Iter> && std::is_same_v<event_t&, decltype(*std::declval<_Iter>())>) | |||||
> | |||||
static future_t<bool> | |||||
wait_all_for(const std::chrono::duration<_Rep, _Period>& dt, _Iter begin_, _Iter end_) | |||||
{ | |||||
auto tidx = co_await when_any(sleep_for(dt), when_all(begin_, end_)); | |||||
if (tidx.first == 0) co_return false; | |||||
std::vector<bool>& vb = any_cast<std::vector<bool>&>(tidx.second); | |||||
co_return is_all_succeeded(vb); | |||||
} | |||||
template<class _Rep, class _Period, _ContainerT _Cont | |||||
COMMA_RESUMEF_ENABLE_IF(traits::is_container_v<_Cont>) | |||||
> | |||||
static future_t<bool> | |||||
wait_all_for(const std::chrono::duration<_Rep, _Period>& dt, _Cont& cont) | |||||
{ | |||||
return wait_all_for(dt, std::begin(cont), std::end(cont)); | |||||
} | |||||
event_t(const event_t&) = default; | |||||
event_t(event_t&&) = default; | |||||
event_t& operator = (const event_t&) = default; | |||||
event_t& operator = (event_t&&) = default; | |||||
private: | private: | ||||
event_impl_ptr _event; | event_impl_ptr _event; | ||||
timeout_awaiter wait_until_(const clock_type::time_point& tp) const noexcept; | timeout_awaiter wait_until_(const clock_type::time_point& tp) const noexcept; | ||||
inline static bool is_all_succeeded(const std::vector<bool>& v) | |||||
{ | |||||
return std::none_of(std::begin(v), std::end(v), [](auto v) | |||||
{ | |||||
return v == false; | |||||
}); | |||||
} | |||||
}; | }; | ||||
} | } | ||||
template<class _Rep, class _Period> | |||||
auto wait_for(event_v2::event_t& e, const std::chrono::duration<_Rep, _Period>& dt) | |||||
{ | |||||
return e.wait_for(dt); | |||||
} | |||||
template<class _Clock, class _Duration> | |||||
auto wait_until(event_v2::event_t& e, const std::chrono::time_point<_Clock, _Duration>& tp) | |||||
{ | |||||
return e.wait_until(tp); | |||||
} | |||||
//when_all_for(dt, args...) -> when_all(wait_for(args, dt)...) | |||||
//就不再单独为每个支持超时的类提供when_all_for实现了。借助when_all和非成员的wait_for实现 | |||||
} | } |
}; | }; | ||||
} | } | ||||
namespace event_v2 | |||||
inline namespace event_v2 | |||||
{ | { | ||||
inline void event_t::signal_all() const noexcept | inline void event_t::signal_all() const noexcept | ||||
{ | { |
//is_generator_v<T> | //is_generator_v<T> | ||||
//判断是不是一个librf的generator_t类 | //判断是不是一个librf的generator_t类 | ||||
// | // | ||||
//is_state_pointer<T> | |||||
//is_state_pointer_v<T> | |||||
//判断是不是一个librf的state_t类的指针或智能指针 | |||||
// | |||||
//has_state<T> | //has_state<T> | ||||
//has_state_v<T> | //has_state_v<T> | ||||
//判断是否具有_state的成员变量 | //判断是否具有_state的成员变量 | ||||
// type:awaitor的类型 | // type:awaitor的类型 | ||||
// value_type:awaitor::await_resume()的返回值类型 | // value_type:awaitor::await_resume()的返回值类型 | ||||
// | // | ||||
//is_awaitable<T> | |||||
//is_awaitable_v<T> | |||||
//判断是否可以被co_await操作。可以是一个awaitor,也可以是重载了成员变量的T::operator co_await(),或者被重载了全局的operator co_awaitor(T) | |||||
// | |||||
//is_callable<T> | //is_callable<T> | ||||
//is_callable_v<T> | //is_callable_v<T> | ||||
//判断是不是一个可被调用的类型,如函数,仿函数,lambda等 | //判断是不是一个可被调用的类型,如函数,仿函数,lambda等 | ||||
// | // | ||||
//is_scheduler_task<T> | |||||
//is_scheduler_task_v<T> | |||||
//判断是不是可以被调度器调度的任务。调度器支持future和callable | |||||
//is_iterator<T> | |||||
//is_iterator_v<T> | |||||
//判断是不是一个支持向后迭代的迭代器 | |||||
// | |||||
//is_container<T> | |||||
//is_container_v<T> | |||||
//判断是不是一个封闭区间的容器,或者数组。 | |||||
template<class _Ty> | template<class _Ty> | ||||
struct is_coroutine_handle : std::false_type {}; | struct is_coroutine_handle : std::false_type {}; | ||||
template<class _Ty, class = std::void_t<>> | template<class _Ty, class = std::void_t<>> | ||||
struct awaitor_traits{}; | struct awaitor_traits{}; | ||||
template<class _Ty> | template<class _Ty> | ||||
struct awaitor_traits<_Ty, | struct awaitor_traits<_Ty, | ||||
std::void_t<decltype(get_awaitor(std::declval<_Ty>()))> | std::void_t<decltype(get_awaitor(std::declval<_Ty>()))> | ||||
using value_type = decltype(std::declval<type>().await_resume()); | using value_type = decltype(std::declval<type>().await_resume()); | ||||
}; | }; | ||||
template<class _Ty, class = std::void_t<>> | |||||
struct is_awaitable : std::false_type {}; | |||||
template<class _Ty> | |||||
struct is_awaitable<_Ty, | |||||
std::void_t<typename awaitor_traits<_Ty>::value_type> | |||||
> : std::true_type {}; | |||||
template<typename _Ty> | |||||
constexpr bool is_awaitable_v = is_awaitable<_Ty>::value; | |||||
template<typename _Ty, class = std::void_t<>> | template<typename _Ty, class = std::void_t<>> | ||||
struct is_callable : std::false_type{}; | struct is_callable : std::false_type{}; | ||||
template<typename _Ty> | template<typename _Ty> | ||||
struct is_iterator | struct is_iterator | ||||
<_Ty, | <_Ty, | ||||
std::void_t< | std::void_t< | ||||
decltype(++std::declval<_Ty>()) | |||||
decltype(std::declval<_Ty>() + 1) | |||||
, decltype(std::declval<_Ty>() != std::declval<_Ty>()) | , decltype(std::declval<_Ty>() != std::declval<_Ty>()) | ||||
, decltype(*std::declval<_Ty>()) | , decltype(*std::declval<_Ty>()) | ||||
> | > | ||||
> | > | ||||
: std::true_type{}; | : std::true_type{}; | ||||
template<class _Ty> | template<class _Ty> | ||||
constexpr bool is_iterator_v = is_iterator<remove_cvref_t<_Ty>>::value; | |||||
constexpr bool is_iterator_v = is_iterator<_Ty>::value; | |||||
template<class _Ty, class = std::void_t<>> | template<class _Ty, class = std::void_t<>> | ||||
struct is_container : std::false_type {}; | struct is_container : std::false_type {}; | ||||
struct is_container | struct is_container | ||||
<_Ty, | <_Ty, | ||||
std::void_t< | std::void_t< | ||||
decltype(std::begin(std::declval<_Ty>())) | |||||
, decltype(std::end(std::declval<_Ty>())) | |||||
decltype(std::declval<_Ty>().begin()) | |||||
, decltype(std::declval<_Ty>().end()) | |||||
> | > | ||||
> | > | ||||
: is_iterator<decltype(std::begin(std::declval<_Ty>()))> {}; | |||||
: std::true_type {}; | |||||
//: is_iterator<decltype(std::declval<_Ty>().begin())> {}; | |||||
template<class _Ty, size_t _Size> | |||||
struct is_container<_Ty[_Size]> : std::true_type {}; | |||||
template<class _Ty, size_t _Size> | |||||
struct is_container<_Ty(&)[_Size]> : std::true_type {}; | |||||
template<class _Ty, size_t _Size> | |||||
struct is_container<_Ty(&&)[_Size]> : std::true_type {}; | |||||
template<class _Ty> | template<class _Ty> | ||||
constexpr bool is_container_v = is_container<remove_cvref_t<_Ty>>::value; | constexpr bool is_container_v = is_container<remove_cvref_t<_Ty>>::value; | ||||
} | } |
RESUMEF_NS | RESUMEF_NS | ||||
{ | { | ||||
using when_any_pair = std::pair<intptr_t, any_t>; | |||||
using when_any_pair_ptr = std::shared_ptr<when_any_pair>; | |||||
namespace detail | namespace detail | ||||
{ | { | ||||
struct state_when_t : public state_base_t | struct state_when_t : public state_base_t | ||||
template<class _Ty> | template<class _Ty> | ||||
struct is_when_task : std::bool_constant<traits::is_awaitor_v<_Ty> || traits::is_callable_v<_Ty>> {}; | |||||
struct is_when_task : std::bool_constant<traits::is_awaitable_v<_Ty> || traits::is_callable_v<_Ty>> {}; | |||||
template<class _Ty> | template<class _Ty> | ||||
constexpr bool is_when_task_v = is_when_task<_Ty>::value; | constexpr bool is_when_task_v = is_when_task<_Ty>::value; | ||||
} | } | ||||
template<_WhenTaskT _Awaitable, class _Ty> | template<_WhenTaskT _Awaitable, class _Ty> | ||||
future_t<> when_all_connector(state_when_t* state, _Awaitable task, _Ty& value) | |||||
future_t<> when_all_connector_1(state_when_t* state, _Awaitable task, _Ty& value) | |||||
{ | { | ||||
decltype(auto) awaitor = when_real_awaitor(task); | decltype(auto) awaitor = when_real_awaitor(task); | ||||
if constexpr (std::is_same_v<_Ty, ignore_type>) | |||||
co_await awaitor; | |||||
else | |||||
value = co_await awaitor; | |||||
state->on_notify_one(); | |||||
}; | |||||
template<class _FuckBoolean> | |||||
using _FuckBoolVectorReference = typename std::vector<_FuckBoolean>::reference; | |||||
template<_WhenTaskT _Awaitable, class _Ty> | |||||
future_t<> when_all_connector_2(state_when_t* state,_Awaitable task, _FuckBoolVectorReference<_Ty> value) | |||||
{ | |||||
auto&& awaitor = when_real_awaitor(task); | |||||
if constexpr(std::is_same_v<_Ty, ignore_type>) | if constexpr(std::is_same_v<_Ty, ignore_type>) | ||||
co_await awaitor; | co_await awaitor; | ||||
else | else | ||||
template<class _Tup, size_t _Idx, _WhenTaskT _Awaitable, _WhenTaskT... _Rest> | template<class _Tup, size_t _Idx, _WhenTaskT _Awaitable, _WhenTaskT... _Rest> | ||||
inline void when_all_one__(scheduler_t& sch, state_when_t* state, _Tup& values, _Awaitable&& awaitable, _Rest&&... rest) | inline void when_all_one__(scheduler_t& sch, state_when_t* state, _Tup& values, _Awaitable&& awaitable, _Rest&&... rest) | ||||
{ | { | ||||
sch + when_all_connector(state, std::forward<_Awaitable>(awaitable), std::get<_Idx>(values)); | |||||
sch + when_all_connector_1(state, std::forward<_Awaitable>(awaitable), std::get<_Idx>(values)); | |||||
when_all_one__<_Tup, _Idx + 1, _Rest...>(sch, state, values, std::forward<_Rest>(rest)...); | when_all_one__<_Tup, _Idx + 1, _Rest...>(sch, state, values, std::forward<_Rest>(rest)...); | ||||
} | } | ||||
template<class _Val, _WhenIterT _Iter> | template<class _Val, _WhenIterT _Iter> | ||||
inline void when_all_range__(scheduler_t& sch, state_when_t* state, std::vector<_Val> & values, _Iter begin, _Iter end) | inline void when_all_range__(scheduler_t& sch, state_when_t* state, std::vector<_Val> & values, _Iter begin, _Iter end) | ||||
{ | { | ||||
using _Awaitable = decltype(*begin); | |||||
intptr_t _Idx = 0; | intptr_t _Idx = 0; | ||||
for (; begin != end; ++begin, ++_Idx) | for (; begin != end; ++begin, ++_Idx) | ||||
{ | { | ||||
sch + when_all_connector(state, std::move(*begin), values[_Idx]); | |||||
sch + when_all_connector_2<_Awaitable, _Val>(state, *begin, values[_Idx]); | |||||
} | } | ||||
} | } | ||||
//----------------------------------------------------------------------------------------------------------------------------------------- | //----------------------------------------------------------------------------------------------------------------------------------------- | ||||
using when_any_pair = std::pair<intptr_t, any_t>; | |||||
using when_any_pair_ptr = std::shared_ptr<when_any_pair>; | |||||
template<_WhenTaskT _Awaitable> | template<_WhenTaskT _Awaitable> | ||||
future_t<> when_any_connector(counted_ptr<state_when_t> state, _Awaitable task, when_any_pair_ptr value, intptr_t idx) | future_t<> when_any_connector(counted_ptr<state_when_t> state, _Awaitable task, when_any_pair_ptr value, intptr_t idx) | ||||
{ | { | ||||
COMMA_RESUMEF_ENABLE_IF(detail::is_when_task_iter_v<_Iter>) | COMMA_RESUMEF_ENABLE_IF(detail::is_when_task_iter_v<_Iter>) | ||||
> | > | ||||
auto when_all(_Iter begin, _Iter end) | auto when_all(_Iter begin, _Iter end) | ||||
-> future_t<std::vector<detail::awaitor_result_t<decltype(*std::declval<_Iter>())>>> | |||||
-> future_t<std::vector<detail::awaitor_result_t<decltype(*begin)>>> | |||||
{ | { | ||||
co_return co_await when_all(*current_scheduler(), begin, end); | co_return co_await when_all(*current_scheduler(), begin, end); | ||||
} | } | ||||
template<_ContainerT _Cont | template<_ContainerT _Cont | ||||
COMMA_RESUMEF_ENABLE_IF(traits::is_container_v<_Cont>) | COMMA_RESUMEF_ENABLE_IF(traits::is_container_v<_Cont>) | ||||
> | > | ||||
decltype(auto) when_all(_Cont& cont) | |||||
auto when_all(_Cont&& cont) | |||||
-> future_t<std::vector<detail::awaitor_result_t<decltype(*std::begin(cont))>>> | |||||
{ | { | ||||
return when_all(std::begin(cont), std::end(cont)); | return when_all(std::begin(cont), std::end(cont)); | ||||
} | } | ||||
COMMA_RESUMEF_ENABLE_IF(std::conjunction_v<detail::is_when_task<_Awaitable>...>) | COMMA_RESUMEF_ENABLE_IF(std::conjunction_v<detail::is_when_task<_Awaitable>...>) | ||||
> | > | ||||
auto when_any(scheduler_t& sch, _Awaitable&&... args) | auto when_any(scheduler_t& sch, _Awaitable&&... args) | ||||
-> detail::when_future_t<detail::when_any_pair> | |||||
-> detail::when_future_t<when_any_pair> | |||||
{ | { | ||||
detail::when_future_t<detail::when_any_pair> awaitor{ sizeof...(_Awaitable) > 0 ? 1 : 0 }; | |||||
detail::when_future_t<when_any_pair> awaitor{ sizeof...(_Awaitable) > 0 ? 1 : 0 }; | |||||
awaitor._values->first = -1; | awaitor._values->first = -1; | ||||
detail::when_any_one__(sch, awaitor._state.get(), awaitor._values, 0, std::forward<_Awaitable>(args)...); | detail::when_any_one__(sch, awaitor._state.get(), awaitor._values, 0, std::forward<_Awaitable>(args)...); | ||||
COMMA_RESUMEF_ENABLE_IF(detail::is_when_task_iter_v<_Iter>) | COMMA_RESUMEF_ENABLE_IF(detail::is_when_task_iter_v<_Iter>) | ||||
> | > | ||||
auto when_any(scheduler_t& sch, _Iter begin, _Iter end) | auto when_any(scheduler_t& sch, _Iter begin, _Iter end) | ||||
-> detail::when_future_t<detail::when_any_pair> | |||||
-> detail::when_future_t<when_any_pair> | |||||
{ | { | ||||
detail::when_future_t<detail::when_any_pair> awaitor{ begin == end ? 0 : 1 }; | |||||
detail::when_future_t<when_any_pair> awaitor{ begin == end ? 0 : 1 }; | |||||
awaitor._values->first = -1; | awaitor._values->first = -1; | ||||
detail::when_any_range__<_Iter>(sch, awaitor._state.get(), awaitor._values, begin, end); | detail::when_any_range__<_Iter>(sch, awaitor._state.get(), awaitor._values, begin, end); | ||||
COMMA_RESUMEF_ENABLE_IF(std::conjunction_v<detail::is_when_task<_Awaitable>...>) | COMMA_RESUMEF_ENABLE_IF(std::conjunction_v<detail::is_when_task<_Awaitable>...>) | ||||
> | > | ||||
auto when_any(_Awaitable&&... awaitor) | auto when_any(_Awaitable&&... awaitor) | ||||
-> future_t<detail::when_any_pair> | |||||
-> future_t<when_any_pair> | |||||
{ | { | ||||
co_return co_await when_any(*current_scheduler(), std::forward<_Awaitable>(awaitor)...); | co_return co_await when_any(*current_scheduler(), std::forward<_Awaitable>(awaitor)...); | ||||
} | } | ||||
COMMA_RESUMEF_ENABLE_IF(detail::is_when_task_iter_v<_Iter>) | COMMA_RESUMEF_ENABLE_IF(detail::is_when_task_iter_v<_Iter>) | ||||
> | > | ||||
auto when_any(_Iter begin, _Iter end) | auto when_any(_Iter begin, _Iter end) | ||||
-> future_t<detail::when_any_pair> | |||||
-> future_t<when_any_pair> | |||||
{ | { | ||||
co_return co_await when_any(*current_scheduler(), begin, end); | co_return co_await when_any(*current_scheduler(), begin, end); | ||||
} | } | ||||
template<_ContainerT _Cont | template<_ContainerT _Cont | ||||
COMMA_RESUMEF_ENABLE_IF(traits::is_container_v<_Cont>) | COMMA_RESUMEF_ENABLE_IF(traits::is_container_v<_Cont>) | ||||
> | > | ||||
decltype(auto) when_any(_Cont& cont) | |||||
auto when_any(_Cont&& cont) | |||||
{ | { | ||||
return when_any(std::begin(cont), std::end(cont)); | return when_any(std::begin(cont), std::end(cont)); | ||||
} | } |
intptr_t counter = 0; | intptr_t counter = 0; | ||||
for (;;) | for (;;) | ||||
{ | { | ||||
if (co_await event_t::wait_all_until(system_clock::now() + 500ms, evts)) | |||||
if (co_await event_t::wait_all_for(500ms, evts)) | |||||
{ | { | ||||
std::cout << counter << std::endl; | std::cout << counter << std::endl; | ||||
std::cout << "all event signal!" << std::endl; | std::cout << "all event signal!" << std::endl; |