diff --git a/include/asioexec/completion_token.hpp b/include/asioexec/completion_token.hpp index 36bbba072..cfb8cbda7 100644 --- a/include/asioexec/completion_token.hpp +++ b/include/asioexec/completion_token.hpp @@ -36,9 +36,24 @@ namespace asioexec "exec::asio::completion_token_t " "instead.")]] = exec::asio::completion_token_t; + using thread_unsafe_completion_token_t + [[deprecated("asioexec::thread_unsafe_completion_token_t " + "is deprecated. Please use " + "exec::asio::thread_unsafe_completion_token_" + "t instead.")]] = exec::asio::thread_unsafe_completion_token_t; + inline constexpr auto const & completion_token [[deprecated("asioexec::completion_token is " "deprecated. Please use " "exec::asio::completion_token " "instead.")]] = exec::asio::completion_token; + + inline constexpr auto const & thread_unsafe_completion_token [[deprecated("asioexec::thread_" + "unsafe_completion_" + "token is deprecated. " + "Please use " + "exec::asio::thread_" + "unsafe_completion_" + "token instead.")]] + = exec::asio::thread_unsafe_completion_token; } // namespace asioexec diff --git a/include/asioexec/use_sender.hpp b/include/asioexec/use_sender.hpp index 9883b9b95..4008e3e4f 100644 --- a/include/asioexec/use_sender.hpp +++ b/include/asioexec/use_sender.hpp @@ -36,8 +36,19 @@ namespace asioexec "exec::asio::use_sender_t " "instead.")]] = exec::asio::use_sender_t; + using thread_unsafe_use_sender_t [[deprecated( + "asioexec::thread_unsafe_use_sender_t is deprecated. Please use " + "exec::asio::thread_unsafe_use_sender_t instead.")]] = exec::asio::thread_unsafe_use_sender_t; + inline constexpr auto const & use_sender [[deprecated("asioexec::use_sender is deprecated. " "Please use exec::asio::use_sender " "instead.")]] = exec::asio::use_sender; + + inline constexpr auto const & thread_unsafe_use_sender [[deprecated("asioexec::thread_unsafe_use_" + "sender is deprecated. " + "Please use " + "exec::asio::thread_unsafe_" + "use_sender instead.")]] + = exec::asio::thread_unsafe_use_sender; } // namespace asioexec diff --git a/include/exec/asio/completion_token.hpp b/include/exec/asio/completion_token.hpp index f8ad7b2cb..c1dda4f47 100644 --- a/include/exec/asio/completion_token.hpp +++ b/include/exec/asio/completion_token.hpp @@ -27,12 +27,12 @@ #include "../../stdexec/__detail/__queries.hpp" #include "../../stdexec/__detail/__receivers.hpp" #include "../../stdexec/__detail/__type_traits.hpp" +#include "../../stdexec/functional.hpp" #include "../../stdexec/stop_token.hpp" #include "as_default_on.hpp" #include #include -#include #include #include #include @@ -154,10 +154,7 @@ namespace experimental::execution::asio ::STDEXEC::set_error_t(std::exception_ptr), ::STDEXEC::set_stopped_t()>; - template - class completion_handler; - - template + template struct operation_state_base { class frame_; @@ -172,20 +169,19 @@ namespace experimental::execution::asio Receiver r_; asio_impl::cancellation_signal signal_; - std::recursive_mutex m_; + STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS + Mutex m_; frame_* frames_{nullptr}; std::exception_ptr ex_; bool abandoned_{false}; class frame_ { - operation_state_base& self_; - std::unique_lock l_; - frame_* prev_; + operation_state_base* self_; + frame_* prev_; public: explicit frame_(operation_state_base& self) noexcept - : self_(self) - , l_(self.m_) + : self_((self.m_.lock(), &self)) , prev_(self.frames_) { self.frames_ = this; @@ -195,23 +191,24 @@ namespace experimental::execution::asio ~frame_() noexcept { - if (l_) + if (self_) { - STDEXEC_ASSERT(self_.frames_ == this); - self_.frames_ = prev_; - if (!self_.frames_ && self_.abandoned_) + std::unique_lock l(self_->m_, std::adopt_lock); + STDEXEC_ASSERT(self_->frames_ == this); + self_->frames_ = prev_; + if (!self_->frames_ && self_->abandoned_) { // We are the last frame and the handler is gone so it's up to us to // finalize the operation - l_.unlock(); - self_.callback_.reset(); - if (self_.ex_) + l.unlock(); + self_->callback_.reset(); + if (self_->ex_) { - ::STDEXEC::set_error(static_cast(self_.r_), std::move(self_.ex_)); + ::STDEXEC::set_error(static_cast(self_->r_), std::move(self_->ex_)); } else { - ::STDEXEC::set_stopped(static_cast(self_.r_)); + ::STDEXEC::set_stopped(static_cast(self_->r_)); } } } @@ -219,22 +216,27 @@ namespace experimental::execution::asio explicit operator bool() const noexcept { - return bool(l_); + return bool(self_); } void release() noexcept { - auto ptr = this; - do + auto&& self = *self_; + STDEXEC_ASSERT(this == self.frames_); + for (;;) { - STDEXEC_ASSERT(ptr->l_); - STDEXEC_ASSERT(self_.frames_ == ptr); - ptr = ptr->prev_; - self_.frames_->l_.unlock(); - self_.frames_->prev_ = nullptr; - self_.frames_ = ptr; + STDEXEC_ASSERT(self.frames_); + STDEXEC_ASSERT(self.frames_->self_ == &self); + auto const current = std::exchange(self.frames_, self.frames_->prev_); + current->self_ = nullptr; + current->prev_ = nullptr; + self.m_.unlock(); + if (!self.frames_) + { + break; + } } - while (ptr); + STDEXEC_ASSERT(!self_); } }; @@ -274,12 +276,13 @@ namespace experimental::execution::asio callback_; }; - template + template class completion_handler { - operation_state_base* self_; + using operation_state_type_ = operation_state_base; + operation_state_type_* self_; public: - explicit completion_handler(operation_state_base& self) noexcept + explicit completion_handler(operation_state_type_& self) noexcept : self_(&self) {} @@ -296,7 +299,7 @@ namespace experimental::execution::asio // When this goes out of scope it might send set stopped or set error, or // it might defer that to the executor frames above us on the call stack // (if any) - typename operation_state_base::frame_ const frame(*self_); + typename operation_state_type_::frame_ const frame(*self_); self_->abandoned_ = true; } } @@ -343,17 +346,21 @@ namespace experimental::execution::asio return self_->signal_.slot(); } - operation_state_base& state() const noexcept + operation_state_type_& state() const noexcept { STDEXEC_ASSERT(self_); return *self_; } }; - template - class operation_state : operation_state_base + template + class operation_state : operation_state_base { - using base_ = operation_state_base; + using base_ = operation_state_base; Initiation init_; Args args_; public: @@ -377,9 +384,9 @@ namespace experimental::execution::asio std::apply( [&](auto&&... args) { - std::invoke(static_cast(init_), - completion_handler(*this), - static_cast(args)...); + ::STDEXEC::__invoke(static_cast(init_), + completion_handler(*this), + static_cast(args)...); }, std::move(args_)); } @@ -403,10 +410,13 @@ namespace experimental::execution::asio } }; - template + template class sender { using args_type_ = std::tuple...>; + template + using operation_state_type_ = + operation_state, Initiation, args_type_>; public: using sender_concept = ::STDEXEC::sender_tag; @@ -433,16 +443,12 @@ namespace experimental::execution::asio std::remove_cvref_t, ::STDEXEC::completion_signatures_of_t>> constexpr auto connect(Receiver&& receiver) const & noexcept( - std::is_nothrow_constructible_v< - operation_state, Initiation, args_type_>, - Receiver, - Initiation const &, - args_type_ const &>) + std::is_nothrow_constructible_v, + Receiver, + Initiation const &, + args_type_ const &>) { - return operation_state, Initiation, args_type_>( - static_cast(receiver), - init_, - args_); + return operation_state_type_(static_cast(receiver), init_, args_); } template @@ -450,27 +456,26 @@ namespace experimental::execution::asio std::remove_cvref_t, ::STDEXEC::completion_signatures_of_t>> constexpr auto connect(Receiver&& receiver) && noexcept( - std::is_nothrow_constructible_v< - operation_state, Initiation, args_type_>, - Receiver, - Initiation, - args_type_>) + std::is_nothrow_constructible_v, + Receiver, + Initiation, + args_type_>) { - return operation_state, Initiation, args_type_>( - static_cast(receiver), - static_cast(init_), - static_cast(args_)); + return operation_state_type_(static_cast(receiver), + static_cast(init_), + static_cast(args_)); } private: Initiation init_; args_type_ args_; }; - template + template class executor { - operation_state_base& self_; - Executor ex_; + using operation_state_type_ = operation_state_base; + operation_state_type_& self_; + Executor ex_; template constexpr auto wrap_(F f) const noexcept(std::is_nothrow_move_constructible_v) @@ -481,8 +486,7 @@ namespace experimental::execution::asio }; } public: - constexpr explicit executor(operation_state_base& self, - Executor const & ex) noexcept + constexpr explicit executor(operation_state_type_& self, Executor const & ex) noexcept : self_(self) , ex_(ex) {} @@ -503,7 +507,7 @@ namespace experimental::execution::asio constexpr decltype(auto) prefer(Args&&... args) const noexcept { auto const ex = asio_impl::prefer(ex_, static_cast(args)...); - return executor>(self_, ex); + return executor>(self_, ex); } template @@ -513,7 +517,7 @@ namespace experimental::execution::asio constexpr decltype(auto) require(Args&&... args) const noexcept { auto const ex = asio_impl::require(ex_, static_cast(args)...); - return executor>(self_, ex); + return executor>(self_, ex); } template @@ -569,17 +573,32 @@ namespace experimental::execution::asio bool operator!=(executor const & rhs) const = default; }; + template + struct token + { + static constexpr auto as_default_on = asio::as_default_on; + template + using as_default_on_t = asio::as_default_on_t; + }; + + struct null_basic_lockable + { + constexpr void lock() noexcept {} + + constexpr void unlock() noexcept {} + }; + } // namespace detail::completion_token - struct completion_token_t - { - static constexpr auto as_default_on = asio::as_default_on; - template - using as_default_on_t = asio::as_default_on_t; - }; + using completion_token_t = detail::completion_token::token; inline completion_token_t const completion_token{}; + using thread_unsafe_completion_token_t = + detail::completion_token::token; + + inline thread_unsafe_completion_token_t const thread_unsafe_completion_token{}; + } // namespace experimental::execution::asio namespace exec = experimental::execution; @@ -587,48 +606,53 @@ namespace exec = experimental::execution; namespace ASIOEXEC_ASIO_NAMESPACE { - template - struct async_result<::exec::asio::completion_token_t, Signatures...> + template + struct async_result<::exec::asio::detail::completion_token::token, Signatures...> { template requires(std::is_constructible_v, Args> && ...) - static constexpr auto - initiate(Initiation&& i, ::exec::asio::completion_token_t const &, Args&&... args) + static constexpr auto initiate(Initiation&& i, + ::exec::asio::detail::completion_token::token const &, + Args&&... args) { return ::exec::asio::detail::completion_token::sender< + Mutex, ::exec::asio::detail::completion_token::completion_signatures, std::remove_cvref_t, Args...>(static_cast(i), static_cast(args)...); } }; - template + template struct associated_executor< - ::exec::asio::detail::completion_token::completion_handler, + ::exec::asio::detail::completion_token::completion_handler, Executor> { - using type = ::exec::asio::detail::completion_token::executor; + using type = + ::exec::asio::detail::completion_token::executor; - static type - get(::exec::asio::detail::completion_token::completion_handler const & h, - Executor const & ex) noexcept + static type get(::exec::asio::detail::completion_token::completion_handler const & h, + Executor const & ex) noexcept { return type(h.state(), ex); } }; - template - requires requires(Receiver const & r) { ::STDEXEC::get_allocator(::STDEXEC::get_env(r)); } + template + requires ::STDEXEC::__callable<::STDEXEC::get_allocator_t, ::STDEXEC::env_of_t> struct associated_allocator< - ::exec::asio::detail::completion_token::completion_handler, + ::exec::asio::detail::completion_token::completion_handler, Allocator> { using type = std::remove_cvref_t())))>; - static type - get(::exec::asio::detail::completion_token::completion_handler const & h, - ::STDEXEC::__ignore = {}) noexcept + static type get(::exec::asio::detail::completion_token::completion_handler const & h, + ::STDEXEC::__ignore = {}) noexcept { return ::STDEXEC::get_allocator(::STDEXEC::get_env(h.state().r_)); } diff --git a/include/exec/asio/use_sender.hpp b/include/exec/asio/use_sender.hpp index 760b7b8d4..bb1ff8023 100644 --- a/include/exec/asio/use_sender.hpp +++ b/include/exec/asio/use_sender.hpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -215,17 +216,25 @@ namespace experimental::execution::asio template explicit sender(Sender) -> sender; + template + struct token + { + static constexpr auto as_default_on = asio::as_default_on; + template + using as_default_on_t = asio::as_default_on_t; + }; + } // namespace detail::use_sender - struct use_sender_t - { - static constexpr auto as_default_on = asio::as_default_on; - template - using as_default_on_t = asio::as_default_on_t; - }; + using use_sender_t = detail::use_sender::token; inline use_sender_t const use_sender{}; + using thread_unsafe_use_sender_t = + detail::use_sender::token; + + inline thread_unsafe_use_sender_t const thread_unsafe_use_sender{}; + } // namespace experimental::execution::asio namespace exec = experimental::execution; @@ -233,18 +242,18 @@ namespace exec = experimental::execution; namespace ASIOEXEC_ASIO_NAMESPACE { - template - struct async_result<::exec::asio::use_sender_t, Signatures...> + template + struct async_result<::exec::asio::detail::use_sender::token, Signatures...> { template requires(std::is_constructible_v, Args> && ...) static constexpr auto - initiate(Initiation&& i, ::exec::asio::use_sender_t const &, Args&&... args) + initiate(Initiation&& i, ::exec::asio::detail::use_sender::token const &, Args&&... args) { return ::exec::asio::detail::use_sender::sender( - async_result<::exec::asio::completion_token_t, Signatures...>::initiate( + async_result<::exec::asio::detail::completion_token::token, Signatures...>::initiate( static_cast(i), - ::exec::asio::completion_token, + ::exec::asio::detail::completion_token::token{}, static_cast(args)...)); } }; diff --git a/test/exec/asio/test_completion_token.cpp b/test/exec/asio/test_completion_token.cpp index 4cbcf63e6..b96bd1d2b 100644 --- a/test/exec/asio/test_completion_token.cpp +++ b/test/exec/asio/test_completion_token.cpp @@ -166,7 +166,7 @@ namespace asio_impl::io_context ctx; asio_impl::system_timer t(ctx); t.expires_after(std::chrono::years(1)); - auto sender = t.async_wait(completion_token); + auto sender = t.async_wait(thread_unsafe_completion_token); static_assert(set_equivalent>, completion_signatures); static_assert(::STDEXEC::sender_of); static_assert(