From be3ff137bc47ac78ea720ef00223088eb801e25d Mon Sep 17 00:00:00 2001 From: Cra3z <3324654761@qq.com> Date: Tue, 5 May 2026 16:16:27 +0800 Subject: [PATCH] Fix async scopes --- .../execution/detail/counting_scope_base.hpp | 60 ++++++++++--------- .../execution/detail/counting_scope_join.hpp | 26 +++++++- .../beman/execution/detail/unreachable.hpp | 29 +++++++++ src/beman/execution/CMakeLists.txt | 2 + src/beman/execution/unreachable.cppm | 11 ++++ .../execution/exec-scope-counting.test.cpp | 14 +++++ .../exec-scope-simple-counting.test.cpp | 14 +++++ 7 files changed, 126 insertions(+), 30 deletions(-) create mode 100644 include/beman/execution/detail/unreachable.hpp create mode 100644 src/beman/execution/unreachable.cppm diff --git a/include/beman/execution/detail/counting_scope_base.hpp b/include/beman/execution/detail/counting_scope_base.hpp index 02062fbb..66802682 100644 --- a/include/beman/execution/detail/counting_scope_base.hpp +++ b/include/beman/execution/detail/counting_scope_base.hpp @@ -10,13 +10,16 @@ import std; #else #include #include +#include #include #include #endif #ifdef BEMAN_HAS_MODULES import beman.execution.detail.immovable; +import beman.execution.detail.unreachable; #else #include +#include #endif // ---------------------------------------------------------------------------- @@ -33,7 +36,7 @@ class beman::execution::detail::counting_scope_base : ::beman::execution::detail counting_scope_base(counting_scope_base&&) = delete; ~counting_scope_base(); - static constexpr ::std::size_t max_associations{8194u}; + static constexpr ::std::size_t max_associations = ::std::numeric_limits<::std::size_t>::max(); auto close() noexcept -> void; @@ -62,7 +65,7 @@ class beman::execution::detail::counting_scope_base : ::beman::execution::detail } auto operator=(assoc_t other) noexcept -> assoc_t& { - std::swap(scope, other.scope); + ::std::swap(scope, other.scope); return *this; } @@ -100,8 +103,9 @@ class beman::execution::detail::counting_scope_base : ::beman::execution::detail }; auto try_associate() noexcept -> assoc_t; + auto disassociate() noexcept -> void; - auto complete() noexcept -> void; + auto add_node(node* n) noexcept -> void; ::std::mutex mutex; @@ -142,11 +146,15 @@ inline auto beman::execution::detail::counting_scope_base::close() noexcept -> v } inline auto beman::execution::detail::counting_scope_base::add_node(node* n) noexcept -> void { - n->next = std::exchange(this->head, n); + n->next = ::std::exchange(this->head, n); } inline auto beman::execution::detail::counting_scope_base::try_associate() noexcept -> assoc_t { ::std::lock_guard lock(this->mutex); + if (this->count == max_associations) { + return assoc_t{}; + } + switch (this->state) { default: return assoc_t{}; @@ -161,20 +169,14 @@ inline auto beman::execution::detail::counting_scope_base::try_associate() noexc } inline auto beman::execution::detail::counting_scope_base::disassociate() noexcept -> void { - { - ::std::lock_guard lock(this->mutex); - if (0u < --this->count) - return; - this->state = state_t::joined; - } - this->complete(); -} + ::std::unique_lock guard(this->mutex); + if (--this->count > 0u || (this->state != state_t::open_and_joining && this->state != state_t::closed_and_joining)) + return; + + this->state = state_t::joined; + node* current = ::std::exchange(this->head, nullptr); + guard.unlock(); -inline auto beman::execution::detail::counting_scope_base::complete() noexcept -> void { - node* current{[this] { - ::std::lock_guard lock(this->mutex); - return ::std::exchange(this->head, nullptr); - }()}; while (current) { ::std::exchange(current, current->next)->complete(); } @@ -182,24 +184,26 @@ inline auto beman::execution::detail::counting_scope_base::complete() noexcept - inline auto beman::execution::detail::counting_scope_base::start_node(node* n) -> void { ::std::unique_lock guard(this->mutex); - switch (this->state) { - case ::beman::execution::detail::counting_scope_base::state_t::unused: - case ::beman::execution::detail::counting_scope_base::state_t::unused_and_closed: - case ::beman::execution::detail::counting_scope_base::state_t::joined: - this->state = ::beman::execution::detail::counting_scope_base::state_t::joined; + if (this->count == 0u) { + this->state = state_t::joined; guard.unlock(); n->complete_inline(); return; - case ::beman::execution::detail::counting_scope_base::state_t::open: - this->state = ::beman::execution::detail::counting_scope_base::state_t::open_and_joining; + } + + switch (this->state) { + case state_t::open: + this->state = state_t::open_and_joining; break; - case ::beman::execution::detail::counting_scope_base::state_t::open_and_joining: + case state_t::open_and_joining: break; - case ::beman::execution::detail::counting_scope_base::state_t::closed: - this->state = ::beman::execution::detail::counting_scope_base::state_t::closed_and_joining; + case state_t::closed: + this->state = state_t::closed_and_joining; break; - case ::beman::execution::detail::counting_scope_base::state_t::closed_and_joining: + case state_t::closed_and_joining: break; + default: + ::beman::execution::detail::unreachable(); } this->add_node(n); } diff --git a/include/beman/execution/detail/counting_scope_join.hpp b/include/beman/execution/detail/counting_scope_join.hpp index a4ec7245..a7059da3 100644 --- a/include/beman/execution/detail/counting_scope_join.hpp +++ b/include/beman/execution/detail/counting_scope_join.hpp @@ -25,6 +25,8 @@ import beman.execution.detail.make_sender; import beman.execution.detail.receiver; import beman.execution.detail.schedule; import beman.execution.detail.set_value; +import beman.execution.detail.set_error; +import beman.execution.detail.set_stopped; import beman.execution.detail.start; #else #include @@ -39,6 +41,8 @@ import beman.execution.detail.start; #include #include #include +#include +#include #include #endif @@ -82,9 +86,26 @@ inline constexpr counting_scope_join_t counting_scope_join{}; template <::beman::execution::receiver Receiver> struct beman::execution::detail::counting_scope_join_t::state : ::beman::execution::detail::counting_scope_base::node { + struct receiver_ref { + using receiver_concept = ::beman::execution::receiver_tag; + + auto set_value() && noexcept -> void { ::beman::execution::set_value(::std::move(rcvr)); } + + template + auto set_error(E&& e) && noexcept -> void { + ::beman::execution::set_error(::std::move(rcvr), ::std::forward(e)); + } + + auto set_stopped() && noexcept -> void { ::beman::execution::set_stopped(::std::move(rcvr)); } + + auto get_env() const noexcept { return ::beman::execution::get_env(rcvr); } + + Receiver& rcvr; + }; + using op_t = decltype(::beman::execution::connect(::beman::execution::schedule(::beman::execution::get_scheduler( ::beman::execution::get_env(::std::declval()))), - ::std::declval())); + ::std::declval())); ::beman::execution::detail::counting_scope_base* scope; explicit state(::beman::execution::detail::counting_scope_base* s, Receiver& r) @@ -92,10 +113,11 @@ struct beman::execution::detail::counting_scope_join_t::state : ::beman::executi receiver(r), op(::beman::execution::connect(::beman::execution::schedule(::beman::execution::get_scheduler( ::beman::execution::get_env(this->receiver))), - this->receiver)) {} + receiver_ref(this->receiver))) {} virtual ~state() = default; auto complete() noexcept -> void override { ::beman::execution::start(this->op); } + auto complete_inline() noexcept -> void override { ::beman::execution::set_value(::std::move(this->receiver)); } auto start() noexcept -> void { this->scope->start_node(this); } diff --git a/include/beman/execution/detail/unreachable.hpp b/include/beman/execution/detail/unreachable.hpp new file mode 100644 index 00000000..6da60ef9 --- /dev/null +++ b/include/beman/execution/detail/unreachable.hpp @@ -0,0 +1,29 @@ +// include/beman/execution/detail/unreachable.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_UNREACHABLE +#define INCLUDED_BEMAN_EXECUTION_DETAIL_UNREACHABLE + +#include +#ifdef BEMAN_HAS_IMPORT_STD +import std; +#else +#include +#include +#endif + +// ---------------------------------------------------------------------------- + +namespace beman::execution::detail { +[[noreturn]] inline auto unreachable() -> void { +#ifdef __cpp_lib_unreachable + ::std::unreachable(); +#else + ::std::terminate(); +#endif +} +} // namespace beman::execution::detail + +// ---------------------------------------------------------------------------- + +#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_UNREACHABLE diff --git a/src/beman/execution/CMakeLists.txt b/src/beman/execution/CMakeLists.txt index 24d6c881..8fd78364 100644 --- a/src/beman/execution/CMakeLists.txt +++ b/src/beman/execution/CMakeLists.txt @@ -194,6 +194,7 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/unspecified_promise.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/unstoppable.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/unstoppable_token.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/unreachable.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/valid_completion_for.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/valid_completion_signatures.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/valid_specialization.hpp @@ -387,6 +388,7 @@ if(BEMAN_USE_MODULES) unspecified_promise.cppm unstoppable.cppm unstoppable_token.cppm + unreachable.cppm valid_completion_for.cppm valid_completion_signatures.cppm valid_specialization.cppm diff --git a/src/beman/execution/unreachable.cppm b/src/beman/execution/unreachable.cppm new file mode 100644 index 00000000..907c9256 --- /dev/null +++ b/src/beman/execution/unreachable.cppm @@ -0,0 +1,11 @@ +module; +// src/beman/execution/unreachable.cppm -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include + +export module beman.execution.detail.unreachable; + +namespace beman::execution::detail { +export using beman::execution::detail::unreachable; +} // namespace beman::execution::detail diff --git a/tests/beman/execution/exec-scope-counting.test.cpp b/tests/beman/execution/exec-scope-counting.test.cpp index ec023787..fdaf152f 100644 --- a/tests/beman/execution/exec-scope-counting.test.cpp +++ b/tests/beman/execution/exec-scope-counting.test.cpp @@ -11,7 +11,9 @@ import beman.execution; #include #include #include +#include #include +#include #include #endif @@ -113,6 +115,17 @@ auto token() -> void { ASSERT(true == called); } +auto spawn() -> void { + constexpr std::size_t expected = 10; + std::size_t counter = 0; + test_std::counting_scope scope; + for (std::size_t i = 0; i < expected; ++i) { + test_std::spawn(test_std::just() | test_std::then([&counter]() noexcept { ++counter; }), scope.get_token()); + } + test_std::sync_wait(scope.join()); + ASSERT(counter == expected); +} + } // namespace TEST(exec_scope_counting) { @@ -120,4 +133,5 @@ TEST(exec_scope_counting) { ctor(); mem(); token(); + spawn(); } diff --git a/tests/beman/execution/exec-scope-simple-counting.test.cpp b/tests/beman/execution/exec-scope-simple-counting.test.cpp index 30aec3c7..202a6154 100644 --- a/tests/beman/execution/exec-scope-simple-counting.test.cpp +++ b/tests/beman/execution/exec-scope-simple-counting.test.cpp @@ -11,7 +11,9 @@ import beman.execution; #include #include #include +#include #include +#include #include #endif @@ -111,6 +113,17 @@ auto token() -> void { ASSERT(true == called); } +auto spawn() -> void { + constexpr std::size_t expected = 10; + std::size_t counter = 0; + test_std::simple_counting_scope scope; + for (std::size_t i = 0; i < expected; ++i) { + test_std::spawn(test_std::just() | test_std::then([&counter]() noexcept { ++counter; }), scope.get_token()); + } + test_std::sync_wait(scope.join()); + ASSERT(counter == expected); +} + } // namespace TEST(exec_scope_simple_counting) { @@ -118,4 +131,5 @@ TEST(exec_scope_simple_counting) { ctor(); mem(); token(); + spawn(); }