// examples/demo_task.hpp -*-C++-*- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef INCLUDED_EXAMPLES_DEMO_TASK #define INCLUDED_EXAMPLES_DEMO_TASK #include #include #include #include #include #include #include #if defined(__clang__) # pragma clang diagnostic ignored "-Wreturn-type" #endif // ---------------------------------------------------------------------------- namespace demo { namespace ex = ::beman::net29::detail::ex; template struct task_state_base { ::std::optional task_result; virtual auto complete_value() -> void = 0; virtual auto complete_error(::std::exception_ptr) -> void = 0; virtual auto complete_stopped() -> void = 0; template auto complete_set_value(Receiver& receiver) { ::beman::net29::detail::ex::set_value( ::std::move(receiver), ::std::move(*this->task_result) ); } }; template <> struct task_state_base { virtual auto complete_value() -> void = 0; virtual auto complete_error(::std::exception_ptr) -> void = 0; virtual auto complete_stopped() -> void = 0; template auto complete_set_value(Receiver& receiver) { ::beman::net29::detail::ex::set_value(::std::move(receiver)); } }; struct task_none {}; template struct task_type_or_none; template struct task_type_or_none { using type = T; }; template <> struct task_type_or_none<> { using type = task_none; }; template using task_type_or_none_t = typename task_type_or_none::type; template struct task_single_or_tuple { using type = ::std::tuple<::std::decay_t...>; }; template struct task_single_or_tuple { using type = ::std::decay_t; }; template using task_single_or_tuple_t = typename task_single_or_tuple::type; template struct task_promise_result { task_state_base* state{}; template auto return_value(T&& r) -> void { this->state->task_result.emplace(std::forward(r)); } }; template <> struct task_promise_result { task_state_base* state{}; auto return_void() -> void { } }; template struct task_completion { using type = ::beman::net29::detail::ex::set_value_t(T); }; template <> struct task_completion { using type = ::beman::net29::detail::ex::set_value_t(); }; template struct task { enum class stop_state { running, stopping, stopped }; template struct sender_awaiter { struct env { sender_awaiter* awaiter{}; auto query(ex::get_stop_token_t) const noexcept -> ex::inplace_stop_token; }; struct receiver { using receiver_concept = ex::receiver_t; sender_awaiter* awaiter{}; template auto set_value(Args&&... args) noexcept -> void { this->awaiter->result.emplace(::std::forward(args)...); this->awaiter->handle.resume(); } template auto set_error(Error&& error) noexcept -> void { if constexpr (::std::same_as<::std::decay_t, ::std::exception_ptr>) this->awaiter->error = error; else this->awaiter->error = ::std::make_exception_ptr(::std::forward(error)); this->awaiter->handle.resume(); } auto set_stopped() noexcept -> void { this->awaiter->stop(); } auto get_env() const noexcept -> env { return {this->awaiter}; } }; using value_type = ex::value_types_of_t< Sender, Promise, task_single_or_tuple_t, task_type_or_none_t>; using state_type = decltype(ex::connect(::std::declval(), std::declval())); ::std::coroutine_handle handle; ::std::exception_ptr error; ::std::optional result; state_type state; sender_awaiter(Sender sender) : state(ex::connect(::std::move(sender), receiver{this})) { } auto stop() -> void; auto get_token() const noexcept -> ex::inplace_stop_token; constexpr auto await_ready() const noexcept -> bool { return false; } auto await_suspend(::std::coroutine_handle handle) -> void { this->handle = handle; ex::start(this->state); } auto await_resume() { if (this->error) std::rethrow_exception(this->error); return ::std::move(*this->result); } }; struct promise_type; struct final_awaiter { promise_type* promise; constexpr auto await_ready() const noexcept -> bool { return false; } auto await_suspend(::std::coroutine_handle<>) noexcept -> void { this->promise->state->complete_value(); } constexpr auto await_resume() const noexcept -> void {} }; struct promise_type : task_promise_result { task::stop_state stop_state{task::stop_state::running}; ex::inplace_stop_source stop_source{}; auto initial_suspend() -> ::std::suspend_always { return {}; } auto final_suspend() noexcept -> final_awaiter { return {this}; } auto get_return_object() -> task { return {unique_handle(this)}; } auto unhandled_exception() -> void { this->state->complete_error(::std::current_exception()); } template auto await_transform(Sender&& sender) { return sender_awaiter> (::std::forward(sender)); } }; using deleter = decltype([](promise_type* p) { std::coroutine_handle::from_promise(*p).destroy(); }); using unique_handle= std::unique_ptr; template struct state : task_state_base<::std::decay_t> { using operation_state_concept = ex::operation_state_t; struct callback_t { state* object; auto operator()() const { auto state{this->object}; state->callback.reset(); state->handle->stop_state = task::stop_state::stopping; state->handle->stop_source.request_stop(); if (state->handle->stop_state == task::stop_state::stopped) this->object->handle->state->complete_stopped(); } }; using stop_token = decltype(ex::get_stop_token(ex::get_env(::std::declval()))); using stop_callback = ex::stop_callback_for_t; unique_handle handle; ::std::decay_t receiver; ::std::optional callback; template state(unique_handle handle, R&& receiver) : handle(::std::move(handle)) , receiver(::std::forward(receiver)) { } auto start() & noexcept -> void { this->handle->state = this; this->callback.emplace(ex::get_stop_token(ex::get_env(this->receiver)), callback_t{this}); std::coroutine_handle::from_promise(*this->handle).resume(); } auto complete_value() -> void override { this->complete_set_value(this->receiver); } auto complete_error(::std::exception_ptr error) -> void override { ::beman::net29::detail::ex::set_error( ::std::move(this->receiver), ::std::move(error) ); } auto complete_stopped() -> void override { ::beman::net29::detail::ex::set_stopped(::std::move(this->receiver)); } }; unique_handle handle; using sender_concept = ::beman::net29::detail::ex::sender_t; using completion_signatures = ::beman::net29::detail::ex::completion_signatures< ::beman::net29::detail::ex::set_error_t(::std::exception_ptr), ::beman::net29::detail::ex::set_stopped_t(), typename task_completion<::std::decay_t>::type >; template auto connect(Receiver&& receiver) { return state( ::std::move(this->handle), ::std::forward(receiver) ); } }; } // ---------------------------------------------------------------------------- template template auto demo::task::sender_awaiter::env::query(demo::ex::get_stop_token_t) const noexcept -> demo::ex::inplace_stop_token { return this->awaiter->handle.promise().stop_source.get_token(); } template template auto demo::task::sender_awaiter::stop() ->void { if (::std::exchange(this->handle.promise().stop_state, task::stop_state::stopped) == task::stop_state::running) { this->handle.promise().state->complete_stopped(); } } // ---------------------------------------------------------------------------- #endif