From 2cdd844ea1c51c787f7758246142b655c7537ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Sun, 1 Dec 2024 09:29:27 +0000 Subject: [PATCH 01/41] created the infrastructure for concurrent queues --- include/beman/execution26/conqueue.hpp | 17 ++++++ .../detail/async_concurrent_queue.hpp | 24 ++++++++ .../detail/basic_concurrent_queue.hpp | 30 ++++++++++ .../execution26/detail/concurrent_queue.hpp | 26 ++++++++ .../execution26/detail/conqueue_errc.hpp | 60 +++++++++++++++++++ .../execution26/detail/conqueue_error.hpp | 23 +++++++ .../beman/execution26/detail/sender_in_of.hpp | 27 +++++++++ .../beman/execution26/detail/sender_of.hpp | 19 ++++++ .../execution26/detail/value_signature.hpp | 18 ++++++ src/beman/execution26/CMakeLists.txt | 17 ++++++ tests/beman/execution26/CMakeLists.txt | 5 ++ .../async-concurrent-queue.test.cpp | 45 ++++++++++++++ .../basic-concurrent-queue.test.cpp | 31 ++++++++++ .../execution26/concurrent-queue.test.cpp | 41 +++++++++++++ .../beman/execution26/conqueue-errc.test.cpp | 48 +++++++++++++++ .../beman/execution26/conqueue-error.test.cpp | 22 +++++++ .../execution26/exec-snd-concepts.test.cpp | 48 +++++++++++++++ 17 files changed, 501 insertions(+) create mode 100644 include/beman/execution26/conqueue.hpp create mode 100644 include/beman/execution26/detail/async_concurrent_queue.hpp create mode 100644 include/beman/execution26/detail/basic_concurrent_queue.hpp create mode 100644 include/beman/execution26/detail/concurrent_queue.hpp create mode 100644 include/beman/execution26/detail/conqueue_errc.hpp create mode 100644 include/beman/execution26/detail/conqueue_error.hpp create mode 100644 include/beman/execution26/detail/sender_in_of.hpp create mode 100644 include/beman/execution26/detail/sender_of.hpp create mode 100644 include/beman/execution26/detail/value_signature.hpp create mode 100644 tests/beman/execution26/async-concurrent-queue.test.cpp create mode 100644 tests/beman/execution26/basic-concurrent-queue.test.cpp create mode 100644 tests/beman/execution26/concurrent-queue.test.cpp create mode 100644 tests/beman/execution26/conqueue-errc.test.cpp create mode 100644 tests/beman/execution26/conqueue-error.test.cpp diff --git a/include/beman/execution26/conqueue.hpp b/include/beman/execution26/conqueue.hpp new file mode 100644 index 00000000..cd03f436 --- /dev/null +++ b/include/beman/execution26/conqueue.hpp @@ -0,0 +1,17 @@ +// include/beman/execution26/conqueue.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_BEMAN_EXECUTION26_CONQUEUE +#define INCLUDED_BEMAN_EXECUTION26_CONQUEUE + +// ---------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- + +#endif diff --git a/include/beman/execution26/detail/async_concurrent_queue.hpp b/include/beman/execution26/detail/async_concurrent_queue.hpp new file mode 100644 index 00000000..6b1b820c --- /dev/null +++ b/include/beman/execution26/detail/async_concurrent_queue.hpp @@ -0,0 +1,24 @@ +// include/beman/execution26/detail/async_concurrent_queue.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_ASYNC_CONCURRENT_QUEUE +#define INCLUDED_BEMAN_EXECUTION26_DETAIL_ASYNC_CONCURRENT_QUEUE + +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- + +namespace beman::execution26::detail { +template +concept async_concurrent_queue = ::beman::execution26::detail::basic_concurrent_queue && requires(Q q, T&& t) { + { q.async_push(::std::forward(t)) } noexcept -> ::beman::execution26::detail::sender_of<>; + { q.async_pop() } noexcept -> ::beman::execution26::detail::sender_of; +}; +} // namespace beman::execution26::detail + +// ---------------------------------------------------------------------------- + +#endif diff --git a/include/beman/execution26/detail/basic_concurrent_queue.hpp b/include/beman/execution26/detail/basic_concurrent_queue.hpp new file mode 100644 index 00000000..27a92e6f --- /dev/null +++ b/include/beman/execution26/detail/basic_concurrent_queue.hpp @@ -0,0 +1,30 @@ +// include/beman/execution26/detail/basic_concurrent_queue.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_BASIC_CONCURRENT_QUEUE +#define INCLUDED_BEMAN_EXECUTION26_DETAIL_BASIC_CONCURRENT_QUEUE + +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- + +namespace beman::execution26::detail { +template +concept basic_concurrent_queue = + ::std::move_constructible<::std::remove_cvref_t> && ::std::same_as<::std::decay_t, typename Q::value_type> && + requires(Q q, const Q qc, T&& t, ::std::error_code ec) { + { qc.is_closed() } noexcept -> ::std::same_as; + { q.close() } noexcept -> ::std::same_as; + { q.push(std::forward(t)) } -> ::std::same_as; + { q.push(std::forward(t), ec) } noexcept -> ::std::same_as; + { q.pop(ec) } -> ::std::same_as<::std::optional>; + { q.pop() } -> ::std::same_as; + }; +} // namespace beman::execution26::detail +// ---------------------------------------------------------------------------- + +#endif diff --git a/include/beman/execution26/detail/concurrent_queue.hpp b/include/beman/execution26/detail/concurrent_queue.hpp new file mode 100644 index 00000000..8944bb8c --- /dev/null +++ b/include/beman/execution26/detail/concurrent_queue.hpp @@ -0,0 +1,26 @@ +// include/beman/execution26/detail/concurrent_queue.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_CONCURRENT_QUEUE +#define INCLUDED_BEMAN_EXECUTION26_DETAIL_CONCURRENT_QUEUE + +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- + +namespace beman::execution26::detail { +template +concept concurrent_queue = + ::beman::execution26::detail::basic_concurrent_queue && requires(Q q, T&& t, ::std::error_code ec) { + { q.try_push(::std::forward(t), ec) } -> ::std::same_as; + { q.try_pop(ec) } -> ::std::same_as<::std::optional>; + }; +} // namespace beman::execution26::detail + +// ---------------------------------------------------------------------------- + +#endif diff --git a/include/beman/execution26/detail/conqueue_errc.hpp b/include/beman/execution26/detail/conqueue_errc.hpp new file mode 100644 index 00000000..aac0859f --- /dev/null +++ b/include/beman/execution26/detail/conqueue_errc.hpp @@ -0,0 +1,60 @@ +// include/beman/execution26/detail/conqueue_errc.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_CONQUEUE_ERRC +#define INCLUDED_BEMAN_EXECUTION26_DETAIL_CONQUEUE_ERRC + +#include +#include +#include + +// ---------------------------------------------------------------------------- + +namespace beman::execution26 { +enum class conqueue_errc { empty, full, closed, busy }; + +inline auto conqueue_category() noexcept -> const ::std::error_category&; +inline auto make_error_code(::beman::execution26::conqueue_errc) noexcept -> ::std::error_code; +inline auto make_error_condition(::beman::execution26::conqueue_errc) noexcept -> ::std::error_condition; +} // namespace beman::execution26 + +namespace std { +template <> +struct is_error_code_enum<::beman::execution26::conqueue_errc> : ::std::true_type {}; +} // namespace std + +// ---------------------------------------------------------------------------- + +inline auto beman::execution26::conqueue_category() noexcept -> const ::std::error_category& { + struct category : ::std::error_category { + auto name() const noexcept -> const char* override { return "conqueue"; } + auto message(int value) const -> ::std::string override { + switch (value) { + default: + return "unknown"; + case static_cast(::beman::execution26::conqueue_errc::empty): + return "empty"; + case static_cast(::beman::execution26::conqueue_errc::full): + return "full"; + case static_cast(::beman::execution26::conqueue_errc::closed): + return "closed"; + case static_cast(::beman::execution26::conqueue_errc::busy): + return "busy"; + } + } + }; + static category rc{}; + return rc; +} + +inline auto beman::execution26::make_error_code(conqueue_errc e) noexcept -> ::std::error_code { + return ::std::error_code(static_cast(e), ::beman::execution26::conqueue_category()); +} + +inline auto beman::execution26::make_error_condition(conqueue_errc e) noexcept -> ::std::error_condition { + return ::std::error_condition(static_cast(e), ::beman::execution26::conqueue_category()); +} + +// ---------------------------------------------------------------------------- + +#endif diff --git a/include/beman/execution26/detail/conqueue_error.hpp b/include/beman/execution26/detail/conqueue_error.hpp new file mode 100644 index 00000000..86b3e532 --- /dev/null +++ b/include/beman/execution26/detail/conqueue_error.hpp @@ -0,0 +1,23 @@ +// include/beman/execution26/detail/conqueue_error.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_CONQUEUE_ERROR +#define INCLUDED_BEMAN_EXECUTION26_DETAIL_CONQUEUE_ERROR + +#include +#include + +// ---------------------------------------------------------------------------- + +namespace beman::execution26 { +class conqueue_error : public ::std::system_error { + public: + explicit conqueue_error(::beman::execution26::conqueue_errc ec) + : std::system_error(::beman::execution26::make_error_code(ec), + ::beman::execution26::conqueue_category().message(static_cast(ec))) {} +}; +} // namespace beman::execution26 + +// ---------------------------------------------------------------------------- + +#endif diff --git a/include/beman/execution26/detail/sender_in_of.hpp b/include/beman/execution26/detail/sender_in_of.hpp new file mode 100644 index 00000000..931a7d5f --- /dev/null +++ b/include/beman/execution26/detail/sender_in_of.hpp @@ -0,0 +1,27 @@ +// include/beman/execution26/detail/sender_in_of.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_SENDER_IN_OF +#define INCLUDED_BEMAN_EXECUTION26_DETAIL_SENDER_IN_OF + +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- + +namespace beman::execution26::detail { +template +concept sender_in_of = + ::beman::execution26::sender_in && + ::beman::execution26::detail::matching_sig< + ::beman::execution26::set_value_t(A...), + ::beman::execution26:: + value_types_of_t >; +} + +// ---------------------------------------------------------------------------- + +#endif diff --git a/include/beman/execution26/detail/sender_of.hpp b/include/beman/execution26/detail/sender_of.hpp new file mode 100644 index 00000000..46105906 --- /dev/null +++ b/include/beman/execution26/detail/sender_of.hpp @@ -0,0 +1,19 @@ +// include/beman/execution26/detail/sender_of.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_SENDER_OF +#define INCLUDED_BEMAN_EXECUTION26_DETAIL_SENDER_OF + +#include +#include + +// ---------------------------------------------------------------------------- + +namespace beman::execution26::detail { +template +concept sender_of = ::beman::execution26::detail::sender_in_of; +} + +// ---------------------------------------------------------------------------- + +#endif diff --git a/include/beman/execution26/detail/value_signature.hpp b/include/beman/execution26/detail/value_signature.hpp new file mode 100644 index 00000000..709f9be6 --- /dev/null +++ b/include/beman/execution26/detail/value_signature.hpp @@ -0,0 +1,18 @@ +// include/beman/execution26/detail/value_signature.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_VALUE_SIGNATURE +#define INCLUDED_BEMAN_EXECUTION26_DETAIL_VALUE_SIGNATURE + +#include + +// ---------------------------------------------------------------------------- + +namespace beman::execution26::detail { +template +using value_signature = ::beman::execution26::set_value_t(A...); +} + +// ---------------------------------------------------------------------------- + +#endif diff --git a/src/beman/execution26/CMakeLists.txt b/src/beman/execution26/CMakeLists.txt index 6edb36a2..78f48846 100644 --- a/src/beman/execution26/CMakeLists.txt +++ b/src/beman/execution26/CMakeLists.txt @@ -24,6 +24,7 @@ target_sources( BASE_DIRS ${PROJECT_SOURCE_DIR}/include FILES + ${PROJECT_SOURCE_DIR}/include/beman/execution26/conqueue.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/execution.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/functional.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/stop_token.hpp @@ -39,8 +40,12 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/apply_sender.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/as_awaitable.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/as_except_ptr.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/async_concurrent_queue.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/atomic_intrusive_stack.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/await_result_type.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/await_suspend_result.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/awaitable_sender.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/basic_concurrent_queue.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/basic_operation.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/basic_receiver.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/basic_sender.hpp @@ -49,6 +54,7 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/callable.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/check_type_alias_exist.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/child_type.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/class_type.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/common.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/completion_domain.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/completion_signature.hpp @@ -56,14 +62,17 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/completion_signatures_for.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/completion_signatures_of_t.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/completion_tag.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/concurrent_queue.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/connect.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/connect_all.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/connect_all_result.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/connect_awaitable.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/connect_result_t.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/conqueue_errc.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/continues_on.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/decayed_same_as.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/decayed_tuple.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/decayed_type_list.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/decayed_typeof.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/decays_to.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/default_domain.hpp @@ -97,6 +106,7 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/indirect_meta_apply.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/inplace_stop_source.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/into_variant.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/intrusive_stack.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/is_awaitable.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/is_awaiter.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/join_env.hpp @@ -138,9 +148,12 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/sender.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/sender_adaptor.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/sender_adaptor_closure.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/sender_awaitable.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/sender_decompose.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/sender_for.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/sender_in.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/sender_in_of.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/sender_of.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/sends_stopped.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/set_error.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/set_stopped.hpp @@ -149,6 +162,7 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/simple_counting_scope.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/single_sender.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/single_sender_value_type.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/split.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/start.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/starts_on.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/state_type.hpp @@ -165,15 +179,18 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/then.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/transform_sender.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/type_list.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/unspecified_promise.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/unstoppable_token.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/valid_completion_for.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/valid_completion_signatures.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/valid_specialization.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/value_signature.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/value_types_of_t.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/variant_or_empty.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/when_all.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/when_all_with_variant.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/with_await_transform.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/with_awaitable_senders.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/write_env.hpp ) diff --git a/tests/beman/execution26/CMakeLists.txt b/tests/beman/execution26/CMakeLists.txt index e1d41a7c..d9285a23 100644 --- a/tests/beman/execution26/CMakeLists.txt +++ b/tests/beman/execution26/CMakeLists.txt @@ -11,6 +11,11 @@ endif() list( APPEND execution_tests + conqueue-error.test + conqueue-errc.test + async-concurrent-queue.test + concurrent-queue.test + basic-concurrent-queue.test notify.test exec-scounting.test exec-awaitable.test diff --git a/tests/beman/execution26/async-concurrent-queue.test.cpp b/tests/beman/execution26/async-concurrent-queue.test.cpp new file mode 100644 index 00000000..8c08f3e9 --- /dev/null +++ b/tests/beman/execution26/async-concurrent-queue.test.cpp @@ -0,0 +1,45 @@ +// tests/beman/execution26/async-concurrent-queue.test.cpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- + +namespace { +template +struct queue { + using value_type = T; + + auto is_closed() const noexcept -> bool; + auto close() noexcept -> void; + auto push(auto&&) -> void; + auto push(auto&&, std::error_code&) noexcept -> bool; + auto pop() -> T; + auto pop(std::error_code) noexcept -> std::optional; + auto async_push(auto&&) noexcept -> decltype(test_std::just()); + auto async_pop() noexcept -> decltype(test_std::just(std::declval())); +}; + +template + requires test_detail::basic_concurrent_queue +auto overload(Q&) {} +template + requires test_detail::async_concurrent_queue +auto overload(Q&) {} +} // namespace + +TEST(concurrent_queue) { + static_assert(test_detail::basic_concurrent_queue>); + static_assert(test_detail::async_concurrent_queue>); + + queue q; + overload(q); +} + +// ---------------------------------------------------------------------------- \ No newline at end of file diff --git a/tests/beman/execution26/basic-concurrent-queue.test.cpp b/tests/beman/execution26/basic-concurrent-queue.test.cpp new file mode 100644 index 00000000..dcfbf1e7 --- /dev/null +++ b/tests/beman/execution26/basic-concurrent-queue.test.cpp @@ -0,0 +1,31 @@ +// tests/beman/execution26/basic-concurrent-queue.test.cpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- + +namespace { +template +struct queue { + using value_type = T; + + auto is_closed() const noexcept -> bool; + auto close() noexcept -> void; + auto push(auto&&) -> void; + auto push(auto&&, std::error_code&) noexcept -> bool; + auto pop() -> T; + auto pop(std::error_code) noexcept -> std::optional; +}; +} // namespace + +TEST(basic_concurrent_queue) { + static_assert(test_detail::basic_concurrent_queue>); + static_assert(not test_detail::basic_concurrent_queue>); + static_assert( + not test_detail::basic_concurrent_queue>); +} \ No newline at end of file diff --git a/tests/beman/execution26/concurrent-queue.test.cpp b/tests/beman/execution26/concurrent-queue.test.cpp new file mode 100644 index 00000000..caa4f65d --- /dev/null +++ b/tests/beman/execution26/concurrent-queue.test.cpp @@ -0,0 +1,41 @@ +// tests/beman/execution26/concurrent-queue.test.cpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- + +namespace { +template +struct queue { + using value_type = T; + + auto is_closed() const noexcept -> bool; + auto close() noexcept -> void; + auto push(auto&&) -> void; + auto push(auto&&, std::error_code&) noexcept -> bool; + auto pop() -> T; + auto pop(std::error_code) noexcept -> std::optional; + auto try_push(auto&&, std::error_code&) noexcept -> bool; + auto try_pop(std::error_code&) noexcept -> std::optional; +}; + +template + requires test_detail::basic_concurrent_queue +auto overload(Q&) {} +template + requires test_detail::concurrent_queue +auto overload(Q&) {} +} // namespace + +TEST(concurrent_queue) { + static_assert(test_detail::basic_concurrent_queue>); + static_assert(test_detail::concurrent_queue>); + + queue q; + overload(q); +} \ No newline at end of file diff --git a/tests/beman/execution26/conqueue-errc.test.cpp b/tests/beman/execution26/conqueue-errc.test.cpp new file mode 100644 index 00000000..dc88cf3b --- /dev/null +++ b/tests/beman/execution26/conqueue-errc.test.cpp @@ -0,0 +1,48 @@ +// tests/beman/execution26/conqueue_errc.test.cpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- + +TEST(conqueue_errc) { + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + + static_assert(test_std::conqueue_errc::empty != test_std::conqueue_errc::full); + static_assert(test_std::conqueue_errc::empty != test_std::conqueue_errc::closed); + static_assert(test_std::conqueue_errc::empty != test_std::conqueue_errc::busy); + static_assert(test_std::conqueue_errc::full != test_std::conqueue_errc::closed); + static_assert(test_std::conqueue_errc::full != test_std::conqueue_errc::busy); + static_assert(test_std::conqueue_errc::closed != test_std::conqueue_errc::busy); + static_assert(std::is_error_code_enum()); + + static_assert(std::same_as); + static_assert(noexcept(test_std::conqueue_category())); + const auto& cat{test_std::conqueue_category()}; + ASSERT(cat.name() == std::string_view("conqueue")); + ASSERT(cat.message(static_cast(test_std::conqueue_errc::empty)) == "empty"); + ASSERT(cat.message(static_cast(test_std::conqueue_errc::full)) == "full"); + ASSERT(cat.message(static_cast(test_std::conqueue_errc::closed)) == "closed"); + ASSERT(cat.message(static_cast(test_std::conqueue_errc::busy)) == "busy"); + ASSERT(cat.message(17) == "unknown"); + + static_assert(std::same_as); + static_assert(noexcept(test_std::make_error_code(test_std::conqueue_errc::full))); + std::error_code ec{test_std::make_error_code(test_std::conqueue_errc::full)}; + ASSERT(ec.category() == test_std::conqueue_category()); + ASSERT(ec.value() == static_cast(test_std::conqueue_errc::full)); + + static_assert( + std::same_as); + static_assert(noexcept(test_std::make_error_condition(test_std::conqueue_errc::full))); + std::error_condition econd{test_std::make_error_condition(test_std::conqueue_errc::full)}; + ASSERT(econd.category() == test_std::conqueue_category()); + ASSERT(econd.value() == static_cast(test_std::conqueue_errc::full)); +} diff --git a/tests/beman/execution26/conqueue-error.test.cpp b/tests/beman/execution26/conqueue-error.test.cpp new file mode 100644 index 00000000..0fabe9c2 --- /dev/null +++ b/tests/beman/execution26/conqueue-error.test.cpp @@ -0,0 +1,22 @@ +// tests/beman/execution26/conqueue-error.test.cpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- + +TEST(conqueue_error) { + static_assert(std::is_base_of_v); + test_std::conqueue_error e0(test_std::conqueue_errc::empty); + test_std::conqueue_error e1(test_std::conqueue_errc::full); + + ASSERT(e0.code() == make_error_code(test_std::conqueue_errc::empty)); + ASSERT(std::string_view(e0.what()).find("empty") != std::string_view::npos); + ASSERT(e1.code() == make_error_code(test_std::conqueue_errc::full)); + ASSERT(std::string_view(e1.what()).find("full") != std::string_view::npos); +} \ No newline at end of file diff --git a/tests/beman/execution26/exec-snd-concepts.test.cpp b/tests/beman/execution26/exec-snd-concepts.test.cpp index ebad148c..b55d5643 100644 --- a/tests/beman/execution26/exec-snd-concepts.test.cpp +++ b/tests/beman/execution26/exec-snd-concepts.test.cpp @@ -1,6 +1,9 @@ // src/beman/execution26/tests/exec-snd-concepts.test.cpp -*-C++-*- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include +#include +#include #include #include #include @@ -14,6 +17,9 @@ // ---------------------------------------------------------------------------- namespace { +struct env {}; +struct unsupported_env {}; + struct non_sender {}; struct own_sender_t : test_std::sender_t {}; @@ -77,6 +83,21 @@ struct product_sender4 : test_detail::product_type{}; + } + auto get_completion_signatures(const test_std::empty_env&) const { + return test_std::completion_signatures{}; + } +}; + +static_assert(test_std::sender); +static_assert(test_std::sender_in); +static_assert(test_std::sender_in); + // ------------------------------------------------------------------------- auto test_valid_completion_signatures() { @@ -164,6 +185,30 @@ auto test_sender_for() -> void { static_assert(test_std::sender); static_assert(not test_detail::sender_for); } + +auto test_value_signature() -> void { + static_assert(std::same_as, test_std::set_value_t()>); + static_assert(std::same_as, test_std::set_value_t(int)>); + static_assert(std::same_as, test_std::set_value_t(int, bool)>); + static_assert( + std::same_as, test_std::set_value_t(int, bool, double&)>); +} + +template +auto test_sender_in_of(Sender) -> void { + static_assert(not test_detail::sender_in_of); + static_assert(not test_detail::sender_in_of); + static_assert(test_detail::sender_in_of); + static_assert(not test_detail::sender_in_of); + static_assert(not test_detail::sender_in_of); + static_assert(test_detail::sender_in_of); +} + +template +auto test_sender_of(Sender) -> void { + static_assert(not test_detail::sender_of); + static_assert(test_detail::sender_of); +} } // namespace TEST(exec_snd_concepts) { @@ -174,4 +219,7 @@ TEST(exec_snd_concepts) { test_sender_in(); test_tag_of_t(); test_sender_for(); + test_value_signature(); + test_sender_in_of(sender_of{}); + test_sender_of(sender_of{}); } From e023c2ee7b74bace8ae0021ec16115d7952e8655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Sun, 1 Dec 2024 10:15:06 +0000 Subject: [PATCH 02/41] clean-up some included headers which caused a bit of grief --- include/beman/execution26/detail/as_awaitable.hpp | 1 - include/beman/execution26/detail/sender_awaitable.hpp | 5 ----- 2 files changed, 6 deletions(-) diff --git a/include/beman/execution26/detail/as_awaitable.hpp b/include/beman/execution26/detail/as_awaitable.hpp index 0366b386..4247a60a 100644 --- a/include/beman/execution26/detail/as_awaitable.hpp +++ b/include/beman/execution26/detail/as_awaitable.hpp @@ -4,7 +4,6 @@ #ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_AS_AWAITABLE #define INCLUDED_BEMAN_EXECUTION26_DETAIL_AS_AWAITABLE -#include #include #include #include diff --git a/include/beman/execution26/detail/sender_awaitable.hpp b/include/beman/execution26/detail/sender_awaitable.hpp index 36660fa4..6a5cf9b0 100644 --- a/include/beman/execution26/detail/sender_awaitable.hpp +++ b/include/beman/execution26/detail/sender_awaitable.hpp @@ -4,18 +4,13 @@ #ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_SENDER_AWAITABLE #define INCLUDED_BEMAN_EXECUTION26_DETAIL_SENDER_AWAITABLE -#include #include #include #include -#include #include #include -#include #include -#include #include -#include #include #include From 42e952a45603a292d33b59e36c12c266d89415c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Sun, 1 Dec 2024 22:16:44 +0000 Subject: [PATCH 03/41] implemented all but the async interface --- include/beman/execution26/conqueue.hpp | 1 + .../execution26/detail/bounded_queue.hpp | 209 ++++++++++++++++++ src/beman/execution26/CMakeLists.txt | 2 + tests/beman/execution26/CMakeLists.txt | 3 + .../beman/execution26/bounded-queue.test.cpp | 169 ++++++++++++++ 5 files changed, 384 insertions(+) create mode 100644 include/beman/execution26/detail/bounded_queue.hpp create mode 100644 tests/beman/execution26/bounded-queue.test.cpp diff --git a/include/beman/execution26/conqueue.hpp b/include/beman/execution26/conqueue.hpp index cd03f436..1bda4cfc 100644 --- a/include/beman/execution26/conqueue.hpp +++ b/include/beman/execution26/conqueue.hpp @@ -11,6 +11,7 @@ #include #include #include +#include // ---------------------------------------------------------------------------- diff --git a/include/beman/execution26/detail/bounded_queue.hpp b/include/beman/execution26/detail/bounded_queue.hpp new file mode 100644 index 00000000..181aff8a --- /dev/null +++ b/include/beman/execution26/detail/bounded_queue.hpp @@ -0,0 +1,209 @@ +// include/beman/execution26/detail/bounded_queue.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_BOUNDED_QUEUE +#define INCLUDED_BEMAN_EXECUTION26_DETAIL_BOUNDED_QUEUE + +#include +#include +#include +#include +#include //-dk:TODO remove +#include +#include +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- + +namespace beman::execution26 { +template > +class bounded_queue; +} + +// ---------------------------------------------------------------------------- + +template +class beman::execution26::bounded_queue : ::beman::execution26::detail::immovable { + public: + using value_type = T; + using allocator_type = Allocator; + + static_assert(::std::same_as); + + explicit bounded_queue(::std::size_t max, Allocator allocator = {}) + : allocator(allocator), + array_allocator(allocator), + max(max), + elements(array_allocator_traits::allocate(this->array_allocator, this->max)) {} + ~bounded_queue() { + for (; this->tail != this->head; ++this->tail) { + this->destroy(this->get(this->tail)); + } + array_allocator_traits::deallocate(this->array_allocator, this->elements, this->max); + } + + auto is_closed() const noexcept -> bool { + std::lock_guard cerberos(this->mutex); + return this->closed; + } + auto close() noexcept -> void { + std::lock_guard cerberos(this->mutex); + this->closed = true; + this->push_condition.notify_all(); + this->pop_condition.notify_all(); + } + + auto push(const T& value) -> void { this->internal_push(value); } + auto push(T&& value) -> void { this->internal_push(::std::move(value)); } + auto push(const T& value, ::std::error_code& ec) -> bool { return this->internal_push(value, ec); } + auto push(T&& value, ::std::error_code& ec) -> bool { return this->internal_push(::std::move(value), ec); } + auto try_push(const T& value, ::std::error_code& ec) -> bool { return this->internal_try_push(value, ec); } + auto try_push(T&& value, ::std::error_code& ec) -> bool { return this->internal_try_push(::std::move(value), ec); } + auto async_push(const T& value) -> sender auto { return internal_async_push(value); } + auto async_push(T&& value) -> sender auto { return internal_async_push(::std::move(value)); } + + auto pop() -> T { + ::std::unique_lock cerberus(this->mutex); + this->pop_condition.wait(cerberus, [this] { return this->closed || this->head != this->tail; }); + if (this->closed) { + throw ::beman::execution26::conqueue_error(::beman::execution26::conqueue_errc::closed); + } + element_t* element{this->get(this->tail)}; + T rc{::std::move(element->value)}; + this->destroy(element); + ++this->tail; + cerberus.unlock(); + this->push_condition.notify_one(); + return rc; + } + auto pop(::std::error_code& ec) -> ::std::optional { + ::std::unique_lock cerberus(this->mutex); + this->pop_condition.wait(cerberus, [this] { return this->closed || this->head != this->tail; }); + if (this->closed) { + ec = make_error_code(::beman::execution26::conqueue_errc::closed); + return {}; + } + element_t* element{this->get(this->tail)}; + ::std::optional rc{::std::move(element->value)}; + this->destroy(element); + ++this->tail; + cerberus.unlock(); + this->push_condition.notify_one(); + return rc; + } + auto try_pop(::std::error_code& ec) -> ::std::optional { + ::std::unique_lock cerberus(this->mutex); + if (this->closed) { + ec = make_error_code(::beman::execution26::conqueue_errc::closed); + return {}; + } + if (this->head == this->tail) { + ec = make_error_code(::beman::execution26::conqueue_errc::empty); + return {}; + } + element_t* element{this->get(this->tail)}; + ::std::optional rc{::std::move(element->value)}; + this->destroy(element); + ++this->tail; + cerberus.unlock(); + this->push_condition.notify_one(); + return rc; + } + auto async_pop() -> sender auto { return ::beman::execution26::just(T()); } + + private: + using allocator_traits = ::std::allocator_traits; + union element_t { + element_t() {} + template + element_t(allocator_type& alloc, Args&&... args) { + allocator_traits::construct(alloc, &value, ::std::forward(args)...); + } + ~element_t() {} + T value; + }; + using array_allocator_type = allocator_traits::template rebind_alloc; + using array_allocator_traits = allocator_traits::template rebind_traits; + + template + auto construct(element_t* element, Args&&... args) { + array_allocator_traits::construct( + this->array_allocator, this->get(this->head), this->allocator, ::std::forward(args)...); + } + auto destroy(element_t* element) { + allocator_traits::destroy(this->allocator, &element->value); + array_allocator_traits::destroy(this->array_allocator, element); + } + auto get(::std::uint64_t index) noexcept -> element_t* { return this->elements + (index % this->max); } + + template + auto internal_push(Arg&& arg) -> void { + ::beman::execution26::conqueue_errc error{}; + if (not this->internal_push(::std::forward(arg), error)) { + throw ::beman::execution26::conqueue_error(error); + } + } + template + auto internal_push(Arg&& arg, ::std::error_code& ec) -> bool { + ::beman::execution26::conqueue_errc error{}; + bool rc{this->internal_push(::std::forward(arg), error)}; + if (not rc) { + ec = make_error_code(error); + } + return rc; + } + template + auto internal_push(Arg&& arg, ::beman::execution26::conqueue_errc& error) -> bool { + { + ::std::unique_lock cerberus(this->mutex); + this->push_condition.wait(cerberus, + [this] { return this->closed || this->head - this->tail < this->max; }); + if (this->closed) { + error = ::beman::execution26::conqueue_errc::closed; + return false; + } + this->construct(this->get(this->head), ::std::forward(arg)); + ++this->head; + } + this->pop_condition.notify_one(); + return true; + } + template + auto internal_try_push(Arg&& arg, ::std::error_code& ec) -> bool { + { + ::std::unique_lock cerberus(this->mutex); + if (this->closed) { + ec = make_error_code(::beman::execution26::conqueue_errc::closed); + return false; + } + if (this->head - this->tail == this->max) { + ec = make_error_code(::beman::execution26::conqueue_errc::full); + return false; + } + this->construct(this->get(this->head), ::std::forward(arg)); + ++this->head; + } + this->pop_condition.notify_one(); + return true; + } + auto internal_async_push(T&& value) -> sender auto { return ::beman::execution26::just(); } + + Allocator allocator; + array_allocator_type array_allocator; + mutable ::std::mutex mutex; + ::std::condition_variable push_condition; + ::std::condition_variable pop_condition; + ::std::size_t max; + element_t* elements; + ::std::uint64_t head{}; // the next element to push to + ::std::uint64_t tail{}; // the next element to push from + bool closed{}; +}; + +// ---------------------------------------------------------------------------- + +#endif diff --git a/src/beman/execution26/CMakeLists.txt b/src/beman/execution26/CMakeLists.txt index 78f48846..d06d2a34 100644 --- a/src/beman/execution26/CMakeLists.txt +++ b/src/beman/execution26/CMakeLists.txt @@ -50,6 +50,7 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/basic_receiver.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/basic_sender.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/basic_state.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/bounded_queue.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/call_result_t.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/callable.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/check_type_alias_exist.hpp @@ -69,6 +70,7 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/connect_awaitable.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/connect_result_t.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/conqueue_errc.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/conqueue_error.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/continues_on.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/decayed_same_as.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/decayed_tuple.hpp diff --git a/tests/beman/execution26/CMakeLists.txt b/tests/beman/execution26/CMakeLists.txt index d9285a23..91a6875e 100644 --- a/tests/beman/execution26/CMakeLists.txt +++ b/tests/beman/execution26/CMakeLists.txt @@ -11,6 +11,7 @@ endif() list( APPEND execution_tests + bounded-queue.test conqueue-error.test conqueue-errc.test async-concurrent-queue.test @@ -133,6 +134,7 @@ foreach(test ${execution_tests}) add_test(NAME ${TEST_EXE} COMMAND $) endforeach() +if(False) if(NOT PROJECT_IS_TOP_LEVEL) # test if the targets are findable from the build directory # cmake-format: off @@ -155,3 +157,4 @@ if(NOT PROJECT_IS_TOP_LEVEL) ) # cmake-format: on endif() +endif() \ No newline at end of file diff --git a/tests/beman/execution26/bounded-queue.test.cpp b/tests/beman/execution26/bounded-queue.test.cpp new file mode 100644 index 00000000..48b9f6fd --- /dev/null +++ b/tests/beman/execution26/bounded-queue.test.cpp @@ -0,0 +1,169 @@ +// tests/beman/execution26/bounded-queue.test.cpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- + +namespace { +template +auto test_close(auto one, auto two) { + test_std::bounded_queue queue(1); + ASSERT(noexcept(const_cast&>(queue).is_closed())); + ASSERT(not queue.is_closed()); + std::error_code ec{}; + ASSERT(queue.push(one, ec)); + auto val(queue.pop(ec)); + ASSERT(val.has_value()); + ASSERT(*val == one); + + ASSERT(noexcept(queue.close())); + queue.close(); + ASSERT(queue.is_closed()); + + ASSERT(&ec.category() != &test_std::conqueue_category()); + ASSERT(not queue.push(two, ec)); + ASSERT(ec.value() == static_cast(test_std::conqueue_errc::closed)); + ASSERT(&ec.category() == &test_std::conqueue_category()); + + ec = std::error_code(); + ASSERT(&ec.category() != &test_std::conqueue_category()); + auto noval(queue.pop(ec)); + ASSERT(not noval.has_value()); + ASSERT(ec.value() == static_cast(test_std::conqueue_errc::closed)); + ASSERT(&ec.category() == &test_std::conqueue_category()); +} + +template +auto test_push(const auto one, auto two, const auto three, auto four, auto five) -> void { + test_std::bounded_queue queue(4); + queue.push(one); + queue.push(std::move(two)); + std::error_code code{}; + bool rc1{queue.push(three, code)}; + ASSERT(rc1 == true); + bool rc2{queue.push(std::move(four), code)}; + ASSERT(rc2 == true); + + queue.close(); + try { + queue.push(five); + ASSERT(false); + } catch (const test_std::conqueue_error& error) { + ASSERT(error.code().value() == static_cast(test_std::conqueue_errc::closed)); + } + try { + queue.push(std::move(five)); + ASSERT(false); + } catch (const test_std::conqueue_error& error) { + ASSERT(error.code().value() == static_cast(test_std::conqueue_errc::closed)); + } + std::error_code ec0{}; + ASSERT(queue.push(five, ec0) == false); + ASSERT(ec0.value() == static_cast(test_std::conqueue_errc::closed)); + + std::error_code ec1{}; + ASSERT(queue.push(std::move(five), ec1) == false); + ASSERT(ec1.value() == static_cast(test_std::conqueue_errc::closed)); +} + +template +auto test_pop(const auto one, auto two, const auto three, auto four, auto five) -> void { + test_std::bounded_queue queue(4); + queue.push(one); + queue.push(two); + queue.push(three); + queue.push(four); + + ASSERT(one == queue.pop()); + ASSERT(two == queue.pop()); + std::error_code ec; + auto val1(queue.pop(ec)); + ASSERT(val1.has_value()); + ASSERT(*val1 == three); + auto val2(queue.try_pop(ec)); + ASSERT(val2.has_value()); + ASSERT(*val2 == four); + + auto val3(queue.try_pop(ec)); + ASSERT(not val3.has_value()); + ASSERT(&ec.category() == &test_std::conqueue_category()); + ASSERT(ec.value() == static_cast(test_std::conqueue_errc::empty)); + + { + std::thread t([&] { + using namespace std::chrono_literals; + std::this_thread::sleep_for(10ms); + queue.push(five); + }); + + auto val4(queue.pop()); + ASSERT(val4 == five); + t.join(); + } + { + std::thread t([&] { + using namespace std::chrono_literals; + std::this_thread::sleep_for(10ms); + queue.push(five); + }); + + std::error_code ec{}; + auto val4(queue.pop(ec)); + ASSERT(val4.has_value()); + ASSERT(*val4 == five); + t.join(); + } + { + std::thread t([&] { + using namespace std::chrono_literals; + std::this_thread::sleep_for(10ms); + queue.close(); + }); + + try { + queue.pop(); + ASSERT(false); + } catch (const test_std::conqueue_error& ex) { + ASSERT(&ex.code().category() == &test_std::conqueue_category()); + ASSERT(ex.code().value() == static_cast(test_std::conqueue_errc::closed)); + } + t.join(); + } + { + std::thread t([&] { + using namespace std::chrono_literals; + std::this_thread::sleep_for(10ms); + queue.close(); + }); + + std::error_code ec{}; + auto val4(queue.pop(ec)); + ASSERT(not val4.has_value()); + ASSERT(ec.value() == static_cast(test_std::conqueue_errc::closed)); + t.join(); + } +} +} // namespace + +TEST(bounded_queue) { + using namespace std::string_literals; + + static_assert(not std::movable>); + static_assert(not std::copyable>); + static_assert(std::same_as::value_type>); + static_assert(std::same_as, test_std::bounded_queue::allocator_type>); + + test_close(1, 2); + test_close("one"s, "two"s); + + test_push(1, 2, 3, 4, 5); + test_push("one"s, "two"s, "three"s, "four"s, "five"s); + + test_pop(1, 2, 3, 4, 5); + test_pop("one"s, "two"s, "three"s, "four"s, "five"s); +} \ No newline at end of file From 7fa110f08e39908f871d75c4b69c481d186fefb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Mon, 2 Dec 2024 00:17:40 +0000 Subject: [PATCH 04/41] added async_push functionality --- .../execution26/detail/bounded_queue.hpp | 142 +++++++++++++----- .../execution26/detail/intrusive_queue.hpp | 34 +++++ .../execution26/detail/intrusive_stack.hpp | 14 +- src/beman/execution26/CMakeLists.txt | 1 + tests/beman/execution26/CMakeLists.txt | 1 + .../beman/execution26/bounded-queue.test.cpp | 54 +++++++ .../execution26/intrusive-queue.test.cpp | 36 +++++ 7 files changed, 241 insertions(+), 41 deletions(-) create mode 100644 include/beman/execution26/detail/intrusive_queue.hpp create mode 100644 tests/beman/execution26/intrusive-queue.test.cpp diff --git a/include/beman/execution26/detail/bounded_queue.hpp b/include/beman/execution26/detail/bounded_queue.hpp index 181aff8a..92de8653 100644 --- a/include/beman/execution26/detail/bounded_queue.hpp +++ b/include/beman/execution26/detail/bounded_queue.hpp @@ -5,15 +5,16 @@ #define INCLUDED_BEMAN_EXECUTION26_DETAIL_BOUNDED_QUEUE #include +#include #include #include -#include -#include //-dk:TODO remove +#include #include #include #include #include #include +#include #include #include @@ -32,6 +33,55 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl using value_type = T; using allocator_type = Allocator; + struct push_sender { + struct state_base { + ::std::remove_cvref_t value; + bounded_queue& queue; + state_base* next{}; + + template + state_base(V&& value, bounded_queue& queue) : value(::std::forward(value)), queue(queue) {} + virtual auto complete() -> void = 0; + virtual auto complete(::beman::execution26::conqueue_errc) -> void = 0; + }; + template + struct state : state_base { + using operation_state_concept = ::beman::execution26::operation_state_t; + + ::std::remove_cvref_t receiver; + + template + state(bounded_queue& queue, T&& value, R&& receiver) + : state_base(::std::move(value), queue), receiver(::std::forward(receiver)) {} + + auto start() & noexcept { + if (this->queue.start_push(*this)) + this->complete(); + } + auto complete() -> void override { ::beman::execution26::set_value(::std::move(this->receiver)); } + auto complete(::beman::execution26::conqueue_errc error) -> void override { + ::beman::execution26::set_error(::std::move(this->receiver), error); + } + }; + + using sender_concept = ::beman::execution26::sender_t; + using completion_signatures = + ::beman::execution26::completion_signatures<::beman::execution26::set_value_t(), + ::beman::execution26::set_error_t( + ::beman::execution26::conqueue_errc), + ::beman::execution26::set_stopped_t()>; + + bounded_queue& queue; + ::std::remove_cvref_t value; + template <::beman::execution26::receiver Receiver> + auto connect(Receiver&& receiver) && -> state { + static_assert(::beman::execution26::operation_state>); + return state(queue, ::std::move(value), ::std::forward(receiver)); + } + }; + static_assert(::beman::execution26::sender); + static_assert(::beman::execution26::sender_in); + static_assert(::std::same_as); explicit bounded_queue(::std::size_t max, Allocator allocator = {}) @@ -51,8 +101,11 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl return this->closed; } auto close() noexcept -> void { - std::lock_guard cerberos(this->mutex); + std::lock_guard cerberus(this->mutex); this->closed = true; + while (not this->push_queue.empty()) { + this->push_queue.pop()->complete(::beman::execution26::conqueue_errc::closed); + } this->push_condition.notify_all(); this->pop_condition.notify_all(); } @@ -76,8 +129,7 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl T rc{::std::move(element->value)}; this->destroy(element); ++this->tail; - cerberus.unlock(); - this->push_condition.notify_one(); + this->push_notify(cerberus); return rc; } auto pop(::std::error_code& ec) -> ::std::optional { @@ -91,8 +143,7 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl ::std::optional rc{::std::move(element->value)}; this->destroy(element); ++this->tail; - cerberus.unlock(); - this->push_condition.notify_one(); + this->push_notify(cerberus); return rc; } auto try_pop(::std::error_code& ec) -> ::std::optional { @@ -109,8 +160,7 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl ::std::optional rc{::std::move(element->value)}; this->destroy(element); ++this->tail; - cerberus.unlock(); - this->push_condition.notify_one(); + this->push_notify(cerberus); return rc; } auto async_pop() -> sender auto { return ::beman::execution26::just(T()); } @@ -128,6 +178,7 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl }; using array_allocator_type = allocator_traits::template rebind_alloc; using array_allocator_traits = allocator_traits::template rebind_traits; + using push_sender_queue = ::beman::execution26::detail::intrusive_queue<&push_sender::state_base::next>; template auto construct(element_t* element, Args&&... args) { @@ -158,39 +209,61 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl } template auto internal_push(Arg&& arg, ::beman::execution26::conqueue_errc& error) -> bool { - { - ::std::unique_lock cerberus(this->mutex); - this->push_condition.wait(cerberus, - [this] { return this->closed || this->head - this->tail < this->max; }); - if (this->closed) { - error = ::beman::execution26::conqueue_errc::closed; - return false; - } - this->construct(this->get(this->head), ::std::forward(arg)); - ++this->head; + ::std::unique_lock cerberus(this->mutex); + this->push_condition.wait(cerberus, [this] { return this->closed || this->head - this->tail < this->max; }); + if (this->closed) { + error = ::beman::execution26::conqueue_errc::closed; + return false; } - this->pop_condition.notify_one(); + this->construct(this->get(this->head), ::std::forward(arg)); + ++this->head; + this->pop_notify(cerberus); return true; } template auto internal_try_push(Arg&& arg, ::std::error_code& ec) -> bool { - { - ::std::unique_lock cerberus(this->mutex); - if (this->closed) { - ec = make_error_code(::beman::execution26::conqueue_errc::closed); - return false; - } - if (this->head - this->tail == this->max) { - ec = make_error_code(::beman::execution26::conqueue_errc::full); - return false; - } - this->construct(this->get(this->head), ::std::forward(arg)); - ++this->head; + ::std::unique_lock cerberus(this->mutex); + if (this->closed) { + ec = make_error_code(::beman::execution26::conqueue_errc::closed); + return false; } - this->pop_condition.notify_one(); + if (this->head - this->tail == this->max) { + ec = make_error_code(::beman::execution26::conqueue_errc::full); + return false; + } + this->construct(this->get(this->head), ::std::forward(arg)); + ++this->head; + this->pop_notify(cerberus); return true; } - auto internal_async_push(T&& value) -> sender auto { return ::beman::execution26::just(); } + auto pop_notify(auto& cerberus) { + // if (not this->pop_queue.empty()) + // this->pop_queue.pop()->complete(); + // else + this->pop_condition.notify_one(); + } + auto push_notify(auto& cerberus) { + if (not this->push_queue.empty()) + this->push_queue.pop()->complete(); + else + this->push_condition.notify_one(); + } + template + auto internal_async_push(TT&& value) -> sender auto { + return push_sender{*this, ::std::forward(value)}; + } + auto start_push(push_sender::state_base& s) -> bool { + std::unique_lock cerberus(this->mutex); + if (this->head - this->tail < this->max) { + this->construct(this->get(this->head), ::std::move(s.value)); + ++this->head; + this->pop_notify(cerberus); + return true; + } else { + this->push_queue.push(&s); + return false; + } + } Allocator allocator; array_allocator_type array_allocator; @@ -201,6 +274,7 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl element_t* elements; ::std::uint64_t head{}; // the next element to push to ::std::uint64_t tail{}; // the next element to push from + push_sender_queue push_queue; bool closed{}; }; diff --git a/include/beman/execution26/detail/intrusive_queue.hpp b/include/beman/execution26/detail/intrusive_queue.hpp new file mode 100644 index 00000000..11ff226e --- /dev/null +++ b/include/beman/execution26/detail/intrusive_queue.hpp @@ -0,0 +1,34 @@ +// include/beman/execution26/detail/intrusive_queue.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_INTRUSIVE_QUEUE +#define INCLUDED_BEMAN_EXECUTION26_DETAIL_INTRUSIVE_QUEUE + +#include + +// ---------------------------------------------------------------------------- + +namespace beman::execution26::detail { +template +struct intrusive_queue; + +template +struct intrusive_queue { + T* head{}; + T* tail{}; + + auto push(T* n) -> void { + if (this->head) { + std::exchange(this->tail, n)->*next = n; + } else { + this->head = this->tail = n; + } + } + auto pop() -> T* { return std::exchange(this->head, this->head->*next); } + auto empty() const -> bool { return this->head == nullptr; } +}; +} // namespace beman::execution26::detail + +// ---------------------------------------------------------------------------- + +#endif diff --git a/include/beman/execution26/detail/intrusive_stack.hpp b/include/beman/execution26/detail/intrusive_stack.hpp index 4ee89ffb..bd6c108c 100644 --- a/include/beman/execution26/detail/intrusive_stack.hpp +++ b/include/beman/execution26/detail/intrusive_stack.hpp @@ -1,4 +1,4 @@ -// include/beman/execution26/detail/intrusive_queue.hpp -*-C++-*- +// include/beman/execution26/detail/intrusive_stack.hpp -*-C++-*- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_INTRUSIVE_QUEUE @@ -15,16 +15,16 @@ class atomic_intrusive_stack; template class intrusive_stack; -//! @brief This data structure is an intrusive queue that is not thread-safe. +//! @brief This data structure is an intrusive stack that is not thread-safe. template class intrusive_stack { public: - //! @brief Pushes an item to the queue. + //! @brief Pushes an item to the stack. auto push(Item* item) noexcept -> void { item->*Next = std::exchange(head_, item); } - //! @brief Pops one item from the queue. + //! @brief Pops one item from the stack. //! - //! @return The item that was popped from the queue, or nullptr if the queue is empty. + //! @return The item that was popped from the stack, or nullptr if the stack is empty. auto pop() noexcept -> Item* { if (head_) { auto item = head_; @@ -34,7 +34,7 @@ class intrusive_stack { return nullptr; } - //! @brief Tests if the queue is empty. + //! @brief Tests if the stack is empty. auto empty() const noexcept -> bool { return !head_; } private: @@ -44,4 +44,4 @@ class intrusive_stack { } // namespace beman::execution26::detail -#endif \ No newline at end of file +#endif diff --git a/src/beman/execution26/CMakeLists.txt b/src/beman/execution26/CMakeLists.txt index d06d2a34..3c3c1721 100644 --- a/src/beman/execution26/CMakeLists.txt +++ b/src/beman/execution26/CMakeLists.txt @@ -108,6 +108,7 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/indirect_meta_apply.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/inplace_stop_source.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/into_variant.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/intrusive_queue.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/intrusive_stack.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/is_awaitable.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/is_awaiter.hpp diff --git a/tests/beman/execution26/CMakeLists.txt b/tests/beman/execution26/CMakeLists.txt index 91a6875e..3b58bcf7 100644 --- a/tests/beman/execution26/CMakeLists.txt +++ b/tests/beman/execution26/CMakeLists.txt @@ -11,6 +11,7 @@ endif() list( APPEND execution_tests + intrusive-queue.test bounded-queue.test conqueue-error.test conqueue-errc.test diff --git a/tests/beman/execution26/bounded-queue.test.cpp b/tests/beman/execution26/bounded-queue.test.cpp index 48b9f6fd..a122b193 100644 --- a/tests/beman/execution26/bounded-queue.test.cpp +++ b/tests/beman/execution26/bounded-queue.test.cpp @@ -148,6 +148,57 @@ auto test_pop(const auto one, auto two, const auto three, auto four, auto five) t.join(); } } + +template +auto test_async_push(auto one, auto two, auto three, auto four, auto five) -> void { + struct receiver { + using receiver_concept = test_std::receiver_t; + int& complete; + auto set_value() && noexcept -> void { this->complete = 1; } + auto set_error(test_std::conqueue_errc) && noexcept -> void { this->complete = 2; } + auto set_stopped() && noexcept -> void { this->complete = 3; } + }; + static_assert(test_std::receiver); + + test_std::bounded_queue queue(2); + auto s4{queue.async_push(four)}; // verify that the order isn't call but start() + auto s1{queue.async_push(one)}; + auto s2{queue.async_push(two)}; + auto s3{queue.async_push(three)}; + auto s5{queue.async_push(five)}; + + int c1{}, c2{}, c3{}, c4{}, c5{}; + + auto op2{test_std::connect(std::move(s2), receiver{c2})}; // connect order also doesn't matter + ASSERT(c2 == 0); + auto op1{test_std::connect(std::move(s1), receiver{c1})}; + ASSERT(c1 == 0); + auto op3{test_std::connect(std::move(s3), receiver{c3})}; + ASSERT(c3 == 0); + auto op4{test_std::connect(std::move(s4), receiver{c4})}; + ASSERT(c4 == 0); + auto op5{test_std::connect(std::move(s5), receiver{c5})}; + ASSERT(c5 == 0); + + test_std::start(op1); + ASSERT(c1 == 1); + test_std::start(op2); + ASSERT(c2 == 1); + test_std::start(op3); + ASSERT(c3 == 0); + test_std::start(op4); + ASSERT(c4 == 0); + test_std::start(op5); + ASSERT(c5 == 0); + + ASSERT(queue.pop() == one); + ASSERT(c3 == 1); + ASSERT(queue.pop() == two); + ASSERT(c4 == 1); + queue.close(); + ASSERT(c5 == 2); +} + } // namespace TEST(bounded_queue) { @@ -166,4 +217,7 @@ TEST(bounded_queue) { test_pop(1, 2, 3, 4, 5); test_pop("one"s, "two"s, "three"s, "four"s, "five"s); + + test_async_push(1, 2, 3, 4, 5); + test_async_push("one"s, "two"s, "three"s, "four"s, "five"s); } \ No newline at end of file diff --git a/tests/beman/execution26/intrusive-queue.test.cpp b/tests/beman/execution26/intrusive-queue.test.cpp new file mode 100644 index 00000000..d109dec8 --- /dev/null +++ b/tests/beman/execution26/intrusive-queue.test.cpp @@ -0,0 +1,36 @@ +// tests/beman/execution26/intrusive-queue.test.cpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include + +// ---------------------------------------------------------------------------- + +namespace { +struct node { + int value; + node* n_{}; +}; +} // namespace + +TEST(intrusive_queue) { + test_detail::intrusive_queue<&node::n_> queue; + node n[]{{1}, {2}, {3}, {4}, {5}}; + ASSERT(queue.empty()); + + queue.push(n + 0); + ASSERT(not queue.empty()); + node* n0{queue.pop()}; + ASSERT(n0 == n + 0); + ASSERT(n0->n_ == nullptr); + ASSERT(queue.empty()); + + for (int i = 0; i != 5; ++i) { + queue.push(n + i); + } + for (int i = 0; i != 5; ++i) { + ASSERT(not queue.empty()); + ASSERT(queue.pop() == n + i); + } + ASSERT(queue.empty()); +} From 3bc65535c2259a8ba17a8cfbf93cd859f432c50e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Mon, 2 Dec 2024 08:17:01 +0000 Subject: [PATCH 05/41] added async_pop --- .../execution26/detail/bounded_queue.hpp | 108 ++++++++++++++++-- .../execution26/detail/intrusive_queue.hpp | 6 + .../beman/execution26/bounded-queue.test.cpp | 76 ++++++++++++ 3 files changed, 178 insertions(+), 12 deletions(-) diff --git a/include/beman/execution26/detail/bounded_queue.hpp b/include/beman/execution26/detail/bounded_queue.hpp index 92de8653..f23fb158 100644 --- a/include/beman/execution26/detail/bounded_queue.hpp +++ b/include/beman/execution26/detail/bounded_queue.hpp @@ -67,8 +67,7 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl using sender_concept = ::beman::execution26::sender_t; using completion_signatures = ::beman::execution26::completion_signatures<::beman::execution26::set_value_t(), - ::beman::execution26::set_error_t( - ::beman::execution26::conqueue_errc), + ::beman::execution26::set_error_t(::beman::execution26::conqueue_errc), ::beman::execution26::set_stopped_t()>; bounded_queue& queue; @@ -82,6 +81,50 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl static_assert(::beman::execution26::sender); static_assert(::beman::execution26::sender_in); + struct pop_sender { + struct state_base { + bounded_queue& queue; + state_base* next{}; + + state_base(bounded_queue& queue) : queue(queue) {} + virtual auto complete(T) -> void = 0; + virtual auto complete(::beman::execution26::conqueue_errc) -> void = 0; + }; + template + struct state : state_base { + using operation_state_concept = ::beman::execution26::operation_state_t; + + ::std::remove_cvref_t receiver; + + template + state(bounded_queue& queue, R&& receiver) + : state_base(queue), receiver(::std::forward(receiver)) {} + + auto start() & noexcept { + this->queue.start_pop(*this); + } + auto complete(T val) -> void override { ::beman::execution26::set_value(::std::move(this->receiver), ::std::move(val)); } + auto complete(::beman::execution26::conqueue_errc error) -> void override { + ::beman::execution26::set_error(::std::move(this->receiver), error); + } + }; + + using sender_concept = ::beman::execution26::sender_t; + using completion_signatures = + ::beman::execution26::completion_signatures<::beman::execution26::set_value_t(T), + ::beman::execution26::set_error_t(::beman::execution26::conqueue_errc), + ::beman::execution26::set_stopped_t()>; + + bounded_queue& queue; + template <::beman::execution26::receiver Receiver> + auto connect(Receiver&& receiver) && -> state { + static_assert(::beman::execution26::operation_state>); + return state(queue, ::std::forward(receiver)); + } + }; + static_assert(::beman::execution26::sender); + static_assert(::beman::execution26::sender_in); + static_assert(::std::same_as); explicit bounded_queue(::std::size_t max, Allocator allocator = {}) @@ -101,13 +144,20 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl return this->closed; } auto close() noexcept -> void { - std::lock_guard cerberus(this->mutex); + std::unique_lock cerberus(this->mutex); this->closed = true; - while (not this->push_queue.empty()) { - this->push_queue.pop()->complete(::beman::execution26::conqueue_errc::closed); - } + push_sender_queue push_queue(std::move(this->push_queue)); + pop_sender_queue pop_queue(std::move(this->pop_queue)); + cerberus.unlock(); this->push_condition.notify_all(); this->pop_condition.notify_all(); + + while (not push_queue.empty()) { + push_queue.pop()->complete(::beman::execution26::conqueue_errc::closed); + } + while (not pop_queue.empty()) { + pop_queue.pop()->complete(::beman::execution26::conqueue_errc::closed); + } } auto push(const T& value) -> void { this->internal_push(value); } @@ -163,7 +213,7 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl this->push_notify(cerberus); return rc; } - auto async_pop() -> sender auto { return ::beman::execution26::just(T()); } + auto async_pop() -> sender auto { return pop_sender(*this); } private: using allocator_traits = ::std::allocator_traits; @@ -179,6 +229,7 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl using array_allocator_type = allocator_traits::template rebind_alloc; using array_allocator_traits = allocator_traits::template rebind_traits; using push_sender_queue = ::beman::execution26::detail::intrusive_queue<&push_sender::state_base::next>; + using pop_sender_queue = ::beman::execution26::detail::intrusive_queue<&pop_sender::state_base::next>; template auto construct(element_t* element, Args&&... args) { @@ -237,16 +288,33 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl return true; } auto pop_notify(auto& cerberus) { - // if (not this->pop_queue.empty()) - // this->pop_queue.pop()->complete(); - // else - this->pop_condition.notify_one(); + if (not this->pop_queue.empty()) + { + element_t* element{this->get(this->tail)}; + ::std::remove_cvref_t val{std::move(element->value)}; + this->destroy(element); + ++this->tail; + auto state{this->pop_queue.pop()}; + this->push_notify(cerberus); + state->complete(std::move(val)); + } + else + { + cerberus.unlock(); + this->pop_condition.notify_one(); + } } auto push_notify(auto& cerberus) { if (not this->push_queue.empty()) + { + cerberus.unlock(); this->push_queue.pop()->complete(); + } else + { + cerberus.unlock(); this->push_condition.notify_one(); + } } template auto internal_async_push(TT&& value) -> sender auto { @@ -264,17 +332,33 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl return false; } } + auto start_pop(pop_sender::state_base& s) -> bool { + std::unique_lock cerberus(this->mutex); + if (this->head != this->tail) { + element_t* element(this->get(tail)); + std::remove_cvref_t val(std::move(element->value)); + ++this->tail; + this->destroy(element); + this->push_notify(cerberus); + s.complete(std::move(val)); + return true; + } else { + this->pop_queue.push(&s); + return false; + } + } Allocator allocator; array_allocator_type array_allocator; mutable ::std::mutex mutex; + push_sender_queue push_queue; ::std::condition_variable push_condition; + pop_sender_queue pop_queue; ::std::condition_variable pop_condition; ::std::size_t max; element_t* elements; ::std::uint64_t head{}; // the next element to push to ::std::uint64_t tail{}; // the next element to push from - push_sender_queue push_queue; bool closed{}; }; diff --git a/include/beman/execution26/detail/intrusive_queue.hpp b/include/beman/execution26/detail/intrusive_queue.hpp index 11ff226e..b7839840 100644 --- a/include/beman/execution26/detail/intrusive_queue.hpp +++ b/include/beman/execution26/detail/intrusive_queue.hpp @@ -17,6 +17,12 @@ struct intrusive_queue { T* head{}; T* tail{}; + intrusive_queue() = default; + intrusive_queue(intrusive_queue&& other) + : head(::std::exchange(other.head, nullptr)) + , tail(::std::exchange(other.tail, nullptr)) + { + } auto push(T* n) -> void { if (this->head) { std::exchange(this->tail, n)->*next = n; diff --git a/tests/beman/execution26/bounded-queue.test.cpp b/tests/beman/execution26/bounded-queue.test.cpp index a122b193..5cfbc686 100644 --- a/tests/beman/execution26/bounded-queue.test.cpp +++ b/tests/beman/execution26/bounded-queue.test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include //-dk:TODO remove // ---------------------------------------------------------------------------- @@ -199,6 +200,79 @@ auto test_async_push(auto one, auto two, auto three, auto four, auto five) -> vo ASSERT(c5 == 2); } +template +auto test_async_pop(auto one, auto two, auto three, auto four, auto five) -> void { + struct receiver { + using receiver_concept = test_std::receiver_t; + int& complete; + std::vector& vals; + auto set_value(T val) && noexcept -> void { this->complete = 1; vals.push_back(val); } + auto set_error(test_std::conqueue_errc) && noexcept -> void { this->complete = 2; } + auto set_stopped() && noexcept -> void { this->complete = 3; } + }; + static_assert(test_std::receiver); + + test_std::bounded_queue queue(2); + std::vector vals; + queue.push(one); + queue.push(two); + + auto s4{queue.async_pop()}; + auto s2{queue.async_pop()}; + auto s1{queue.async_pop()}; + auto s3{queue.async_pop()}; + auto s5{queue.async_pop()}; + + int c1{}, c2{}, c3{}, c4{}, c5{}; + + auto op4{test_std::connect(std::move(s4), receiver{c4, vals})}; + ASSERT(c4 == 0); + auto op2{test_std::connect(std::move(s2), receiver{c2, vals})}; + ASSERT(c2 == 0); + auto op1{test_std::connect(std::move(s1), receiver{c1, vals})}; + ASSERT(c1 == 0); + auto op3{test_std::connect(std::move(s3), receiver{c3, vals})}; + ASSERT(c3 == 0); + auto op5{test_std::connect(std::move(s5), receiver{c5, vals})}; + ASSERT(c5 == 0); + + test_std::start(op1); + ASSERT(c1 == 1); + ASSERT(vals.size() == 1u); + ASSERT(vals.back() == one); + + test_std::start(op2); + ASSERT(c2 == 1); + ASSERT(vals.size() == 2u); + ASSERT(vals.back() == two); + + test_std::start(op3); + ASSERT(c3 == 0); + ASSERT(vals.size() == 2u); + + test_std::start(op4); + ASSERT(c4 == 0); + ASSERT(vals.size() == 2u); + + test_std::start(op5); + ASSERT(c5 == 0); + ASSERT(vals.size() == 2u); + + queue.push(three); + ASSERT(c3 == 1); + ASSERT(vals.size() == 3u); + ASSERT(vals.back() == three); + + queue.push(four); + ASSERT(c4 == 1); + ASSERT(vals.size() == 4u); + ASSERT(vals.back() == four); + + queue.close(); + ASSERT(c5 == 2); + ASSERT(vals.size() == 4u); +} + } // namespace TEST(bounded_queue) { @@ -220,4 +294,6 @@ TEST(bounded_queue) { test_async_push(1, 2, 3, 4, 5); test_async_push("one"s, "two"s, "three"s, "four"s, "five"s); + test_async_pop(1, 2, 3, 4, 5); + test_async_pop("one"s, "two"s, "three"s, "four"s, "five"s); } \ No newline at end of file From 1faeac7fce61e1eb7a134fb7dd7be96eb045fc34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Tue, 3 Dec 2024 01:36:04 +0000 Subject: [PATCH 06/41] refactored how the bounded_queue works --- .../execution26/detail/bounded_queue.hpp | 793 +++++++++++------- .../execution26/detail/intrusive_queue.hpp | 62 +- .../beman/execution26/bounded-queue.test.cpp | 11 +- 3 files changed, 563 insertions(+), 303 deletions(-) diff --git a/include/beman/execution26/detail/bounded_queue.hpp b/include/beman/execution26/detail/bounded_queue.hpp index f23fb158..01fb7224 100644 --- a/include/beman/execution26/detail/bounded_queue.hpp +++ b/include/beman/execution26/detail/bounded_queue.hpp @@ -10,213 +10,77 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include // ---------------------------------------------------------------------------- namespace beman::execution26 { +//! @brief This class templates provides a thread-safe, bounded queue template > class bounded_queue; -} +} // namespace beman::execution26 // ---------------------------------------------------------------------------- template class beman::execution26::bounded_queue : ::beman::execution26::detail::immovable { public: - using value_type = T; - using allocator_type = Allocator; - - struct push_sender { - struct state_base { - ::std::remove_cvref_t value; - bounded_queue& queue; - state_base* next{}; - - template - state_base(V&& value, bounded_queue& queue) : value(::std::forward(value)), queue(queue) {} - virtual auto complete() -> void = 0; - virtual auto complete(::beman::execution26::conqueue_errc) -> void = 0; - }; - template - struct state : state_base { - using operation_state_concept = ::beman::execution26::operation_state_t; - - ::std::remove_cvref_t receiver; - - template - state(bounded_queue& queue, T&& value, R&& receiver) - : state_base(::std::move(value), queue), receiver(::std::forward(receiver)) {} - - auto start() & noexcept { - if (this->queue.start_push(*this)) - this->complete(); - } - auto complete() -> void override { ::beman::execution26::set_value(::std::move(this->receiver)); } - auto complete(::beman::execution26::conqueue_errc error) -> void override { - ::beman::execution26::set_error(::std::move(this->receiver), error); - } - }; - - using sender_concept = ::beman::execution26::sender_t; - using completion_signatures = - ::beman::execution26::completion_signatures<::beman::execution26::set_value_t(), - ::beman::execution26::set_error_t(::beman::execution26::conqueue_errc), - ::beman::execution26::set_stopped_t()>; - - bounded_queue& queue; - ::std::remove_cvref_t value; - template <::beman::execution26::receiver Receiver> - auto connect(Receiver&& receiver) && -> state { - static_assert(::beman::execution26::operation_state>); - return state(queue, ::std::move(value), ::std::forward(receiver)); - } - }; - static_assert(::beman::execution26::sender); - static_assert(::beman::execution26::sender_in); - - struct pop_sender { - struct state_base { - bounded_queue& queue; - state_base* next{}; - - state_base(bounded_queue& queue) : queue(queue) {} - virtual auto complete(T) -> void = 0; - virtual auto complete(::beman::execution26::conqueue_errc) -> void = 0; - }; - template - struct state : state_base { - using operation_state_concept = ::beman::execution26::operation_state_t; - - ::std::remove_cvref_t receiver; - - template - state(bounded_queue& queue, R&& receiver) - : state_base(queue), receiver(::std::forward(receiver)) {} - - auto start() & noexcept { - this->queue.start_pop(*this); - } - auto complete(T val) -> void override { ::beman::execution26::set_value(::std::move(this->receiver), ::std::move(val)); } - auto complete(::beman::execution26::conqueue_errc error) -> void override { - ::beman::execution26::set_error(::std::move(this->receiver), error); - } - }; - - using sender_concept = ::beman::execution26::sender_t; - using completion_signatures = - ::beman::execution26::completion_signatures<::beman::execution26::set_value_t(T), - ::beman::execution26::set_error_t(::beman::execution26::conqueue_errc), - ::beman::execution26::set_stopped_t()>; - - bounded_queue& queue; - template <::beman::execution26::receiver Receiver> - auto connect(Receiver&& receiver) && -> state { - static_assert(::beman::execution26::operation_state>); - return state(queue, ::std::forward(receiver)); - } - }; - static_assert(::beman::execution26::sender); - static_assert(::beman::execution26::sender_in); + using value_type = ::std::remove_cvref_t; + using allocator_type = Allocator; + using allocator_traits = ::std::allocator_traits; + class push_sender; + class pop_sender; static_assert(::std::same_as); - explicit bounded_queue(::std::size_t max, Allocator allocator = {}) - : allocator(allocator), - array_allocator(allocator), - max(max), - elements(array_allocator_traits::allocate(this->array_allocator, this->max)) {} - ~bounded_queue() { - for (; this->tail != this->head; ++this->tail) { - this->destroy(this->get(this->tail)); - } - array_allocator_traits::deallocate(this->array_allocator, this->elements, this->max); - } + explicit bounded_queue(::std::size_t max, Allocator allocator = {}); + ~bounded_queue(); - auto is_closed() const noexcept -> bool { - std::lock_guard cerberos(this->mutex); - return this->closed; - } - auto close() noexcept -> void { - std::unique_lock cerberus(this->mutex); - this->closed = true; - push_sender_queue push_queue(std::move(this->push_queue)); - pop_sender_queue pop_queue(std::move(this->pop_queue)); - cerberus.unlock(); - this->push_condition.notify_all(); - this->pop_condition.notify_all(); + auto is_closed() const noexcept -> bool; + auto close() noexcept -> void; - while (not push_queue.empty()) { - push_queue.pop()->complete(::beman::execution26::conqueue_errc::closed); - } - while (not pop_queue.empty()) { - pop_queue.pop()->complete(::beman::execution26::conqueue_errc::closed); - } - } + auto push(const T& value) -> void; + auto push(T&& value) -> void; + auto push(const T& value, ::std::error_code& ec) -> bool; + auto push(T&& value, ::std::error_code& ec) -> bool; + auto try_push(const T& value, ::std::error_code& ec) -> bool; + auto try_push(T&& value, ::std::error_code& ec) -> bool; + auto async_push(const T& value) -> push_sender; + auto async_push(T&& value) -> push_sender; - auto push(const T& value) -> void { this->internal_push(value); } - auto push(T&& value) -> void { this->internal_push(::std::move(value)); } - auto push(const T& value, ::std::error_code& ec) -> bool { return this->internal_push(value, ec); } - auto push(T&& value, ::std::error_code& ec) -> bool { return this->internal_push(::std::move(value), ec); } - auto try_push(const T& value, ::std::error_code& ec) -> bool { return this->internal_try_push(value, ec); } - auto try_push(T&& value, ::std::error_code& ec) -> bool { return this->internal_try_push(::std::move(value), ec); } - auto async_push(const T& value) -> sender auto { return internal_async_push(value); } - auto async_push(T&& value) -> sender auto { return internal_async_push(::std::move(value)); } - - auto pop() -> T { - ::std::unique_lock cerberus(this->mutex); - this->pop_condition.wait(cerberus, [this] { return this->closed || this->head != this->tail; }); - if (this->closed) { - throw ::beman::execution26::conqueue_error(::beman::execution26::conqueue_errc::closed); - } - element_t* element{this->get(this->tail)}; - T rc{::std::move(element->value)}; - this->destroy(element); - ++this->tail; - this->push_notify(cerberus); - return rc; - } - auto pop(::std::error_code& ec) -> ::std::optional { - ::std::unique_lock cerberus(this->mutex); - this->pop_condition.wait(cerberus, [this] { return this->closed || this->head != this->tail; }); - if (this->closed) { - ec = make_error_code(::beman::execution26::conqueue_errc::closed); - return {}; - } - element_t* element{this->get(this->tail)}; - ::std::optional rc{::std::move(element->value)}; - this->destroy(element); - ++this->tail; - this->push_notify(cerberus); - return rc; - } - auto try_pop(::std::error_code& ec) -> ::std::optional { - ::std::unique_lock cerberus(this->mutex); - if (this->closed) { - ec = make_error_code(::beman::execution26::conqueue_errc::closed); - return {}; - } - if (this->head == this->tail) { - ec = make_error_code(::beman::execution26::conqueue_errc::empty); - return {}; - } - element_t* element{this->get(this->tail)}; - ::std::optional rc{::std::move(element->value)}; - this->destroy(element); - ++this->tail; - this->push_notify(cerberus); - return rc; - } - auto async_pop() -> sender auto { return pop_sender(*this); } + auto pop() -> value_type; + auto pop(::std::error_code& ec) -> ::std::optional; + auto try_pop(::std::error_code& ec) -> ::std::optional; + auto async_pop() -> pop_sender; private: - using allocator_traits = ::std::allocator_traits; + struct push_base { + value_type value; + push_base* next{}; + + template + push_base(V&& value) : value(::std::forward(value)) {} + virtual auto complete() -> void = 0; + virtual auto complete(::beman::execution26::conqueue_errc) -> void = 0; + virtual auto complete(::std::exception_ptr) -> void = 0; + }; + struct pop_base { + pop_base* next{}; + + pop_base() = default; + virtual auto complete(value_type) -> void = 0; + virtual auto complete(::beman::execution26::conqueue_errc) -> void = 0; + virtual auto complete(::std::exception_ptr) -> void = 0; + }; + union element_t { element_t() {} template @@ -224,144 +88,491 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl allocator_traits::construct(alloc, &value, ::std::forward(args)...); } ~element_t() {} - T value; + value_type value; }; + using array_allocator_type = allocator_traits::template rebind_alloc; using array_allocator_traits = allocator_traits::template rebind_traits; - using push_sender_queue = ::beman::execution26::detail::intrusive_queue<&push_sender::state_base::next>; - using pop_sender_queue = ::beman::execution26::detail::intrusive_queue<&pop_sender::state_base::next>; + using push_sender_queue = ::beman::execution26::detail::intrusive_queue<&push_base::next>; + using pop_sender_queue = ::beman::execution26::detail::intrusive_queue<&pop_base::next>; template - auto construct(element_t* element, Args&&... args) { - array_allocator_traits::construct( - this->array_allocator, this->get(this->head), this->allocator, ::std::forward(args)...); - } - auto destroy(element_t* element) { - allocator_traits::destroy(this->allocator, &element->value); - array_allocator_traits::destroy(this->array_allocator, element); - } - auto get(::std::uint64_t index) noexcept -> element_t* { return this->elements + (index % this->max); } + auto construct(element_t* element, Args&&... args) -> void; + auto destroy(element_t* element) -> void; + auto get(::std::uint64_t index) noexcept -> element_t*; + template + auto has_space(Lock&) noexcept -> bool; + template + auto push_value(Lock&, V&&) -> void; + template + auto pop_value(Lock&) -> value_type; template - auto internal_push(Arg&& arg) -> void { - ::beman::execution26::conqueue_errc error{}; - if (not this->internal_push(::std::forward(arg), error)) { - throw ::beman::execution26::conqueue_error(error); - } - } + auto internal_push(Arg&& arg, ::beman::execution26::conqueue_errc& error) -> bool; template - auto internal_push(Arg&& arg, ::std::error_code& ec) -> bool { - ::beman::execution26::conqueue_errc error{}; - bool rc{this->internal_push(::std::forward(arg), error)}; - if (not rc) { - ec = make_error_code(error); + auto internal_try_push(Arg&& arg, ::std::error_code& ec) -> bool; + template + auto internal_pop(HandleResult); + + auto start_push(push_base& s) -> void; + auto start_pop(pop_base& s) -> void; + + auto pop_notify(auto& cerberus) -> void; + auto push_notify(auto& cerberus) -> void; + + allocator_type allocator; + array_allocator_type array_allocator; + mutable ::std::mutex mutex; + push_sender_queue push_queue; + pop_sender_queue pop_queue; + ::std::size_t max; + element_t* elements; + ::std::uint64_t head{}; // the next element to push to + ::std::uint64_t tail{}; // the next element to push from + bool closed{}; +}; + +template +class beman::execution26::bounded_queue::push_sender { + public: + template + struct state : push_base { + using operation_state_concept = ::beman::execution26::operation_state_t; + + bounded_queue& queue; + ::std::remove_cvref_t receiver; + + template + state(bounded_queue& queue, T&& value, R&& receiver) + : push_base(::std::move(value)), queue(queue), receiver(::std::forward(receiver)) {} + + auto start() & noexcept { this->queue.start_push(*this); } + auto complete() -> void override { ::beman::execution26::set_value(::std::move(this->receiver)); } + auto complete(::beman::execution26::conqueue_errc error) -> void override { + ::beman::execution26::set_error(::std::move(this->receiver), error); + } + auto complete(::std::exception_ptr ex) -> void override { + ::beman::execution26::set_error(::std::move(this->receiver), std::move(ex)); } - return rc; + }; + + using sender_concept = ::beman::execution26::sender_t; + //-dk:TODO remove exception_ptr if move-ctor can't throw + using completion_signatures = + ::beman::execution26::completion_signatures<::beman::execution26::set_value_t(), + ::beman::execution26::set_error_t( + ::beman::execution26::conqueue_errc), + ::beman::execution26::set_error_t(::std::exception_ptr), + ::beman::execution26::set_stopped_t()>; + + bounded_queue& queue; + ::std::remove_cvref_t value; + template + push_sender(bounded_queue& queue, V&& value) : queue(queue), value(::std::forward(value)) {} + template <::beman::execution26::receiver Receiver> + auto connect(Receiver&& receiver) && -> state { + return state(queue, ::std::move(value), ::std::forward(receiver)); } - template - auto internal_push(Arg&& arg, ::beman::execution26::conqueue_errc& error) -> bool { - ::std::unique_lock cerberus(this->mutex); - this->push_condition.wait(cerberus, [this] { return this->closed || this->head - this->tail < this->max; }); - if (this->closed) { - error = ::beman::execution26::conqueue_errc::closed; - return false; +}; + +template +class beman::execution26::bounded_queue::pop_sender { + public: + template + struct state : pop_base { + using operation_state_concept = ::beman::execution26::operation_state_t; + + bounded_queue& queue; + ::std::remove_cvref_t receiver; + + template + state(bounded_queue& queue, R&& receiver) : pop_base(), queue(queue), receiver(::std::forward(receiver)) {} + + auto start() & noexcept { this->queue.start_pop(*this); } + auto complete(T val) -> void override { + ::beman::execution26::set_value(::std::move(this->receiver), ::std::move(val)); } - this->construct(this->get(this->head), ::std::forward(arg)); - ++this->head; - this->pop_notify(cerberus); - return true; + auto complete(::beman::execution26::conqueue_errc error) -> void override { + ::beman::execution26::set_error(::std::move(this->receiver), error); + } + auto complete(::std::exception_ptr ex) -> void override { + ::beman::execution26::set_error(::std::move(this->receiver), ::std::move(ex)); + } + }; + + using sender_concept = ::beman::execution26::sender_t; + using completion_signatures = ::beman::execution26::completion_signatures<::beman::execution26::set_value_t(T), + ::beman::execution26::set_error_t( + ::beman::execution26::conqueue_errc), + ::beman::execution26::set_stopped_t()>; + + bounded_queue& queue; + template <::beman::execution26::receiver Receiver> + auto connect(Receiver&& receiver) && -> state { + return state(queue, ::std::forward(receiver)); } - template - auto internal_try_push(Arg&& arg, ::std::error_code& ec) -> bool { - ::std::unique_lock cerberus(this->mutex); - if (this->closed) { - ec = make_error_code(::beman::execution26::conqueue_errc::closed); - return false; +}; + +template +beman::execution26::bounded_queue::bounded_queue(::std::size_t max, Allocator allocator) + : allocator(allocator), + array_allocator(allocator), + max(max), + elements(array_allocator_traits::allocate(this->array_allocator, this->max)) {} + +template +beman::execution26::bounded_queue::~bounded_queue() { + for (; this->tail != this->head; ++this->tail) { + this->destroy(this->get(this->tail)); + } + array_allocator_traits::deallocate(this->array_allocator, this->elements, this->max); +} + +template +auto beman::execution26::bounded_queue::is_closed() const noexcept -> bool { + std::lock_guard cerberos(this->mutex); + return this->closed; +} + +template +auto beman::execution26::bounded_queue::close() noexcept -> void { + std::unique_lock cerberus(this->mutex); + this->closed = true; + push_sender_queue push_queue(std::move(this->push_queue)); + pop_sender_queue pop_queue(std::move(this->pop_queue)); + assert(cerberus.owns_lock()); + cerberus.unlock(); + + while (not push_queue.empty()) { + push_queue.pop()->complete(::beman::execution26::conqueue_errc::closed); + } + while (not pop_queue.empty()) { + pop_queue.pop()->complete(::beman::execution26::conqueue_errc::closed); + } +} + +template +auto beman::execution26::bounded_queue::push(const T& value) -> void { + ::beman::execution26::conqueue_errc error{}; + if (not this->internal_push(value, error)) { + throw ::beman::execution26::conqueue_error(error); + } +} + +template +auto beman::execution26::bounded_queue::push(T&& value) -> void { + ::beman::execution26::conqueue_errc error{}; + if (not this->internal_push(::std::move(value), error)) { + throw ::beman::execution26::conqueue_error(error); + } +} + +template +auto beman::execution26::bounded_queue::push(const T& value, ::std::error_code& ec) -> bool { + ::beman::execution26::conqueue_errc error{}; + if (not this->internal_push(value, error)) { + ec = make_error_code(error); + return false; + } + return true; +} + +template +auto beman::execution26::bounded_queue::push(T&& value, ::std::error_code& ec) -> bool { + ::beman::execution26::conqueue_errc error{}; + if (not this->internal_push(value, error)) { + ec = make_error_code(error); + return false; + } + return true; +} + +template +auto beman::execution26::bounded_queue::try_push(const T& value, ::std::error_code& ec) -> bool { + return this->internal_try_push(value, ec); +} + +template +auto beman::execution26::bounded_queue::try_push(T&& value, ::std::error_code& ec) -> bool { + return this->internal_try_push(::std::move(value), ec); +} + +template +auto beman::execution26::bounded_queue::async_push(const T& value) -> push_sender { + return push_sender(*this, value); +} + +template +auto beman::execution26::bounded_queue::async_push(T&& value) -> push_sender { + return push_sender(*this, ::std::move(value)); +} + +template +auto beman::execution26::bounded_queue::pop() -> value_type { + return this->internal_pop([](auto&& variant) { + switch (variant.index()) { + default: + throw ::std::runtime_error("unknown result in bounded_queue"); + case 1: + return ::std::move(::std::get<1>(variant)); + case 2: + throw ::beman::execution26::conqueue_error(::std::get<2>(variant)); + case 3: + ::std::rethrow_exception(::std::get<3>(variant)); } - if (this->head - this->tail == this->max) { - ec = make_error_code(::beman::execution26::conqueue_errc::full); - return false; + }); +} + +template +auto beman::execution26::bounded_queue::pop(::std::error_code& ec) -> ::std::optional { + return this->internal_pop([&ec](auto&& variant) { + switch (variant.index()) { + default: + throw ::std::runtime_error("unknown result in bounded_queue"); + case 1: + return ::std::optional(::std::move(::std::get<1>(variant))); + case 2: + ec = make_error_code(::std::get<2>(variant)); + return ::std::optional(); + case 3: + ::std::rethrow_exception(::std::get<3>(variant)); } - this->construct(this->get(this->head), ::std::forward(arg)); - ++this->head; - this->pop_notify(cerberus); - return true; + }); +} + +template +auto beman::execution26::bounded_queue::try_pop(::std::error_code& ec) -> ::std::optional { + ::std::unique_lock cerberus(this->mutex); + if (this->closed) { + ec = make_error_code(::beman::execution26::conqueue_errc::closed); + return {}; + } + if (this->head == this->tail) { + ec = make_error_code(::beman::execution26::conqueue_errc::empty); + return {}; } - auto pop_notify(auto& cerberus) { - if (not this->pop_queue.empty()) - { - element_t* element{this->get(this->tail)}; - ::std::remove_cvref_t val{std::move(element->value)}; - this->destroy(element); - ++this->tail; - auto state{this->pop_queue.pop()}; - this->push_notify(cerberus); - state->complete(std::move(val)); + return this->pop_value(cerberus); +} + +template +auto beman::execution26::bounded_queue::async_pop() -> pop_sender { + return pop_sender(*this); +} + +template +template +auto beman::execution26::bounded_queue::construct(element_t* element, Args&&... args) -> void { + array_allocator_traits::construct( + this->array_allocator, this->get(this->head), this->allocator, ::std::forward(args)...); +} + +template +auto beman::execution26::bounded_queue::destroy(element_t* element) -> void { + allocator_traits::destroy(this->allocator, &element->value); + array_allocator_traits::destroy(this->array_allocator, element); +} + +template +auto beman::execution26::bounded_queue::get(::std::uint64_t index) noexcept -> element_t* { + return this->elements + (index % this->max); +} + +template +template +auto beman::execution26::bounded_queue::internal_pop(HandleResult handle_result) { + struct state : pop_base { + bounded_queue& queue; + bool ready{false}; + ::std::variant<::std::monostate, value_type, ::beman::execution26::conqueue_errc, ::std::exception_ptr> + result{}; + ::std::condition_variable condition{}; + + state(bounded_queue& queue) : queue(queue) {} + auto complete(value_type value) -> void override { + { + ::std::lock_guard cerberus(queue.mutex); + this->result = ::std::move(value); + this->ready = true; + } + this->condition.notify_one(); } - else - { - cerberus.unlock(); - this->pop_condition.notify_one(); + auto complete(::beman::execution26::conqueue_errc err) -> void override { + { + ::std::lock_guard cerberus(queue.mutex); + result = err; + this->ready = true; + } + this->condition.notify_one(); + } + auto complete(::std::exception_ptr ex) -> void override { + { + ::std::lock_guard cerberus(queue.mutex); + result = std::move(ex); + this->ready = true; + } + this->condition.notify_one(); } + }; + state s(*this); + this->start_pop(s); + { + ::std::unique_lock cerberus(this->mutex); + s.condition.wait(cerberus, [&ready = s.ready] { return ready; }); } - auto push_notify(auto& cerberus) { - if (not this->push_queue.empty()) - { - cerberus.unlock(); - this->push_queue.pop()->complete(); + return handle_result(::std::move(s.result)); +} + +template +template +auto beman::execution26::bounded_queue::internal_push(Arg&& arg, + ::beman::execution26::conqueue_errc& error) + -> bool { + struct state : push_base { + bool ready{false}; + ::std::variant<::std::monostate, ::beman::execution26::conqueue_errc, ::std::exception_ptr> result; + ::std::condition_variable condition; + + state(Arg&& arg) : push_base(::std::forward(arg)) {} + auto complete() -> void override { + this->ready = true; + this->condition.notify_one(); } - else - { - cerberus.unlock(); - this->push_condition.notify_one(); + auto complete(::beman::execution26::conqueue_errc err) -> void override { + result = err; + this->complete(); + } + auto complete(::std::exception_ptr ex) -> void override { + result = std::move(ex); + this->complete(); } + }; + state s(::std::forward(arg)); + this->start_push(s); + { + ::std::unique_lock cerberus(this->mutex); + s.condition.wait(cerberus, [&ready = s.ready] { return ready; }); } - template - auto internal_async_push(TT&& value) -> sender auto { - return push_sender{*this, ::std::forward(value)}; + switch (s.result.index()) { + case 0: + return true; + case 1: + error = ::std::get<1>(s.result); + break; + case 2: + std::rethrow_exception(::std::get<2>(s.result)); } - auto start_push(push_sender::state_base& s) -> bool { - std::unique_lock cerberus(this->mutex); - if (this->head - this->tail < this->max) { - this->construct(this->get(this->head), ::std::move(s.value)); - ++this->head; - this->pop_notify(cerberus); - return true; - } else { - this->push_queue.push(&s); - return false; + return false; +} + +template +template +auto beman::execution26::bounded_queue::internal_try_push(Arg&& arg, ::std::error_code& ec) -> bool { + ::std::unique_lock cerberus(this->mutex); + if (this->closed) { + ec = make_error_code(::beman::execution26::conqueue_errc::closed); + return false; + } + if (not this->has_space(cerberus)) { + ec = make_error_code(::beman::execution26::conqueue_errc::full); + return false; + } + this->push_value(cerberus, ::std::forward(arg)); + return true; +} + +template +auto beman::execution26::bounded_queue::pop_notify(auto& cerberus) -> void { + assert(cerberus.owns_lock()); + if (not this->pop_queue.empty()) { + auto state{this->pop_queue.pop()}; + state->complete(this->pop_value(cerberus)); + } else { + cerberus.unlock(); + } +} + +template +auto beman::execution26::bounded_queue::push_notify(auto& cerberus) -> void { + assert(cerberus.owns_lock()); + if (not this->push_queue.empty()) { + push_base* push(this->push_queue.pop()); + try { + this->push_value(cerberus, ::std::move(push->value)); + assert(not cerberus.owns_lock()); + } catch (...) { + cerberus.unlock(); + push->complete(::std::current_exception()); + assert(not cerberus.owns_lock()); + return; } + push->complete(); + } else { + cerberus.unlock(); } - auto start_pop(pop_sender::state_base& s) -> bool { - std::unique_lock cerberus(this->mutex); - if (this->head != this->tail) { - element_t* element(this->get(tail)); - std::remove_cvref_t val(std::move(element->value)); - ++this->tail; - this->destroy(element); - this->push_notify(cerberus); - s.complete(std::move(val)); - return true; - } else { - this->pop_queue.push(&s); - return false; + assert(not cerberus.owns_lock()); +} + +template +template +auto beman::execution26::bounded_queue::has_space(Lock&) noexcept -> bool { + return this->head - this->tail < this->max; +} + +template +template +auto beman::execution26::bounded_queue::push_value(Lock& cerberus, V&& value) -> void { + + this->construct(this->get(this->head), ::std::forward(value)); + ++this->head; + assert(cerberus.owns_lock()); + this->pop_notify(cerberus); + assert(not cerberus.owns_lock()); +} + +template +template +auto beman::execution26::bounded_queue::pop_value(Lock& cerberus) -> value_type { + element_t* element(this->get(tail)); + value_type val(std::move(element->value)); + ++this->tail; + this->destroy(element); + this->push_notify(cerberus); + return val; +} + +template +auto beman::execution26::bounded_queue::start_push(push_base& s) -> void { + std::unique_lock cerberus(this->mutex); + if (this->closed) { + cerberus.unlock(); + s.complete(::beman::execution26::conqueue_errc::closed); + } else if (this->has_space(cerberus)) { + try { + this->push_value(cerberus, std::move(s.value)); + s.complete(); + } catch (...) { + assert(cerberus.owns_lock()); + cerberus.unlock(); + s.complete(std::current_exception()); } + } else { + this->push_queue.push(&s); } +} - Allocator allocator; - array_allocator_type array_allocator; - mutable ::std::mutex mutex; - push_sender_queue push_queue; - ::std::condition_variable push_condition; - pop_sender_queue pop_queue; - ::std::condition_variable pop_condition; - ::std::size_t max; - element_t* elements; - ::std::uint64_t head{}; // the next element to push to - ::std::uint64_t tail{}; // the next element to push from - bool closed{}; -}; +template +auto beman::execution26::bounded_queue::start_pop(pop_base& s) -> void { + std::unique_lock cerberus(this->mutex); + if (this->closed) { + cerberus.unlock(); + s.complete(::beman::execution26::conqueue_errc::closed); + } else if (this->head != this->tail) { + s.complete(this->pop_value(cerberus)); + } else { + this->pop_queue.push(&s); + } +} // ---------------------------------------------------------------------------- -#endif +#endif \ No newline at end of file diff --git a/include/beman/execution26/detail/intrusive_queue.hpp b/include/beman/execution26/detail/intrusive_queue.hpp index b7839840..187eb1d9 100644 --- a/include/beman/execution26/detail/intrusive_queue.hpp +++ b/include/beman/execution26/detail/intrusive_queue.hpp @@ -4,6 +4,7 @@ #ifndef INCLUDED_BEMAN_EXECUTION26_DETAIL_INTRUSIVE_QUEUE #define INCLUDED_BEMAN_EXECUTION26_DETAIL_INTRUSIVE_QUEUE +#include #include // ---------------------------------------------------------------------------- @@ -12,26 +13,69 @@ namespace beman::execution26::detail { template struct intrusive_queue; +//! @brief This class template implements a non-thread-safe, intrusive queue. +//! @headerfile beman/execution26/detail/intrusive_queue.hpp +//! @details +//! The template argument is a pointer-to-member for some class which +//! is itself a pointer to the type. The member is used to form a singly +//! linked list. +//! +//!

Usage

+//!
+//! intrusive_queue<&Type::next>
+//! 
+//! +//!

Example

+//!
+//! #include 
+//! #include 
+//! namespace detail = beman::execution26::detail;
+//! struct node {
+//!     int   value{};
+//!     node* link{};
+//! };
+//! int main() {
+//!     detail::intrusive<&node::link> q;
+//!     assert(q.empty());
+//!     node ns[]{ { 1 }, { 2 }, { 3 } };
+//!     for (auto& n: ns) { q.push(&n); }
+//!     assert(!q.empty());
+//!     assert(q.pop()->value == 1);
+//!     assert(q.pop()->value == 2);
+//!     assert(q.pop()->value == 3);
+//! }
+//! 
template struct intrusive_queue { T* head{}; T* tail{}; - intrusive_queue() = default; - intrusive_queue(intrusive_queue&& other) - : head(::std::exchange(other.head, nullptr)) - , tail(::std::exchange(other.tail, nullptr)) - { - } - auto push(T* n) -> void { + //! @brief The default constructor create an empty queue + constexpr intrusive_queue() noexcept = default; + //! @brief The move constructor transfers the content of the parameter to the new queue + //! @details + //! The original queue `other` will be empty after this operation. + intrusive_queue(intrusive_queue&& other) noexcept + : head(::std::exchange(other.head, nullptr)), tail(::std::exchange(other.tail, nullptr)) {} + //! @brief Push the node `n` to the end of the queue + //! @details + //! The "next" pointer of the new node is set to `nullptr`. + auto push(T* n) noexcept -> void { + n->*next = nullptr; if (this->head) { std::exchange(this->tail, n)->*next = n; } else { this->head = this->tail = n; } } - auto pop() -> T* { return std::exchange(this->head, this->head->*next); } - auto empty() const -> bool { return this->head == nullptr; } + //! @brief Remove the start node of the queue and return it + //! @pre The queue is not empty + auto pop() noexcept -> T* { + assert(not this->empty()); + return std::exchange(this->head, this->head->*next); + } + //! @brief Test whether the queue is empty + auto empty() const noexcept -> bool { return this->head == nullptr; } }; } // namespace beman::execution26::detail diff --git a/tests/beman/execution26/bounded-queue.test.cpp b/tests/beman/execution26/bounded-queue.test.cpp index 5cfbc686..e3098412 100644 --- a/tests/beman/execution26/bounded-queue.test.cpp +++ b/tests/beman/execution26/bounded-queue.test.cpp @@ -157,7 +157,8 @@ auto test_async_push(auto one, auto two, auto three, auto four, auto five) -> vo int& complete; auto set_value() && noexcept -> void { this->complete = 1; } auto set_error(test_std::conqueue_errc) && noexcept -> void { this->complete = 2; } - auto set_stopped() && noexcept -> void { this->complete = 3; } + auto set_error(std::exception_ptr) && noexcept -> void { this->complete = 3; } + auto set_stopped() && noexcept -> void { this->complete = 4; } }; static_assert(test_std::receiver); @@ -206,9 +207,13 @@ auto test_async_pop(auto one, auto two, auto three, auto four, auto five) -> voi using receiver_concept = test_std::receiver_t; int& complete; std::vector& vals; - auto set_value(T val) && noexcept -> void { this->complete = 1; vals.push_back(val); } + auto set_value(T val) && noexcept -> void { + this->complete = 1; + vals.push_back(val); + } auto set_error(test_std::conqueue_errc) && noexcept -> void { this->complete = 2; } - auto set_stopped() && noexcept -> void { this->complete = 3; } + auto set_error(std::exception_ptr) && noexcept -> void { this->complete = 3; } + auto set_stopped() && noexcept -> void { this->complete = 4; } }; static_assert(test_std::receiver); From f3f2c3865555f3d7846581ca1d78d40ab76de7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Tue, 3 Dec 2024 01:39:13 +0000 Subject: [PATCH 07/41] fixed a race --- include/beman/execution26/detail/bounded_queue.hpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/include/beman/execution26/detail/bounded_queue.hpp b/include/beman/execution26/detail/bounded_queue.hpp index 01fb7224..5209a77f 100644 --- a/include/beman/execution26/detail/bounded_queue.hpp +++ b/include/beman/execution26/detail/bounded_queue.hpp @@ -431,10 +431,14 @@ auto beman::execution26::bounded_queue::internal_push(Arg&& bool ready{false}; ::std::variant<::std::monostate, ::beman::execution26::conqueue_errc, ::std::exception_ptr> result; ::std::condition_variable condition; + bounded_queue& queue; - state(Arg&& arg) : push_base(::std::forward(arg)) {} + state(bounded_queue& queue, Arg&& arg) : push_base(::std::forward(arg)), queue(queue) {} auto complete() -> void override { - this->ready = true; + ::std::lock_guard cerberus(queue.mutex); + { + this->ready = true; + } this->condition.notify_one(); } auto complete(::beman::execution26::conqueue_errc err) -> void override { @@ -446,7 +450,7 @@ auto beman::execution26::bounded_queue::internal_push(Arg&& this->complete(); } }; - state s(::std::forward(arg)); + state s(*this, ::std::forward(arg)); this->start_push(s); { ::std::unique_lock cerberus(this->mutex); From f23e722f85ce1891c8708eb2c21922f3f5637a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Tue, 3 Dec 2024 01:49:40 +0000 Subject: [PATCH 08/41] try to working around an MSVC++ problem --- .../execution26/detail/bounded_queue.hpp | 72 ++++++++++--------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/include/beman/execution26/detail/bounded_queue.hpp b/include/beman/execution26/detail/bounded_queue.hpp index 5209a77f..2504e30a 100644 --- a/include/beman/execution26/detail/bounded_queue.hpp +++ b/include/beman/execution26/detail/bounded_queue.hpp @@ -81,6 +81,8 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl virtual auto complete(::std::exception_ptr) -> void = 0; }; + struct blocking_pop_state; + union element_t { element_t() {} template @@ -132,6 +134,41 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl bool closed{}; }; +template +struct beman::execution26::bounded_queue::blocking_pop_state : pop_base { + bounded_queue& queue; + bool ready{false}; + ::std::variant<::std::monostate, value_type, ::beman::execution26::conqueue_errc, ::std::exception_ptr> + result{}; + ::std::condition_variable condition{}; + + blocking_pop_state(bounded_queue& queue) : queue(queue) {} + auto complete(value_type value) -> void override { + { + ::std::lock_guard cerberus(queue.mutex); + this->result = ::std::move(value); + this->ready = true; + } + this->condition.notify_one(); + } + auto complete(::beman::execution26::conqueue_errc err) -> void override { + { + ::std::lock_guard cerberus(queue.mutex); + result = err; + this->ready = true; + } + this->condition.notify_one(); + } + auto complete(::std::exception_ptr ex) -> void override { + { + ::std::lock_guard cerberus(queue.mutex); + result = std::move(ex); + this->ready = true; + } + this->condition.notify_one(); + } + }; + template class beman::execution26::bounded_queue::push_sender { public: @@ -380,40 +417,7 @@ auto beman::execution26::bounded_queue::get(::std::uint64_t index) template template auto beman::execution26::bounded_queue::internal_pop(HandleResult handle_result) { - struct state : pop_base { - bounded_queue& queue; - bool ready{false}; - ::std::variant<::std::monostate, value_type, ::beman::execution26::conqueue_errc, ::std::exception_ptr> - result{}; - ::std::condition_variable condition{}; - - state(bounded_queue& queue) : queue(queue) {} - auto complete(value_type value) -> void override { - { - ::std::lock_guard cerberus(queue.mutex); - this->result = ::std::move(value); - this->ready = true; - } - this->condition.notify_one(); - } - auto complete(::beman::execution26::conqueue_errc err) -> void override { - { - ::std::lock_guard cerberus(queue.mutex); - result = err; - this->ready = true; - } - this->condition.notify_one(); - } - auto complete(::std::exception_ptr ex) -> void override { - { - ::std::lock_guard cerberus(queue.mutex); - result = std::move(ex); - this->ready = true; - } - this->condition.notify_one(); - } - }; - state s(*this); + blocking_pop_state s(*this); this->start_pop(s); { ::std::unique_lock cerberus(this->mutex); From 69ba1e0851f0873d2f277d967d33dc1462b3967c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Tue, 3 Dec 2024 01:57:15 +0000 Subject: [PATCH 09/41] another attempt at working around an MSVC++ problem with local classes --- .../execution26/detail/bounded_queue.hpp | 108 +++++++++--------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/include/beman/execution26/detail/bounded_queue.hpp b/include/beman/execution26/detail/bounded_queue.hpp index 2504e30a..4dc354d0 100644 --- a/include/beman/execution26/detail/bounded_queue.hpp +++ b/include/beman/execution26/detail/bounded_queue.hpp @@ -81,6 +81,7 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl virtual auto complete(::std::exception_ptr) -> void = 0; }; + struct blocking_push_state; struct blocking_pop_state; union element_t { @@ -134,40 +135,64 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl bool closed{}; }; +template +struct beman::execution26::bounded_queue::blocking_push_state : push_base { + bool ready{false}; + ::std::variant<::std::monostate, ::beman::execution26::conqueue_errc, ::std::exception_ptr> result; + ::std::condition_variable condition; + bounded_queue& queue; + + template + blocking_push_state(bounded_queue& queue, Arg&& arg) : push_base(::std::forward(arg)), queue(queue) {} + auto complete() -> void override { + ::std::lock_guard cerberus(queue.mutex); + { + this->ready = true; + } + this->condition.notify_one(); + } + auto complete(::beman::execution26::conqueue_errc err) -> void override { + result = err; + this->complete(); + } + auto complete(::std::exception_ptr ex) -> void override { + result = std::move(ex); + this->complete(); + } +}; template struct beman::execution26::bounded_queue::blocking_pop_state : pop_base { - bounded_queue& queue; - bool ready{false}; - ::std::variant<::std::monostate, value_type, ::beman::execution26::conqueue_errc, ::std::exception_ptr> - result{}; - ::std::condition_variable condition{}; - - blocking_pop_state(bounded_queue& queue) : queue(queue) {} - auto complete(value_type value) -> void override { - { - ::std::lock_guard cerberus(queue.mutex); - this->result = ::std::move(value); - this->ready = true; - } - this->condition.notify_one(); + bounded_queue& queue; + bool ready{false}; + ::std::variant<::std::monostate, value_type, ::beman::execution26::conqueue_errc, ::std::exception_ptr> result{}; + ::std::condition_variable condition{}; + + blocking_pop_state(bounded_queue& queue) : queue(queue) {} + auto complete(value_type value) -> void override { + { + ::std::lock_guard cerberus(queue.mutex); + this->result = ::std::move(value); + this->ready = true; } - auto complete(::beman::execution26::conqueue_errc err) -> void override { - { - ::std::lock_guard cerberus(queue.mutex); - result = err; - this->ready = true; - } - this->condition.notify_one(); + this->condition.notify_one(); + } + auto complete(::beman::execution26::conqueue_errc err) -> void override { + { + ::std::lock_guard cerberus(queue.mutex); + result = err; + this->ready = true; } - auto complete(::std::exception_ptr ex) -> void override { - { - ::std::lock_guard cerberus(queue.mutex); - result = std::move(ex); - this->ready = true; - } - this->condition.notify_one(); + this->condition.notify_one(); + } + auto complete(::std::exception_ptr ex) -> void override { + { + ::std::lock_guard cerberus(queue.mutex); + result = std::move(ex); + this->ready = true; } - }; + this->condition.notify_one(); + } +}; template class beman::execution26::bounded_queue::push_sender { @@ -431,30 +456,7 @@ template auto beman::execution26::bounded_queue::internal_push(Arg&& arg, ::beman::execution26::conqueue_errc& error) -> bool { - struct state : push_base { - bool ready{false}; - ::std::variant<::std::monostate, ::beman::execution26::conqueue_errc, ::std::exception_ptr> result; - ::std::condition_variable condition; - bounded_queue& queue; - - state(bounded_queue& queue, Arg&& arg) : push_base(::std::forward(arg)), queue(queue) {} - auto complete() -> void override { - ::std::lock_guard cerberus(queue.mutex); - { - this->ready = true; - } - this->condition.notify_one(); - } - auto complete(::beman::execution26::conqueue_errc err) -> void override { - result = err; - this->complete(); - } - auto complete(::std::exception_ptr ex) -> void override { - result = std::move(ex); - this->complete(); - } - }; - state s(*this, ::std::forward(arg)); + blocking_push_state s(*this, ::std::forward(arg)); this->start_push(s); { ::std::unique_lock cerberus(this->mutex); From c2ef01a96ceefe5afcb6cf17aea0f7ebf3e76db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Tue, 3 Dec 2024 23:19:30 +0000 Subject: [PATCH 10/41] addressed gcc warnings --- .../beman/execution26/detail/basic_state.hpp | 6 +-- .../execution26/detail/bounded_queue.hpp | 42 +++++++++---------- .../beman/execution26/detail/connect_all.hpp | 4 +- include/beman/execution26/detail/fwd_env.hpp | 2 +- .../detail/get_completion_signatures.hpp | 7 ++-- .../detail/inplace_stop_source.hpp | 2 +- include/beman/execution26/detail/join_env.hpp | 2 +- include/beman/execution26/detail/make_env.hpp | 2 +- include/beman/execution26/detail/notify.hpp | 4 +- .../detail/operation_state_task.hpp | 4 +- include/beman/execution26/detail/run_loop.hpp | 2 +- .../beman/execution26/detail/sched_attrs.hpp | 2 +- .../detail/simple_counting_scope.hpp | 5 +-- .../beman/execution26/detail/stop_source.hpp | 4 +- .../beman/execution26/bounded-queue.test.cpp | 15 ++++--- 15 files changed, 52 insertions(+), 51 deletions(-) diff --git a/include/beman/execution26/detail/basic_state.hpp b/include/beman/execution26/detail/basic_state.hpp index 292f3090..73a030c9 100644 --- a/include/beman/execution26/detail/basic_state.hpp +++ b/include/beman/execution26/detail/basic_state.hpp @@ -20,10 +20,10 @@ namespace beman::execution26::detail { */ template struct basic_state { - basic_state(Sender&& sender, Receiver&& receiver) noexcept(true) - : receiver(::std::move(receiver)), + basic_state(Sender&& sndr, Receiver&& rcvr) noexcept(true) + : receiver(::std::move(rcvr)), state(::beman::execution26::detail::impls_for< ::beman::execution26::tag_of_t >::get_state( - ::std::forward(sender), this->receiver)) {} + ::std::forward(sndr), this->receiver)) {} Receiver receiver; ::beman::execution26::detail::state_type state; diff --git a/include/beman/execution26/detail/bounded_queue.hpp b/include/beman/execution26/detail/bounded_queue.hpp index 4dc354d0..632b7bbc 100644 --- a/include/beman/execution26/detail/bounded_queue.hpp +++ b/include/beman/execution26/detail/bounded_queue.hpp @@ -62,17 +62,17 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl auto async_pop() -> pop_sender; private: - struct push_base { + struct push_base : ::beman::execution26::detail::virtual_immovable { value_type value; push_base* next{}; - template - push_base(V&& value) : value(::std::forward(value)) {} + template + push_base(Val&& val) : value(::std::forward(val)) {} virtual auto complete() -> void = 0; virtual auto complete(::beman::execution26::conqueue_errc) -> void = 0; virtual auto complete(::std::exception_ptr) -> void = 0; }; - struct pop_base { + struct pop_base : ::beman::execution26::detail::virtual_immovable { pop_base* next{}; pop_base() = default; @@ -143,7 +143,7 @@ struct beman::execution26::bounded_queue::blocking_push_state : pu bounded_queue& queue; template - blocking_push_state(bounded_queue& queue, Arg&& arg) : push_base(::std::forward(arg)), queue(queue) {} + blocking_push_state(bounded_queue& que, Arg&& arg) : push_base(::std::forward(arg)), queue(que) {} auto complete() -> void override { ::std::lock_guard cerberus(queue.mutex); { @@ -167,7 +167,7 @@ struct beman::execution26::bounded_queue::blocking_pop_state : pop ::std::variant<::std::monostate, value_type, ::beman::execution26::conqueue_errc, ::std::exception_ptr> result{}; ::std::condition_variable condition{}; - blocking_pop_state(bounded_queue& queue) : queue(queue) {} + blocking_pop_state(bounded_queue& que) : queue(que) {} auto complete(value_type value) -> void override { { ::std::lock_guard cerberus(queue.mutex); @@ -205,8 +205,8 @@ class beman::execution26::bounded_queue::push_sender { ::std::remove_cvref_t receiver; template - state(bounded_queue& queue, T&& value, R&& receiver) - : push_base(::std::move(value)), queue(queue), receiver(::std::forward(receiver)) {} + state(bounded_queue& que, T&& value, R&& rcvr) + : push_base(::std::move(value)), queue(que), receiver(::std::forward(rcvr)) {} auto start() & noexcept { this->queue.start_push(*this); } auto complete() -> void override { ::beman::execution26::set_value(::std::move(this->receiver)); } @@ -229,8 +229,8 @@ class beman::execution26::bounded_queue::push_sender { bounded_queue& queue; ::std::remove_cvref_t value; - template - push_sender(bounded_queue& queue, V&& value) : queue(queue), value(::std::forward(value)) {} + template + push_sender(bounded_queue& que, Val&& val) : queue(que), value(::std::forward(val)) {} template <::beman::execution26::receiver Receiver> auto connect(Receiver&& receiver) && -> state { return state(queue, ::std::move(value), ::std::forward(receiver)); @@ -248,7 +248,7 @@ class beman::execution26::bounded_queue::pop_sender { ::std::remove_cvref_t receiver; template - state(bounded_queue& queue, R&& receiver) : pop_base(), queue(queue), receiver(::std::forward(receiver)) {} + state(bounded_queue& que, R&& rcvr) : pop_base(), queue(que), receiver(::std::forward(rcvr)) {} auto start() & noexcept { this->queue.start_pop(*this); } auto complete(T val) -> void override { @@ -276,10 +276,10 @@ class beman::execution26::bounded_queue::pop_sender { }; template -beman::execution26::bounded_queue::bounded_queue(::std::size_t max, Allocator allocator) - : allocator(allocator), - array_allocator(allocator), - max(max), +beman::execution26::bounded_queue::bounded_queue(::std::size_t mx, Allocator alloc) + : allocator(alloc), + array_allocator(alloc), + max(mx), elements(array_allocator_traits::allocate(this->array_allocator, this->max)) {} template @@ -300,16 +300,16 @@ template auto beman::execution26::bounded_queue::close() noexcept -> void { std::unique_lock cerberus(this->mutex); this->closed = true; - push_sender_queue push_queue(std::move(this->push_queue)); - pop_sender_queue pop_queue(std::move(this->pop_queue)); + push_sender_queue push_que(std::move(this->push_queue)); + pop_sender_queue pop_que(std::move(this->pop_queue)); assert(cerberus.owns_lock()); cerberus.unlock(); - while (not push_queue.empty()) { - push_queue.pop()->complete(::beman::execution26::conqueue_errc::closed); + while (not push_que.empty()) { + push_que.pop()->complete(::beman::execution26::conqueue_errc::closed); } - while (not pop_queue.empty()) { - pop_queue.pop()->complete(::beman::execution26::conqueue_errc::closed); + while (not pop_que.empty()) { + pop_que.pop()->complete(::beman::execution26::conqueue_errc::closed); } } diff --git a/include/beman/execution26/detail/connect_all.hpp b/include/beman/execution26/detail/connect_all.hpp index 5598ab72..766fa319 100644 --- a/include/beman/execution26/detail/connect_all.hpp +++ b/include/beman/execution26/detail/connect_all.hpp @@ -34,10 +34,10 @@ struct connect_all_t { auto data{::beman::execution26::detail::get_sender_data(::std::forward(sender))}; return ::std::apply( [&op](auto&&... c) { - return [&op]<::std::size_t... J>(::std::index_sequence, auto&&... c) { + return [&op]<::std::size_t... J>(::std::index_sequence, auto&&... cc) { use(op); return ::beman::execution26::detail::product_type{::beman::execution26::connect( - ::beman::execution26::detail::forward_like(c), + ::beman::execution26::detail::forward_like(cc), ::beman::execution26::detail:: basic_receiver>{op})...}; }(::std::make_index_sequence<::std::tuple_size_v<::std::decay_t>>{}, c...); diff --git a/include/beman/execution26/detail/fwd_env.hpp b/include/beman/execution26/detail/fwd_env.hpp index 0cf15759..b3288deb 100644 --- a/include/beman/execution26/detail/fwd_env.hpp +++ b/include/beman/execution26/detail/fwd_env.hpp @@ -25,7 +25,7 @@ class fwd_env { Env env; public: - explicit fwd_env(Env&& env) : env(::std::forward(env)) {} + explicit fwd_env(Env&& e) : env(::std::forward(e)) {} template requires(not::beman::execution26::forwarding_query(::std::remove_cvref_t())) diff --git a/include/beman/execution26/detail/get_completion_signatures.hpp b/include/beman/execution26/detail/get_completion_signatures.hpp index c77d1707..5ad453ab 100644 --- a/include/beman/execution26/detail/get_completion_signatures.hpp +++ b/include/beman/execution26/detail/get_completion_signatures.hpp @@ -23,10 +23,9 @@ struct get_completion_signatures_t { private: template static auto get(Sender&& sender, Env&& env) noexcept { - auto new_sender{[](auto&& sender, auto&& env) -> decltype(auto) { - return ::beman::execution26::transform_sender(::beman::execution26::detail::get_domain_late(sender, env), - ::std::forward(sender), - ::std::forward(env)); + auto new_sender{[](auto&& sndr, auto&& e) -> decltype(auto) { + auto dom{::beman::execution26::detail::get_domain_late(sndr, e)}; + return ::beman::execution26::transform_sender(dom, ::std::forward(sndr), ::std::forward(e)); }}; using sender_type = ::std::remove_cvref_t; diff --git a/include/beman/execution26/detail/inplace_stop_source.hpp b/include/beman/execution26/detail/inplace_stop_source.hpp index 609da36b..da4d6c4a 100644 --- a/include/beman/execution26/detail/inplace_stop_source.hpp +++ b/include/beman/execution26/detail/inplace_stop_source.hpp @@ -41,7 +41,7 @@ class beman::execution26::inplace_stop_token { friend class ::beman::execution26::inplace_stop_source; template friend class ::beman::execution26::inplace_stop_callback; - explicit inplace_stop_token(::beman::execution26::inplace_stop_source* source) : source(source) {} + explicit inplace_stop_token(::beman::execution26::inplace_stop_source* src) : source(src) {} ::beman::execution26::inplace_stop_source* source{}; }; diff --git a/include/beman/execution26/detail/join_env.hpp b/include/beman/execution26/detail/join_env.hpp index f61852e6..ab9cce6b 100644 --- a/include/beman/execution26/detail/join_env.hpp +++ b/include/beman/execution26/detail/join_env.hpp @@ -18,7 +18,7 @@ class join_env { public: template - join_env(E1&& env1, E2&& env2) : env1(::std::forward(env1)), env2(::std::forward(env2)) {} + join_env(E1&& ev1, E2&& ev2) : env1(::std::forward(ev1)), env2(::std::forward(ev2)) {} template requires( diff --git a/include/beman/execution26/detail/make_env.hpp b/include/beman/execution26/detail/make_env.hpp index 5b7c9860..b836e84b 100644 --- a/include/beman/execution26/detail/make_env.hpp +++ b/include/beman/execution26/detail/make_env.hpp @@ -17,7 +17,7 @@ class make_env { public: template - make_env(const Query&, V&& value) : value(::std::forward(value)) {} + make_env(const Query&, V&& val) : value(::std::forward(val)) {} constexpr auto query(const Query&) const noexcept -> const Value& { return this->value; } constexpr auto query(const Query&) noexcept -> Value& { return this->value; } }; diff --git a/include/beman/execution26/detail/notify.hpp b/include/beman/execution26/detail/notify.hpp index afa4ecda..1a09c6a9 100644 --- a/include/beman/execution26/detail/notify.hpp +++ b/include/beman/execution26/detail/notify.hpp @@ -58,8 +58,8 @@ struct impls_for<::beman::execution26::detail::notify_t> : ::beman::execution26: struct state : ::beman::execution26::detail::notifier::base { ::beman::execution26::detail::notifier* n; ::std::remove_cvref_t& receiver{}; - state(::beman::execution26::detail::notifier* n, ::std::remove_cvref_t& receiver) - : n(n), receiver(receiver) {} + state(::beman::execution26::detail::notifier* nn, ::std::remove_cvref_t& rcvr) + : n(nn), receiver(rcvr) {} auto complete() -> void override { ::beman::execution26::set_value(::std::move(this->receiver)); } }; static constexpr auto get_state{[](Sender&& sender, Receiver&& receiver) { diff --git a/include/beman/execution26/detail/operation_state_task.hpp b/include/beman/execution26/detail/operation_state_task.hpp index 85e115e1..6818d178 100644 --- a/include/beman/execution26/detail/operation_state_task.hpp +++ b/include/beman/execution26/detail/operation_state_task.hpp @@ -28,7 +28,7 @@ struct connect_awaitable_promise; template struct beman::execution26::detail::connect_awaitable_promise : ::beman::execution26::detail::with_await_transform> { - connect_awaitable_promise(auto&&, Receiver& receiver) noexcept : receiver(receiver) {} + connect_awaitable_promise(auto&&, Receiver& rcvr) noexcept : receiver(rcvr) {} auto initial_suspend() noexcept -> ::std::suspend_always { return {}; } [[noreturn]] auto final_suspend() noexcept -> ::std::suspend_always { ::std::terminate(); } [[noreturn]] auto unhandled_exception() noexcept -> void { ::std::terminate(); } @@ -56,7 +56,7 @@ struct beman::execution26::detail::operation_state_task { using operation_state_concept = ::beman::execution26::operation_state_t; using promise_type = ::beman::execution26::detail::connect_awaitable_promise; - explicit operation_state_task(::std::coroutine_handle<> handle) noexcept : handle(handle) {} + explicit operation_state_task(::std::coroutine_handle<> hndl) noexcept : handle(hndl) {} operation_state_task(const operation_state_task&) = delete; operation_state_task(operation_state_task&& other) noexcept : handle(::std::exchange(other.handle, {})) {} ~operation_state_task() { diff --git a/include/beman/execution26/detail/run_loop.hpp b/include/beman/execution26/detail/run_loop.hpp index 1f6b47cf..d6ebb708 100644 --- a/include/beman/execution26/detail/run_loop.hpp +++ b/include/beman/execution26/detail/run_loop.hpp @@ -52,7 +52,7 @@ class run_loop { Receiver receiver; template - opstate(run_loop* loop, R&& receiver) : loop(loop), receiver(::std::forward(receiver)) {} + opstate(run_loop* lp, R&& rcvr) : loop(lp), receiver(::std::forward(rcvr)) {} auto start() & noexcept -> void { try { this->loop->push_back(this); diff --git a/include/beman/execution26/detail/sched_attrs.hpp b/include/beman/execution26/detail/sched_attrs.hpp index ef650152..f56818f3 100644 --- a/include/beman/execution26/detail/sched_attrs.hpp +++ b/include/beman/execution26/detail/sched_attrs.hpp @@ -24,7 +24,7 @@ class sched_attrs { public: template - explicit sched_attrs(S sched) : sched(::std::move(sched)) {} + explicit sched_attrs(S sch) : sched(::std::move(sch)) {} template auto query(const ::beman::execution26::get_completion_scheduler_t&) const noexcept { diff --git a/include/beman/execution26/detail/simple_counting_scope.hpp b/include/beman/execution26/detail/simple_counting_scope.hpp index 09206613..ae9730a8 100644 --- a/include/beman/execution26/detail/simple_counting_scope.hpp +++ b/include/beman/execution26/detail/simple_counting_scope.hpp @@ -118,8 +118,7 @@ class beman::execution26::simple_counting_scope::assoc { private: friend class beman::execution26::simple_counting_scope::token; - explicit assoc(beman::execution26::simple_counting_scope* scope) - : scope(scope ? scope->try_associate() : nullptr) {} + explicit assoc(beman::execution26::simple_counting_scope* scp) : scope(scp ? scp->try_associate() : nullptr) {} beman::execution26::simple_counting_scope* scope{}; }; // NOLINTEND(misc-unconventional-assign-operator,hicpp-special-member-functions) @@ -139,7 +138,7 @@ class beman::execution26::simple_counting_scope::token { private: friend class beman::execution26::simple_counting_scope; - explicit token(beman::execution26::simple_counting_scope* scope) noexcept : scope(scope) {} + explicit token(beman::execution26::simple_counting_scope* scp) noexcept : scope(scp) {} beman::execution26::simple_counting_scope* scope; }; diff --git a/include/beman/execution26/detail/stop_source.hpp b/include/beman/execution26/detail/stop_source.hpp index 0ed9c7c1..24b42c48 100644 --- a/include/beman/execution26/detail/stop_source.hpp +++ b/include/beman/execution26/detail/stop_source.hpp @@ -192,8 +192,8 @@ inline auto beman::execution26::detail::stop_callback_base::deregister() -> void inline auto beman::execution26::detail::stop_callback_base::call() -> void { this->do_call(); } -inline beman::execution26::stop_token::stop_token(::std::shared_ptr<::beman::execution26::detail::stop_state> state) - : state(::std::move(state)) {} +inline beman::execution26::stop_token::stop_token(::std::shared_ptr<::beman::execution26::detail::stop_state> st) + : state(::std::move(st)) {} inline auto beman::execution26::stop_token::swap(stop_token& other) noexcept -> void { this->state.swap(other.state); } diff --git a/tests/beman/execution26/bounded-queue.test.cpp b/tests/beman/execution26/bounded-queue.test.cpp index e3098412..d3184868 100644 --- a/tests/beman/execution26/bounded-queue.test.cpp +++ b/tests/beman/execution26/bounded-queue.test.cpp @@ -6,7 +6,6 @@ #include #include #include -#include //-dk:TODO remove // ---------------------------------------------------------------------------- @@ -113,8 +112,8 @@ auto test_pop(const auto one, auto two, const auto three, auto four, auto five) queue.push(five); }); - std::error_code ec{}; - auto val4(queue.pop(ec)); + std::error_code ec4{}; + auto val4(queue.pop(ec4)); ASSERT(val4.has_value()); ASSERT(*val4 == five); t.join(); @@ -142,10 +141,10 @@ auto test_pop(const auto one, auto two, const auto three, auto four, auto five) queue.close(); }); - std::error_code ec{}; - auto val4(queue.pop(ec)); + std::error_code ec4{}; + auto val4(queue.pop(ec4)); ASSERT(not val4.has_value()); - ASSERT(ec.value() == static_cast(test_std::conqueue_errc::closed)); + ASSERT(ec4.value() == static_cast(test_std::conqueue_errc::closed)); t.join(); } } @@ -160,6 +159,8 @@ auto test_async_push(auto one, auto two, auto three, auto four, auto five) -> vo auto set_error(std::exception_ptr) && noexcept -> void { this->complete = 3; } auto set_stopped() && noexcept -> void { this->complete = 4; } }; + typename receiver::receiver_concept c{}; + test::use(c); static_assert(test_std::receiver); test_std::bounded_queue queue(2); @@ -215,6 +216,8 @@ auto test_async_pop(auto one, auto two, auto three, auto four, auto five) -> voi auto set_error(std::exception_ptr) && noexcept -> void { this->complete = 3; } auto set_stopped() && noexcept -> void { this->complete = 4; } }; + typename receiver::receiver_concept c{}; + test::use(c); static_assert(test_std::receiver); test_std::bounded_queue queue(2); From b9cb7c9c4699331c4464e2af64ba56d65f7abc92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Wed, 4 Dec 2024 00:08:05 +0000 Subject: [PATCH 11/41] addressed more gcc warnings --- examples/allocator.cpp | 12 +- examples/doc-just_error.cpp | 1 + examples/doc-just_stopped.cpp | 1 + examples/sender-demo.cpp | 4 +- examples/stopping.cpp | 2 +- .../execution26/detail/bounded_queue.hpp | 3 +- tests/beman/execution26/CMakeLists.txt | 2 - .../beman/execution26/bounded-queue.test.cpp | 2 +- tests/beman/execution26/exec-connect.test.cpp | 25 ++-- tests/beman/execution26/exec-let.test.cpp | 2 +- .../beman/execution26/exec-snd-expos.test.cpp | 132 +++++++++--------- tests/beman/execution26/exec-split.test.cpp | 5 +- .../beman/execution26/exec-sync-wait.test.cpp | 18 +-- tests/beman/execution26/exec-then.test.cpp | 6 +- .../beman/execution26/exec-when-all.test.cpp | 8 +- .../execution26/include/test/stop_token.hpp | 8 +- 16 files changed, 113 insertions(+), 118 deletions(-) diff --git a/examples/allocator.cpp b/examples/allocator.cpp index 483521c9..92557950 100644 --- a/examples/allocator.cpp +++ b/examples/allocator.cpp @@ -12,7 +12,7 @@ namespace { template struct inline_resource : std::pmr::memory_resource { const char* name; - explicit inline_resource(const char* name) : name(name) {} + explicit inline_resource(const char* nm) : name(nm) {} std::byte buffer[Size]{}; // NOLINT(hicpp-avoid-c-arrays) std::byte* next{+this->buffer}; // NOLINT(hicpp-no-array-decay) @@ -39,13 +39,13 @@ struct allocator_aware_fun { template requires std::same_as, std::remove_cvref_t> - explicit allocator_aware_fun(F&& fun) : fun(std::forward(fun)) {} - allocator_aware_fun(const allocator_aware_fun& other, allocator_type allocator = {}) - : fun(other.fun), allocator(allocator) {} + explicit allocator_aware_fun(F&& fn) : fun(std::forward(fn)) {} + allocator_aware_fun(const allocator_aware_fun& other, allocator_type alloc = {}) + : fun(other.fun), allocator(alloc) {} allocator_aware_fun(allocator_aware_fun&& other) noexcept : fun(std::move(other.fun)), allocator(other.allocator) {} - allocator_aware_fun(allocator_aware_fun&& other, allocator_type allocator) - : fun(std::move(other.fun)), allocator(allocator) {} + allocator_aware_fun(allocator_aware_fun&& other, allocator_type alloc) + : fun(std::move(other.fun)), allocator(alloc) {} template auto operator()(Args&&... args) noexcept { diff --git a/examples/doc-just_error.cpp b/examples/doc-just_error.cpp index 54771202..88a8bc0f 100644 --- a/examples/doc-just_error.cpp +++ b/examples/doc-just_error.cpp @@ -13,5 +13,6 @@ int main() { assert(ec.value() == 17); had_error = true; })); + assert(result); assert(had_error); } \ No newline at end of file diff --git a/examples/doc-just_stopped.cpp b/examples/doc-just_stopped.cpp index a7cc8a77..b2195b58 100644 --- a/examples/doc-just_stopped.cpp +++ b/examples/doc-just_stopped.cpp @@ -11,5 +11,6 @@ int main() { bool stopped{false}; auto result = ex::sync_wait(ex::just_stopped() | ex::upon_stopped([&] { stopped = true; })); + assert(result); assert(stopped); } \ No newline at end of file diff --git a/examples/sender-demo.cpp b/examples/sender-demo.cpp index a2894aa0..38140ab5 100644 --- a/examples/sender-demo.cpp +++ b/examples/sender-demo.cpp @@ -14,8 +14,8 @@ struct just_op_state { std::pmr::string value; template - just_op_state(R&& r, std::pmr::string&& value) - : rec(std::forward(r)), value(std::move(value), ex::get_allocator(ex::get_env(rec))) {} + just_op_state(R&& r, std::pmr::string&& val) + : rec(std::forward(r)), value(std::move(val), ex::get_allocator(ex::get_env(rec))) {} void start() & noexcept { ex::set_value(std::move(rec), std::move(value)); } }; diff --git a/examples/stopping.cpp b/examples/stopping.cpp index d82151f5..deec0d58 100644 --- a/examples/stopping.cpp +++ b/examples/stopping.cpp @@ -22,7 +22,7 @@ namespace { struct env { ex::inplace_stop_token token; - env(ex::inplace_stop_token token) : token(token) {} // NOLINT(hicpp-explicit-conversions) + env(ex::inplace_stop_token tok) : token(tok) {} // NOLINT(hicpp-explicit-conversions) auto query(const ex::get_stop_token_t&) const noexcept { return this->token; } }; diff --git a/include/beman/execution26/detail/bounded_queue.hpp b/include/beman/execution26/detail/bounded_queue.hpp index 632b7bbc..09242a84 100644 --- a/include/beman/execution26/detail/bounded_queue.hpp +++ b/include/beman/execution26/detail/bounded_queue.hpp @@ -424,8 +424,7 @@ auto beman::execution26::bounded_queue::async_pop() -> pop_sender template template auto beman::execution26::bounded_queue::construct(element_t* element, Args&&... args) -> void { - array_allocator_traits::construct( - this->array_allocator, this->get(this->head), this->allocator, ::std::forward(args)...); + array_allocator_traits::construct(this->array_allocator, element, this->allocator, ::std::forward(args)...); } template diff --git a/tests/beman/execution26/CMakeLists.txt b/tests/beman/execution26/CMakeLists.txt index 3b58bcf7..a62eb792 100644 --- a/tests/beman/execution26/CMakeLists.txt +++ b/tests/beman/execution26/CMakeLists.txt @@ -135,7 +135,6 @@ foreach(test ${execution_tests}) add_test(NAME ${TEST_EXE} COMMAND $) endforeach() -if(False) if(NOT PROJECT_IS_TOP_LEVEL) # test if the targets are findable from the build directory # cmake-format: off @@ -158,4 +157,3 @@ if(NOT PROJECT_IS_TOP_LEVEL) ) # cmake-format: on endif() -endif() \ No newline at end of file diff --git a/tests/beman/execution26/bounded-queue.test.cpp b/tests/beman/execution26/bounded-queue.test.cpp index d3184868..2e52508b 100644 --- a/tests/beman/execution26/bounded-queue.test.cpp +++ b/tests/beman/execution26/bounded-queue.test.cpp @@ -203,7 +203,7 @@ auto test_async_push(auto one, auto two, auto three, auto four, auto five) -> vo } template -auto test_async_pop(auto one, auto two, auto three, auto four, auto five) -> void { +auto test_async_pop(auto one, auto two, auto three, auto four, auto) -> void { struct receiver { using receiver_concept = test_std::receiver_t; int& complete; diff --git a/tests/beman/execution26/exec-connect.test.cpp b/tests/beman/execution26/exec-connect.test.cpp index 4b23ec1b..1ef95494 100644 --- a/tests/beman/execution26/exec-connect.test.cpp +++ b/tests/beman/execution26/exec-connect.test.cpp @@ -22,7 +22,7 @@ struct state { std::remove_cvref_t receiver; template - state(int value, R&& r) : value(value), receiver(std::forward(r)) {} + state(int val, R&& r) : value(val), receiver(std::forward(r)) {} state(const state&) = delete; state(state&&) = delete; ~state() = default; @@ -45,7 +45,7 @@ struct rvalue_sender { using sender_concept = test_std::sender_t; int value{}; - explicit rvalue_sender(int value) : value(value) {} + explicit rvalue_sender(int val) : value(val) {} rvalue_sender(const rvalue_sender&) = delete; rvalue_sender(rvalue_sender&&) = default; auto operator=(const rvalue_sender&) -> rvalue_sender& = delete; @@ -67,8 +67,7 @@ struct receiver { int value{}; bool* set_stopped_called{}; - explicit receiver(int value, bool* set_stopped_called = {}) - : value(value), set_stopped_called(set_stopped_called) {} + explicit receiver(int val, bool* called = {}) : value(val), set_stopped_called(called) {} receiver(receiver&&) = default; receiver(const receiver&) = delete; ~receiver() = default; @@ -104,7 +103,7 @@ struct domain_receiver { using receiver_concept = test_std::receiver_t; int value{}; - explicit domain_receiver(int value) : value(value) {} + explicit domain_receiver(int val) : value(val) {} domain_receiver(domain_receiver&&) = default; domain_receiver(const domain_receiver&) = delete; ~domain_receiver() = default; @@ -167,7 +166,7 @@ auto test_operation_state_task() -> void { static_assert( std::same_as<::beman::execution26::detail::connect_awaitable_promise, state_t::promise_type>); static_assert(noexcept(state_t(std::coroutine_handle<>{}))); - static_assert(noexcept(state_t(state_t(std::coroutine_handle<>{})))); + static_assert(noexcept(state_t(std::coroutine_handle<>{}))); state_t state(::std::coroutine_handle<>{}); static_assert(noexcept(state.start())); } @@ -201,7 +200,7 @@ auto test_suspend_complete() -> void { } auto test_connect_awaitable() -> void { - struct awaiter { + struct awaiter_t { ::std::coroutine_handle<>& handle; int& result; @@ -225,7 +224,7 @@ auto test_connect_awaitable() -> void { auto await_resume() -> void {} }; - struct receiver { + struct areceiver { using receiver_concept = test_std::receiver_t; int& iv; @@ -253,7 +252,7 @@ auto test_connect_awaitable() -> void { int iv{}; bool bv{}; - auto op1{test_detail::connect_awaitable(awaiter{handle, result}, receiver{iv, bv})}; + auto op1{test_detail::connect_awaitable(awaiter_t{handle, result}, areceiver{iv, bv})}; ASSERT(handle == std::coroutine_handle<>()); op1.start(); ASSERT(handle != std::coroutine_handle<>()); @@ -269,7 +268,7 @@ auto test_connect_awaitable() -> void { int iv{}; bool bv{}; - auto op1{test_detail::connect_awaitable(awaiter{handle, result}, receiver{iv, bv})}; + auto op1{test_detail::connect_awaitable(awaiter_t{handle, result}, areceiver{iv, bv})}; ASSERT(handle == std::coroutine_handle<>()); op1.start(); ASSERT(handle != std::coroutine_handle<>()); @@ -285,7 +284,7 @@ auto test_connect_awaitable() -> void { int iv{}; bool bv{}; - auto op1{test_detail::connect_awaitable(void_awaiter{handle}, receiver{iv, bv})}; + auto op1{test_detail::connect_awaitable(void_awaiter{handle}, areceiver{iv, bv})}; ASSERT(handle == std::coroutine_handle<>()); op1.start(); ASSERT(handle != std::coroutine_handle<>()); @@ -296,7 +295,7 @@ auto test_connect_awaitable() -> void { } auto test_connect_with_awaiter() -> void { - struct awaiter { + struct awaiter_t { ::std::coroutine_handle<>& handle; auto await_ready() -> bool { return {}; } auto await_suspend(std::coroutine_handle<> h) -> void { this->handle = h; } @@ -312,7 +311,7 @@ auto test_connect_with_awaiter() -> void { std::coroutine_handle<> handle{}; bool result{}; - auto op{test_std::connect(awaiter{handle}, receiver{result})}; + auto op{test_std::connect(awaiter_t{handle}, receiver{result})}; ASSERT(handle == std::coroutine_handle{}); test_std::start(op); ASSERT(handle != std::coroutine_handle{}); diff --git a/tests/beman/execution26/exec-let.test.cpp b/tests/beman/execution26/exec-let.test.cpp index 6d890357..6610d9d3 100644 --- a/tests/beman/execution26/exec-let.test.cpp +++ b/tests/beman/execution26/exec-let.test.cpp @@ -91,7 +91,7 @@ namespace ex = test_std; struct fun { std::pmr::polymorphic_allocator<> allocator{}; fun() {} - explicit fun(std::pmr::polymorphic_allocator<> allocator) : allocator(allocator) {} + explicit fun(std::pmr::polymorphic_allocator<> alloc) : allocator(alloc) {} auto operator()(std::span s) noexcept { return ex::just(std::pmr::vector(s.begin(), s.end(), this->allocator)); } diff --git a/tests/beman/execution26/exec-snd-expos.test.cpp b/tests/beman/execution26/exec-snd-expos.test.cpp index da4c7ff7..d474ab4a 100644 --- a/tests/beman/execution26/exec-snd-expos.test.cpp +++ b/tests/beman/execution26/exec-snd-expos.test.cpp @@ -99,7 +99,7 @@ template struct operation_state : test_detail::immovable { using operation_state_concept = test_std::operation_state_t; int* counter; - explicit operation_state(int* counter) : counter(counter) {} + explicit operation_state(int* cntr) : counter(cntr) {} auto start() & noexcept -> void { ++*counter; } }; @@ -499,39 +499,39 @@ auto test_get_domain_late() -> void { } auto test_default_impls_get_attrs() -> void { - struct env { + struct aenv { int value; }; struct child1 { - auto get_env() const noexcept { return env{1}; } + auto get_env() const noexcept { return aenv{1}; } }; struct child2 { - auto get_env() const noexcept { return env{2}; } + auto get_env() const noexcept { return aenv{2}; } }; static_assert(noexcept(test_detail::default_impls::get_attrs(0, child1{}))); static_assert( - std::same_as, decltype(test_detail::default_impls::get_attrs(0, child1{}))>); + std::same_as, decltype(test_detail::default_impls::get_attrs(0, child1{}))>); // static_assert(std::same_as); } auto test_default_impls_get_env() -> void { - struct env { + struct genv { int value; }; - struct receiver { - auto get_env() const noexcept { return env{1}; } + struct greceiver { + auto get_env() const noexcept { return genv{1}; } }; int arg{}; - static_assert(noexcept(test_detail::default_impls::get_env(0, arg, receiver{}))); + static_assert(noexcept(test_detail::default_impls::get_env(0, arg, greceiver{}))); static_assert( - std::same_as, decltype(test_detail::default_impls::get_env(0, arg, receiver{}))>); + std::same_as, decltype(test_detail::default_impls::get_env(0, arg, greceiver{}))>); } auto test_default_impls_get_state() -> void { - struct tag { + struct gtag { static auto name() { return "test_default_impls_get_state"; } }; struct data { @@ -539,30 +539,30 @@ auto test_default_impls_get_state() -> void { int v2{}; auto operator==(const data&) const -> bool = default; }; - struct sender0 { - tag t{}; + struct gsender0 { + gtag t{}; data d{1, 2}; }; - struct sender1 { - tag t{}; + struct gsender1 { + gtag t{}; data d{1, 2}; int i1{}; }; - struct sender2 { - tag t{}; + struct gsender2 { + gtag t{}; data d{1, 2}; int i1{}; int i2{}; }; - struct sender3 { - tag t{}; + struct gsender3 { + gtag t{}; data d{1, 2}; int i1{}; int i2{}; int i3{}; }; - struct sender4 { - tag t{}; + struct gsender4 { + gtag t{}; data d{1, 2}; int i1{}; int i2{}; @@ -571,20 +571,20 @@ auto test_default_impls_get_state() -> void { }; struct receiver {}; - sender0 s{}; - const sender0 cs{}; + gsender0 s{}; + const gsender0 cs{}; receiver r{}; - static_assert(noexcept(test_detail::default_impls::get_state(sender0{}, r))); - static_assert(std::same_as); - ASSERT((data{1, 2}) == test_detail::default_impls::get_state(sender0{}, r)); - static_assert(std::same_as); - ASSERT((data{1, 2}) == test_detail::default_impls::get_state(sender1{}, r)); - static_assert(std::same_as); - ASSERT((data{1, 2}) == test_detail::default_impls::get_state(sender2{}, r)); - static_assert(std::same_as); - ASSERT((data{1, 2}) == test_detail::default_impls::get_state(sender3{}, r)); - static_assert(std::same_as); - ASSERT((data{1, 2}) == test_detail::default_impls::get_state(sender4{}, r)); + static_assert(noexcept(test_detail::default_impls::get_state(gsender0{}, r))); + static_assert(std::same_as); + ASSERT((data{1, 2}) == test_detail::default_impls::get_state(gsender0{}, r)); + static_assert(std::same_as); + ASSERT((data{1, 2}) == test_detail::default_impls::get_state(gsender1{}, r)); + static_assert(std::same_as); + ASSERT((data{1, 2}) == test_detail::default_impls::get_state(gsender2{}, r)); + static_assert(std::same_as); + ASSERT((data{1, 2}) == test_detail::default_impls::get_state(gsender3{}, r)); + static_assert(std::same_as); + ASSERT((data{1, 2}) == test_detail::default_impls::get_state(gsender4{}, r)); static_assert(std::same_as); static_assert(std::same_as); } @@ -616,15 +616,15 @@ auto test_default_impls_start() -> void { template auto test_default_impls_complete(Impls) -> void { struct arg {}; - struct receiver { + struct dreceiver { bool& called; }; struct state {}; bool called{false}; - auto non_tag = [](receiver&&, int) {}; - auto tag = [](receiver&& r, int, arg) { r.called = true; }; - receiver r{called}; + auto non_tag = [](dreceiver&&, int) {}; + auto tag = [](dreceiver&& r, int, arg) { r.called = true; }; + dreceiver r{called}; state s{}; static_assert(not requires { Impls::complete(::std::integral_constant{}, s, r, non_tag, 0, arg{}); }); @@ -645,39 +645,39 @@ auto test_default_impls() -> void { } auto test_impls_for() -> void { - struct tag { + struct itag { static auto name() { return "test_impls_for"; } }; - static_assert(std::derived_from, test_detail::default_impls>); + static_assert(std::derived_from, test_detail::default_impls>); } auto test_state_type() -> void { - struct tag { + struct stag { static auto name() { return "test_state_type"; } }; struct state {}; struct sender { - tag t; + stag t; state s; }; - struct receiver {}; + struct sreceiver {}; - static_assert(std::same_as>); + static_assert(std::same_as>); } auto test_basic_state() -> void { - struct tag { + struct stag { static auto name() { return "test_basic_state"; } }; struct data {}; struct sender { - tag t; + stag t; data d; }; - struct receiver {}; + struct sreceiver {}; - test_detail::basic_state state(sender{}, receiver{}); + test_detail::basic_state state(sender{}, sreceiver{}); } auto test_indices_for() -> void { @@ -704,36 +704,35 @@ auto test_valid_specialization() -> void { auto test_env_type() -> void { using index = std::integral_constant; - struct tag { + struct ttag { static auto name() { return "test_env_type"; } }; struct data {}; - struct env {}; struct sender { - tag t; + ttag t; data d; }; struct sender_with_env { - tag t; + ttag t; data d; auto get_env() const noexcept -> env { return {}; } }; - struct receiver {}; + struct treceiver {}; struct receiver_with_env { auto get_env() const noexcept -> env { return {}; } }; static_assert( - std::same_as, test_detail::env_type>); + std::same_as, test_detail::env_type>); static_assert(std::same_as, - test_detail::env_type>); + test_detail::env_type>); static_assert(std::same_as, test_detail::env_type>); } template auto test_basic_receiver() -> void { using index = std::integral_constant; - struct tag { + struct btag { static auto name() { return "test_basic_receiver"; } }; struct data {}; @@ -742,10 +741,10 @@ auto test_basic_receiver() -> void { auto operator==(const err&) const -> bool = default; }; struct sender { - tag t{}; + btag t{}; data d{}; }; - struct receiver { + struct breceiver { T value{}; err error{}; bool stopped{}; @@ -757,14 +756,14 @@ auto test_basic_receiver() -> void { struct unstoppable_receiver { T value; }; - using basic_receiver = test_detail::basic_receiver; + using basic_receiver = test_detail::basic_receiver; static_assert(test_std::receiver); - static_assert(std::same_as); - static_assert(std::same_as, typename basic_receiver::state_t>); + static_assert(std::same_as); + static_assert(std::same_as, typename basic_receiver::state_t>); ASSERT(&basic_receiver::complete == &test_detail::default_impls::complete); { - test_detail::basic_state op(sender{}, receiver{}); + test_detail::basic_state op(sender{}, breceiver{}); basic_receiver br{&op}; static_assert(not requires { test_std::set_value(std::move(br)); }); static_assert(not requires { test_std::set_value(std::move(br), 42, 1); }); @@ -775,7 +774,7 @@ auto test_basic_receiver() -> void { ASSERT(op.receiver.value == 42); } { - test_detail::basic_state op(sender{}, receiver{}); + test_detail::basic_state op(sender{}, breceiver{}); basic_receiver br{&op}; static_assert(not requires { test_std::set_error(std::move(br)); }); static_assert(not requires { test_std::set_error(std::move(br), 0); }); @@ -786,7 +785,7 @@ auto test_basic_receiver() -> void { ASSERT(op.receiver.error == err{42}); } { - test_detail::basic_state op(sender{}, receiver{}); + test_detail::basic_state op(sender{}, breceiver{}); basic_receiver br{&op}; static_assert(requires { test_std::set_stopped(std::move(br)); }); static_assert(noexcept(test_std::set_stopped(std::move(br)))); @@ -815,7 +814,7 @@ auto test_completion_tag() -> void { auto test_product_type() -> void { struct nm { int value{}; - explicit nm(int value) : value(value) {} + explicit nm(int val) : value(val) {} nm(nm&&) = delete; nm(const nm&) = delete; ~nm() = default; @@ -995,7 +994,6 @@ auto test_basic_operation() -> void { auto test_completion_signatures_for() -> void { struct arg {}; - struct env {}; struct bad_env {}; struct sender { using sender_concept = test_std::sender_t; @@ -1059,8 +1057,6 @@ struct tuple_element { } // namespace std namespace { auto test_basic_sender() -> void { - struct env {}; - { auto&& [a, b, c] = tagged_sender{basic_sender_tag{}, data{}, sender0{}}; test::use(a); diff --git a/tests/beman/execution26/exec-split.test.cpp b/tests/beman/execution26/exec-split.test.cpp index a3f1fdfa..0962061c 100644 --- a/tests/beman/execution26/exec-split.test.cpp +++ b/tests/beman/execution26/exec-split.test.cpp @@ -143,9 +143,10 @@ void test_completion_sigs_and_sync_wait_on_split() { auto just = beman::execution26::just(NonCopyable{}); auto split = beman::execution26::split(std::move(just)); using split_sender = std::decay_t; - struct empty_env {}; + struct local_empty_env {}; using expected_value_completions = type_list; - using value_completions = beman::execution26::value_types_of_t; + using value_completions = + beman::execution26::value_types_of_t; static_assert(std::same_as); auto eat_completion = beman::execution26::then(split, [&](const NonCopyable&) {}); diff --git a/tests/beman/execution26/exec-sync-wait.test.cpp b/tests/beman/execution26/exec-sync-wait.test.cpp index e36f8e6c..b1691f75 100644 --- a/tests/beman/execution26/exec-sync-wait.test.cpp +++ b/tests/beman/execution26/exec-sync-wait.test.cpp @@ -136,11 +136,11 @@ auto test_sync_wait_state() -> void { auto test_sync_wait_receiver() -> void { { - using sender = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{})); - test_detail::sync_wait_state state{}; + using sender_t = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{})); + test_detail::sync_wait_state state{}; ASSERT(not state.result); ASSERT(not state.error); - test_std::set_value(test_detail::sync_wait_receiver{&state}, arg<0>{2}, arg<1>{3}, arg<2>{5}); + test_std::set_value(test_detail::sync_wait_receiver{&state}, arg<0>{2}, arg<1>{3}, arg<2>{5}); ASSERT(state.result); ASSERT(not state.error); ASSERT(*state.result == (std::tuple{arg<0>{2}, arg<1>{3}, arg<2>{5}})); @@ -163,11 +163,11 @@ auto test_sync_wait_receiver() -> void { } } { - using sender = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{})); - test_detail::sync_wait_state state{}; + using sender_t = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{})); + test_detail::sync_wait_state state{}; ASSERT(not state.result); ASSERT(not state.error); - test_std::set_error(test_detail::sync_wait_receiver{&state}, std::make_exception_ptr(error{17})); + test_std::set_error(test_detail::sync_wait_receiver{&state}, std::make_exception_ptr(error{17})); ASSERT(not state.result); ASSERT(state.error); try { @@ -180,11 +180,11 @@ auto test_sync_wait_receiver() -> void { } } { - using sender = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{})); - test_detail::sync_wait_state state{}; + using sender_t = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{})); + test_detail::sync_wait_state state{}; ASSERT(not state.result); ASSERT(not state.error); - test_std::set_stopped(test_detail::sync_wait_receiver{&state}); + test_std::set_stopped(test_detail::sync_wait_receiver{&state}); ASSERT(not state.result); ASSERT(not state.error); } diff --git a/tests/beman/execution26/exec-then.test.cpp b/tests/beman/execution26/exec-then.test.cpp index 5a0ce64b..1a02e58b 100644 --- a/tests/beman/execution26/exec-then.test.cpp +++ b/tests/beman/execution26/exec-then.test.cpp @@ -187,11 +187,11 @@ struct allocator_fun { std::pmr::polymorphic_allocator<> alloc; std::byte* data{nullptr}; - explicit allocator_fun(std::pmr::polymorphic_allocator<> alloc) : alloc(alloc), data(alloc.allocate(1)) {} + explicit allocator_fun(std::pmr::polymorphic_allocator<> all) : alloc(all), data(alloc.allocate(1)) {} allocator_fun(const allocator_fun&, std::pmr::polymorphic_allocator<> = {}) {} allocator_fun(allocator_fun&& other) noexcept : alloc(other.alloc), data(std::exchange(other.data, nullptr)) {} - allocator_fun(allocator_fun&& other, std::pmr::polymorphic_allocator<> alloc) - : alloc(alloc), data(alloc == other.alloc ? std::exchange(other.data, nullptr) : alloc.allocate(1)) {} + allocator_fun(allocator_fun&& other, std::pmr::polymorphic_allocator<> all) + : alloc(all), data(all == other.alloc ? std::exchange(other.data, nullptr) : alloc.allocate(1)) {} ~allocator_fun() { if (this->data) this->alloc.deallocate(this->data, 1u); diff --git a/tests/beman/execution26/exec-when-all.test.cpp b/tests/beman/execution26/exec-when-all.test.cpp index 0bc0d872..d5709fb8 100644 --- a/tests/beman/execution26/exec-when-all.test.cpp +++ b/tests/beman/execution26/exec-when-all.test.cpp @@ -58,7 +58,7 @@ struct await_cancel { using operation_state_concept = test_std::operation_state_t; struct callback { Receiver* receiver; - explicit callback(Receiver* receiver) : receiver(receiver) {} + explicit callback(Receiver* rcvr) : receiver(rcvr) {} auto operator()() const noexcept -> void { test_std::set_stopped(std::move(*this->receiver)); } }; @@ -116,9 +116,9 @@ struct test_sender { std::remove_cvref_t receiver; decltype(test_std::connect(std::declval(), std::declval>())) inner_state; template - state(S&& sender, auto&& expect, R&& receiver) - : expect(expect), - receiver(std::forward(receiver)), + state(S&& sender, auto&& exp, R&& rcvr) + : expect(exp), + receiver(std::forward(rcvr)), inner_state(test_std::connect(std::forward(sender), upstream{this->expect, this->receiver})) {} auto start() & noexcept -> void { test_std::start(this->inner_state); } diff --git a/tests/beman/execution26/include/test/stop_token.hpp b/tests/beman/execution26/include/test/stop_token.hpp index 1e427621..0113a082 100644 --- a/tests/beman/execution26/include/test/stop_token.hpp +++ b/tests/beman/execution26/include/test/stop_token.hpp @@ -84,7 +84,7 @@ inline auto test::stop_callback(const Token& token, Stop stop) -> void { struct Callback { Data* data; - explicit Callback(Data* data) : data(data) {} + explicit Callback(Data* dat) : data(dat) {} auto operator()() { ++this->data->count; this->data->stop_requested = this->data->token.stop_requested(); @@ -124,7 +124,7 @@ auto test::stop_callback_dtor_deregisters(const Token& token, Stop stop) -> void struct Callback { bool* ptr; - explicit Callback(bool* ptr) : ptr(ptr) {} + explicit Callback(bool* pt) : ptr(pt) {} auto operator()() { *this->ptr = true; } }; @@ -166,7 +166,7 @@ inline auto test::stop_callback_dtor_other_thread(const Token& token, Stop stop) }; struct Callback { Data* data; - explicit Callback(Data* data) : data(data) {} + explicit Callback(Data* dat) : data(dat) {} auto operator()() -> void { using namespace ::std::chrono_literals; { @@ -221,7 +221,7 @@ inline auto test::stop_callback_dtor_same_thread(Token token, Stop stop) -> void }; struct Callback { ::std::unique_ptr* self; - explicit Callback(::std::unique_ptr* self) : self(self) {} + explicit Callback(::std::unique_ptr* slf) : self(slf) {} auto operator()() { this->self->reset(); } }; struct Object : Base { From 7c2d57eb8dbaf8e7135f9e834ca000fd4d652b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Wed, 4 Dec 2024 00:25:35 +0000 Subject: [PATCH 12/41] a few more warnings addressed --- .../beman/execution26/detail/bounded_queue.hpp | 15 ++++++--------- tests/beman/execution26/bounded-queue.test.cpp | 12 +++++++----- tests/beman/execution26/exec-connect.test.cpp | 4 ++-- tests/beman/execution26/exec-snd-expos.test.cpp | 4 ++-- tests/beman/execution26/exec-sync-wait.test.cpp | 6 +++--- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/include/beman/execution26/detail/bounded_queue.hpp b/include/beman/execution26/detail/bounded_queue.hpp index 09242a84..20efe451 100644 --- a/include/beman/execution26/detail/bounded_queue.hpp +++ b/include/beman/execution26/detail/bounded_queue.hpp @@ -42,7 +42,12 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl static_assert(::std::same_as); explicit bounded_queue(::std::size_t max, Allocator allocator = {}); - ~bounded_queue(); + ~bounded_queue() { + for (; this->tail != this->head; ++this->tail) { + this->destroy(this->get(this->tail)); + } + array_allocator_traits::deallocate(this->array_allocator, this->elements, this->max); + } auto is_closed() const noexcept -> bool; auto close() noexcept -> void; @@ -282,14 +287,6 @@ beman::execution26::bounded_queue::bounded_queue(::std::size_t mx, max(mx), elements(array_allocator_traits::allocate(this->array_allocator, this->max)) {} -template -beman::execution26::bounded_queue::~bounded_queue() { - for (; this->tail != this->head; ++this->tail) { - this->destroy(this->get(this->tail)); - } - array_allocator_traits::deallocate(this->array_allocator, this->elements, this->max); -} - template auto beman::execution26::bounded_queue::is_closed() const noexcept -> bool { std::lock_guard cerberos(this->mutex); diff --git a/tests/beman/execution26/bounded-queue.test.cpp b/tests/beman/execution26/bounded-queue.test.cpp index 2e52508b..6ee563c4 100644 --- a/tests/beman/execution26/bounded-queue.test.cpp +++ b/tests/beman/execution26/bounded-queue.test.cpp @@ -154,13 +154,15 @@ auto test_async_push(auto one, auto two, auto three, auto four, auto five) -> vo struct receiver { using receiver_concept = test_std::receiver_t; int& complete; - auto set_value() && noexcept -> void { this->complete = 1; } + auto set_value() && noexcept -> void { + receiver_concept c{}; + test::use(c); + this->complete = 1; + } auto set_error(test_std::conqueue_errc) && noexcept -> void { this->complete = 2; } auto set_error(std::exception_ptr) && noexcept -> void { this->complete = 3; } auto set_stopped() && noexcept -> void { this->complete = 4; } }; - typename receiver::receiver_concept c{}; - test::use(c); static_assert(test_std::receiver); test_std::bounded_queue queue(2); @@ -209,6 +211,8 @@ auto test_async_pop(auto one, auto two, auto three, auto four, auto) -> void { int& complete; std::vector& vals; auto set_value(T val) && noexcept -> void { + typename receiver::receiver_concept c{}; + test::use(c); this->complete = 1; vals.push_back(val); } @@ -216,8 +220,6 @@ auto test_async_pop(auto one, auto two, auto three, auto four, auto) -> void { auto set_error(std::exception_ptr) && noexcept -> void { this->complete = 3; } auto set_stopped() && noexcept -> void { this->complete = 4; } }; - typename receiver::receiver_concept c{}; - test::use(c); static_assert(test_std::receiver); test_std::bounded_queue queue(2); diff --git a/tests/beman/execution26/exec-connect.test.cpp b/tests/beman/execution26/exec-connect.test.cpp index 1ef95494..aaedde78 100644 --- a/tests/beman/execution26/exec-connect.test.cpp +++ b/tests/beman/execution26/exec-connect.test.cpp @@ -301,7 +301,7 @@ auto test_connect_with_awaiter() -> void { auto await_suspend(std::coroutine_handle<> h) -> void { this->handle = h; } auto await_resume() -> int { return 17; } }; - struct receiver { + struct areceiver { using receiver_concept = test_std::receiver_t; bool& result; auto set_value(int i) && noexcept -> void { this->result = i == 17; } @@ -311,7 +311,7 @@ auto test_connect_with_awaiter() -> void { std::coroutine_handle<> handle{}; bool result{}; - auto op{test_std::connect(awaiter_t{handle}, receiver{result})}; + auto op{test_std::connect(awaiter_t{handle}, areceiver{result})}; ASSERT(handle == std::coroutine_handle{}); test_std::start(op); ASSERT(handle != std::coroutine_handle{}); diff --git a/tests/beman/execution26/exec-snd-expos.test.cpp b/tests/beman/execution26/exec-snd-expos.test.cpp index d474ab4a..94d54dbf 100644 --- a/tests/beman/execution26/exec-snd-expos.test.cpp +++ b/tests/beman/execution26/exec-snd-expos.test.cpp @@ -569,11 +569,11 @@ auto test_default_impls_get_state() -> void { int i3{}; int i4{}; }; - struct receiver {}; + struct greceiver {}; gsender0 s{}; const gsender0 cs{}; - receiver r{}; + greceiver r{}; static_assert(noexcept(test_detail::default_impls::get_state(gsender0{}, r))); static_assert(std::same_as); ASSERT((data{1, 2}) == test_detail::default_impls::get_state(gsender0{}, r)); diff --git a/tests/beman/execution26/exec-sync-wait.test.cpp b/tests/beman/execution26/exec-sync-wait.test.cpp index b1691f75..8b73d87a 100644 --- a/tests/beman/execution26/exec-sync-wait.test.cpp +++ b/tests/beman/execution26/exec-sync-wait.test.cpp @@ -146,11 +146,11 @@ auto test_sync_wait_receiver() -> void { ASSERT(*state.result == (std::tuple{arg<0>{2}, arg<1>{3}, arg<2>{5}})); } { - using sender = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{})); - test_detail::sync_wait_state state{}; + using sender_t = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{})); + test_detail::sync_wait_state state{}; ASSERT(not state.result); ASSERT(not state.error); - test_std::set_error(test_detail::sync_wait_receiver{&state}, error{17}); + test_std::set_error(test_detail::sync_wait_receiver{&state}, error{17}); ASSERT(not state.result); ASSERT(state.error); try { From 0ec7565e2229bfe8ac89329bdf79b06f63608db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Wed, 4 Dec 2024 00:40:23 +0000 Subject: [PATCH 13/41] addressed a remaining warning --- tests/beman/execution26/bounded-queue.test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/beman/execution26/bounded-queue.test.cpp b/tests/beman/execution26/bounded-queue.test.cpp index 6ee563c4..504a9b65 100644 --- a/tests/beman/execution26/bounded-queue.test.cpp +++ b/tests/beman/execution26/bounded-queue.test.cpp @@ -211,7 +211,7 @@ auto test_async_pop(auto one, auto two, auto three, auto four, auto) -> void { int& complete; std::vector& vals; auto set_value(T val) && noexcept -> void { - typename receiver::receiver_concept c{}; + receiver_concept c{}; test::use(c); this->complete = 1; vals.push_back(val); From d9cf895032b5c4d416a619739442b91b010d9c00 Mon Sep 17 00:00:00 2001 From: Claus Klein Date: Wed, 4 Dec 2024 23:56:42 +0100 Subject: [PATCH 14/41] Build with TSAN and clang-tidy (#100) --- CMakeLists.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c6874da7..28b6a427 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,21 +42,21 @@ if(CMAKE_BUILD_TYPE STREQUAL Debug) PREFIX ${PROJECT_NAME} ENABLE_CACHE - # NO! # ENABLE_CLANG_TIDY + ENABLE_CLANG_TIDY # NO! ENABLE_VS_ANALYSIS # ENABLE_INTERPROCEDURAL_OPTIMIZATION # ENABLE_NATIVE_OPTIMIZATION # ENABLE_DOXYGEN # ENABLE_COVERAGE - ENABLE_SANITIZER_ADDRESS - ENABLE_SANITIZER_UNDEFINED - # TODO: ENABLE_SANITIZER_THREAD + # ENABLE_SANITIZER_ADDRESS + # ENABLE_SANITIZER_UNDEFINED + ENABLE_SANITIZER_THREAD # FIXME: on Linux only with clang++? ENABLE_SANITIZER_MEMORY - ENABLE_SANITIZER_POINTER_COMPARE - ENABLE_SANITIZER_POINTER_SUBTRACT - ENABLE_CONTROL_FLOW_PROTECTION - ENABLE_STACK_PROTECTION - ENABLE_OVERFLOW_PROTECTION + # ENABLE_SANITIZER_POINTER_COMPARE + # ENABLE_SANITIZER_POINTER_SUBTRACT + # ENABLE_CONTROL_FLOW_PROTECTION + # FIXME: on Linux only with clang++? ENABLE_STACK_PROTECTION + # ENABLE_OVERFLOW_PROTECTION # ENABLE_ELF_PROTECTION # ENABLE_RUNTIME_SYMBOLS_RESOLUTION # ENABLE_COMPILE_COMMANDS_SYMLINK From 683fc66aeeb582be5db60d962a555b4b9850db34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Wed, 4 Dec 2024 23:20:03 +0000 Subject: [PATCH 15/41] fixed new clang-tidy warnings --- include/beman/execution26/detail/as_awaitable.hpp | 7 +++---- include/beman/execution26/detail/conqueue_errc.hpp | 3 ++- .../beman/execution26/detail/inplace_stop_source.hpp | 2 +- include/beman/execution26/detail/intrusive_queue.hpp | 8 ++++++++ include/beman/execution26/detail/run_loop.hpp | 1 - tests/beman/execution26/intrusive-queue.test.cpp | 11 ++++++----- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/include/beman/execution26/detail/as_awaitable.hpp b/include/beman/execution26/detail/as_awaitable.hpp index 4247a60a..d0f2af31 100644 --- a/include/beman/execution26/detail/as_awaitable.hpp +++ b/include/beman/execution26/detail/as_awaitable.hpp @@ -29,10 +29,9 @@ struct as_awaitable_t { Promise>, "as_awaitable must return an awaitable"); return ::std::forward(expr).as_awaitable(promise); - } else if constexpr (::beman::execution26::detail:: - is_awaitable) { - return ::std::forward(expr); - } else if constexpr (::beman::execution26::detail::awaitable_sender) { + } else if constexpr (!::beman::execution26::detail:: + is_awaitable && + ::beman::execution26::detail::awaitable_sender) { return ::beman::execution26::detail::sender_awaitable{::std::forward(expr), promise}; } else { return ::std::forward(expr); diff --git a/include/beman/execution26/detail/conqueue_errc.hpp b/include/beman/execution26/detail/conqueue_errc.hpp index aac0859f..c9b4ab8d 100644 --- a/include/beman/execution26/detail/conqueue_errc.hpp +++ b/include/beman/execution26/detail/conqueue_errc.hpp @@ -7,11 +7,12 @@ #include #include #include +#include // ---------------------------------------------------------------------------- namespace beman::execution26 { -enum class conqueue_errc { empty, full, closed, busy }; +enum class conqueue_errc : ::std::uint8_t { empty, full, closed, busy }; inline auto conqueue_category() noexcept -> const ::std::error_category&; inline auto make_error_code(::beman::execution26::conqueue_errc) noexcept -> ::std::error_code; diff --git a/include/beman/execution26/detail/inplace_stop_source.hpp b/include/beman/execution26/detail/inplace_stop_source.hpp index da4d6c4a..77cfbfd3 100644 --- a/include/beman/execution26/detail/inplace_stop_source.hpp +++ b/include/beman/execution26/detail/inplace_stop_source.hpp @@ -86,7 +86,7 @@ class beman::execution26::inplace_stop_callback final inplace_stop_callback(::beman::execution26::inplace_stop_token, Init&&); inplace_stop_callback(const inplace_stop_callback&) = delete; inplace_stop_callback(inplace_stop_callback&&) = delete; - ~inplace_stop_callback() { + ~inplace_stop_callback() override { if (this->source) { this->source->deregister(this); } diff --git a/include/beman/execution26/detail/intrusive_queue.hpp b/include/beman/execution26/detail/intrusive_queue.hpp index 187eb1d9..8c02fac2 100644 --- a/include/beman/execution26/detail/intrusive_queue.hpp +++ b/include/beman/execution26/detail/intrusive_queue.hpp @@ -57,6 +57,14 @@ struct intrusive_queue { //! The original queue `other` will be empty after this operation. intrusive_queue(intrusive_queue&& other) noexcept : head(::std::exchange(other.head, nullptr)), tail(::std::exchange(other.tail, nullptr)) {} + intrusive_queue(const intrusive_queue&) = delete; + auto operator=(intrusive_queue&& other) noexcept -> intrusive_queue& { + this->head(::std::exchange(other.head, nullptr)); + this->tail(::std::exchange(other.tail, nullptr)); + return *this; + } + auto operator=(const intrusive_queue&) -> intrusive_queue& = delete; + ~intrusive_queue() = default; //! @brief Push the node `n` to the end of the queue //! @details //! The "next" pointer of the new node is set to `nullptr`. diff --git a/include/beman/execution26/detail/run_loop.hpp b/include/beman/execution26/detail/run_loop.hpp index d6ebb708..af65d20e 100644 --- a/include/beman/execution26/detail/run_loop.hpp +++ b/include/beman/execution26/detail/run_loop.hpp @@ -39,7 +39,6 @@ class run_loop { }; struct opstate_base : ::beman::execution26::detail::virtual_immovable { - virtual ~opstate_base() = default; opstate_base* next{}; virtual auto execute() noexcept -> void = 0; }; diff --git a/tests/beman/execution26/intrusive-queue.test.cpp b/tests/beman/execution26/intrusive-queue.test.cpp index d109dec8..e3319cc2 100644 --- a/tests/beman/execution26/intrusive-queue.test.cpp +++ b/tests/beman/execution26/intrusive-queue.test.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include +#include #include // ---------------------------------------------------------------------------- @@ -15,22 +16,22 @@ struct node { TEST(intrusive_queue) { test_detail::intrusive_queue<&node::n_> queue; - node n[]{{1}, {2}, {3}, {4}, {5}}; + ::std::array n{node{1}, node{2}, node{3}, node{4}, node{5}}; ASSERT(queue.empty()); - queue.push(n + 0); + queue.push(&n[0]); ASSERT(not queue.empty()); node* n0{queue.pop()}; - ASSERT(n0 == n + 0); + ASSERT(n0 == &n[0]); ASSERT(n0->n_ == nullptr); ASSERT(queue.empty()); for (int i = 0; i != 5; ++i) { - queue.push(n + i); + queue.push(&n[i]); } for (int i = 0; i != 5; ++i) { ASSERT(not queue.empty()); - ASSERT(queue.pop() == n + i); + ASSERT(queue.pop() == &n[i]); } ASSERT(queue.empty()); } From 8cb53dd0cec424879b2c0912716281d2c409c6a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Wed, 4 Dec 2024 23:47:00 +0000 Subject: [PATCH 16/41] more warnings --- .../execution26/detail/bounded_queue.hpp | 20 +++++++--- tests/beman/execution26/CMakeLists.txt | 2 + .../beman/execution26/bounded-queue.test.cpp | 39 ++++++++++++------- .../execution26/intrusive-queue.test.cpp | 6 +-- 4 files changed, 44 insertions(+), 23 deletions(-) diff --git a/include/beman/execution26/detail/bounded_queue.hpp b/include/beman/execution26/detail/bounded_queue.hpp index 20efe451..c4857ca1 100644 --- a/include/beman/execution26/detail/bounded_queue.hpp +++ b/include/beman/execution26/detail/bounded_queue.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -42,12 +43,16 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl static_assert(::std::same_as); explicit bounded_queue(::std::size_t max, Allocator allocator = {}); + bounded_queue(bounded_queue&&) = delete; + bounded_queue(const bounded_queue&) = delete; ~bounded_queue() { for (; this->tail != this->head; ++this->tail) { this->destroy(this->get(this->tail)); } array_allocator_traits::deallocate(this->array_allocator, this->elements, this->max); } + auto operator=(bounded_queue&&) -> bounded_queue& = delete; + auto operator=(const bounded_queue&) -> bounded_queue& = delete; auto is_closed() const noexcept -> bool; auto close() noexcept -> void; @@ -71,8 +76,8 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl value_type value; push_base* next{}; - template - push_base(Val&& val) : value(::std::forward(val)) {} + template <::beman::execution26::detail::decayed_same_as Val> + explicit push_base(Val&& val) : value(::std::forward(val)) {} virtual auto complete() -> void = 0; virtual auto complete(::beman::execution26::conqueue_errc) -> void = 0; virtual auto complete(::std::exception_ptr) -> void = 0; @@ -90,12 +95,17 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl struct blocking_pop_state; union element_t { - element_t() {} + element_t() = default; + element_t(element_t&&) = delete; + element_t(const element_t&) = delete; template - element_t(allocator_type& alloc, Args&&... args) { + explicit element_t(allocator_type& alloc, Args&&... args) { allocator_traits::construct(alloc, &value, ::std::forward(args)...); } - ~element_t() {} + ~element_t() {}; + auto operator=(element_t&&) -> element_t& = delete; + auto operator=(const element_t&) -> element_t& = delete; + value_type value; }; diff --git a/tests/beman/execution26/CMakeLists.txt b/tests/beman/execution26/CMakeLists.txt index a62eb792..2ddb2dc5 100644 --- a/tests/beman/execution26/CMakeLists.txt +++ b/tests/beman/execution26/CMakeLists.txt @@ -135,6 +135,7 @@ foreach(test ${execution_tests}) add_test(NAME ${TEST_EXE} COMMAND $) endforeach() +if(False) if(NOT PROJECT_IS_TOP_LEVEL) # test if the targets are findable from the build directory # cmake-format: off @@ -157,3 +158,4 @@ if(NOT PROJECT_IS_TOP_LEVEL) ) # cmake-format: on endif() +endif() diff --git a/tests/beman/execution26/bounded-queue.test.cpp b/tests/beman/execution26/bounded-queue.test.cpp index 504a9b65..d13ee447 100644 --- a/tests/beman/execution26/bounded-queue.test.cpp +++ b/tests/beman/execution26/bounded-queue.test.cpp @@ -50,18 +50,21 @@ auto test_push(const auto one, auto two, const auto three, auto four, auto five) ASSERT(rc2 == true); queue.close(); + bool not_reached{true}; try { queue.push(five); - ASSERT(false); + not_reached = false; } catch (const test_std::conqueue_error& error) { ASSERT(error.code().value() == static_cast(test_std::conqueue_errc::closed)); } + ASSERT(not_reached); try { queue.push(std::move(five)); - ASSERT(false); + not_reached = false; } catch (const test_std::conqueue_error& error) { ASSERT(error.code().value() == static_cast(test_std::conqueue_errc::closed)); } + ASSERT(not_reached); std::error_code ec0{}; ASSERT(queue.push(five, ec0) == false); ASSERT(ec0.value() == static_cast(test_std::conqueue_errc::closed)); @@ -125,13 +128,15 @@ auto test_pop(const auto one, auto two, const auto three, auto four, auto five) queue.close(); }); + bool not_reached{true}; try { queue.pop(); - ASSERT(false); + not_reached = false; } catch (const test_std::conqueue_error& ex) { ASSERT(&ex.code().category() == &test_std::conqueue_category()); ASSERT(ex.code().value() == static_cast(test_std::conqueue_errc::closed)); } + ASSERT(not_reached); t.join(); } { @@ -160,7 +165,7 @@ auto test_async_push(auto one, auto two, auto three, auto four, auto five) -> vo this->complete = 1; } auto set_error(test_std::conqueue_errc) && noexcept -> void { this->complete = 2; } - auto set_error(std::exception_ptr) && noexcept -> void { this->complete = 3; } + auto set_error(const std::exception_ptr&) && noexcept -> void { this->complete = 3; } auto set_stopped() && noexcept -> void { this->complete = 4; } }; static_assert(test_std::receiver); @@ -217,7 +222,7 @@ auto test_async_pop(auto one, auto two, auto three, auto four, auto) -> void { vals.push_back(val); } auto set_error(test_std::conqueue_errc) && noexcept -> void { this->complete = 2; } - auto set_error(std::exception_ptr) && noexcept -> void { this->complete = 3; } + auto set_error(const std::exception_ptr&) && noexcept -> void { this->complete = 3; } auto set_stopped() && noexcept -> void { this->complete = 4; } }; static_assert(test_std::receiver); @@ -293,17 +298,21 @@ TEST(bounded_queue) { static_assert(std::same_as::value_type>); static_assert(std::same_as, test_std::bounded_queue::allocator_type>); - test_close(1, 2); - test_close("one"s, "two"s); + try { + test_close(1, 2); + test_close("one"s, "two"s); - test_push(1, 2, 3, 4, 5); - test_push("one"s, "two"s, "three"s, "four"s, "five"s); + test_push(1, 2, 3, 4, 5); + test_push("one"s, "two"s, "three"s, "four"s, "five"s); - test_pop(1, 2, 3, 4, 5); - test_pop("one"s, "two"s, "three"s, "four"s, "five"s); + test_pop(1, 2, 3, 4, 5); + test_pop("one"s, "two"s, "three"s, "four"s, "five"s); - test_async_push(1, 2, 3, 4, 5); - test_async_push("one"s, "two"s, "three"s, "four"s, "five"s); - test_async_pop(1, 2, 3, 4, 5); - test_async_pop("one"s, "two"s, "three"s, "four"s, "five"s); + test_async_push(1, 2, 3, 4, 5); + test_async_push("one"s, "two"s, "three"s, "four"s, "five"s); + test_async_pop(1, 2, 3, 4, 5); + test_async_pop("one"s, "two"s, "three"s, "four"s, "five"s); + } catch (...) { + abort(); + } } \ No newline at end of file diff --git a/tests/beman/execution26/intrusive-queue.test.cpp b/tests/beman/execution26/intrusive-queue.test.cpp index e3319cc2..215c64aa 100644 --- a/tests/beman/execution26/intrusive-queue.test.cpp +++ b/tests/beman/execution26/intrusive-queue.test.cpp @@ -9,7 +9,7 @@ namespace { struct node { - int value; + int value{}; node* n_{}; }; } // namespace @@ -26,10 +26,10 @@ TEST(intrusive_queue) { ASSERT(n0->n_ == nullptr); ASSERT(queue.empty()); - for (int i = 0; i != 5; ++i) { + for (std::size_t i = 0u; i != n.size(); ++i) { queue.push(&n[i]); } - for (int i = 0; i != 5; ++i) { + for (std::size_t i = 0u; i != n.size(); ++i) { ASSERT(not queue.empty()); ASSERT(queue.pop() == &n[i]); } From 38d3f5b58c20d9dc11959040c7683258ae97c224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Thu, 5 Dec 2024 00:34:57 +0000 Subject: [PATCH 17/41] addressed more clang-tidy warnings --- examples/sender-demo.cpp | 4 ++- examples/stop_token.cpp | 4 ++- examples/when_all-cancel.cpp | 5 +++- .../execution26/detail/bounded_queue.hpp | 14 +++++++--- .../detail/with_awaitable_senders.hpp | 3 +++ src/beman/execution26/execution.cpp | 1 + .../beman/execution26/bounded-queue.test.cpp | 27 ++++++++++++------- tests/beman/execution26/exec-split.test.cpp | 4 +++ .../exec-with-awaitable-senders.test.cpp | 9 +++++-- 9 files changed, 53 insertions(+), 18 deletions(-) diff --git a/examples/sender-demo.cpp b/examples/sender-demo.cpp index 38140ab5..b735e591 100644 --- a/examples/sender-demo.cpp +++ b/examples/sender-demo.cpp @@ -45,7 +45,7 @@ struct just_sender { static_assert(ex::sender>); static_assert(ex::sender_in>); -int main() { +int main() try { auto j = just_sender{std::pmr::string("value")}; auto t = std::move(j) | ex::then([](const std::pmr::string& v) { return v + " then"; }); auto w = ex::when_all(std::move(t)); @@ -60,4 +60,6 @@ int main() { } else std::cout << "operation was cancelled\n"; std::cout << "after start\n"; +} catch (...) { + abort(); } diff --git a/examples/stop_token.cpp b/examples/stop_token.cpp index aa74aa41..925eeb5d 100644 --- a/examples/stop_token.cpp +++ b/examples/stop_token.cpp @@ -91,7 +91,7 @@ auto inactive(Token token) -> void { #endif } // namespace -auto main() -> int { +auto main() -> int try { exec::stop_source source; ::std::thread act([token = source.get_token()] { active(token); }); ::std::thread inact([token = source.get_token()] { inactive(token); }); @@ -103,4 +103,6 @@ auto main() -> int { act.join(); inact.join(); print("done\n"); +} catch (...) { + abort(); } diff --git a/examples/when_all-cancel.cpp b/examples/when_all-cancel.cpp index 0fcb1105..f9e257f9 100644 --- a/examples/when_all-cancel.cpp +++ b/examples/when_all-cancel.cpp @@ -110,7 +110,10 @@ struct eager { inner_state.emplace(std::forward(s), receiver{this}); } // TODO on next line: bugprone-unchecked-optional-access - auto start() & noexcept -> void { ex::start((*this->inner_state).st); } + auto start() & noexcept -> void { + if (this->inner_state) + ex::start((*this->inner_state).st); + } }; template auto connect(Receiver&& receiver) { diff --git a/include/beman/execution26/detail/bounded_queue.hpp b/include/beman/execution26/detail/bounded_queue.hpp index c4857ca1..00afe7da 100644 --- a/include/beman/execution26/detail/bounded_queue.hpp +++ b/include/beman/execution26/detail/bounded_queue.hpp @@ -102,7 +102,7 @@ class beman::execution26::bounded_queue : ::beman::execution26::detail::immovabl explicit element_t(allocator_type& alloc, Args&&... args) { allocator_traits::construct(alloc, &value, ::std::forward(args)...); } - ~element_t() {}; + ~element_t() {} auto operator=(element_t&&) -> element_t& = delete; auto operator=(const element_t&) -> element_t& = delete; @@ -158,7 +158,7 @@ struct beman::execution26::bounded_queue::blocking_push_state : pu bounded_queue& queue; template - blocking_push_state(bounded_queue& que, Arg&& arg) : push_base(::std::forward(arg)), queue(que) {} + explicit blocking_push_state(bounded_queue& que, Arg&& arg) : push_base(::std::forward(arg)), queue(que) {} auto complete() -> void override { ::std::lock_guard cerberus(queue.mutex); { @@ -182,7 +182,7 @@ struct beman::execution26::bounded_queue::blocking_pop_state : pop ::std::variant<::std::monostate, value_type, ::beman::execution26::conqueue_errc, ::std::exception_ptr> result{}; ::std::condition_variable condition{}; - blocking_pop_state(bounded_queue& que) : queue(que) {} + explicit blocking_pop_state(bounded_queue& que) : queue(que) {} auto complete(value_type value) -> void override { { ::std::lock_guard cerberus(queue.mutex); @@ -496,6 +496,7 @@ auto beman::execution26::bounded_queue::internal_try_push(Arg&& ar return true; } +// NOLINTBEGIN(misc-no-recursion) template auto beman::execution26::bounded_queue::pop_notify(auto& cerberus) -> void { assert(cerberus.owns_lock()); @@ -506,7 +507,9 @@ auto beman::execution26::bounded_queue::pop_notify(auto& cerberus) cerberus.unlock(); } } +// NOLINTEND(misc-no-recursion) +// NOLINTBEGIN(misc-no-recursion) template auto beman::execution26::bounded_queue::push_notify(auto& cerberus) -> void { assert(cerberus.owns_lock()); @@ -527,6 +530,7 @@ auto beman::execution26::bounded_queue::push_notify(auto& cerberus } assert(not cerberus.owns_lock()); } +// NOLINTEND(misc-no-recursion) template template @@ -534,6 +538,7 @@ auto beman::execution26::bounded_queue::has_space(Lock&) noexcept return this->head - this->tail < this->max; } +// NOLINTBEGIN(misc-no-recursion) template template auto beman::execution26::bounded_queue::push_value(Lock& cerberus, V&& value) -> void { @@ -544,7 +549,9 @@ auto beman::execution26::bounded_queue::push_value(Lock& cerberus, this->pop_notify(cerberus); assert(not cerberus.owns_lock()); } +// NOLINTEND(misc-no-recursion) +// NOLINTBEGIN(misc-no-recursion) template template auto beman::execution26::bounded_queue::pop_value(Lock& cerberus) -> value_type { @@ -555,6 +562,7 @@ auto beman::execution26::bounded_queue::pop_value(Lock& cerberus) this->push_notify(cerberus); return val; } +// NOLINTEND(misc-no-recursion) template auto beman::execution26::bounded_queue::start_push(push_base& s) -> void { diff --git a/include/beman/execution26/detail/with_awaitable_senders.hpp b/include/beman/execution26/detail/with_awaitable_senders.hpp index e86817f5..d1b76d9d 100644 --- a/include/beman/execution26/detail/with_awaitable_senders.hpp +++ b/include/beman/execution26/detail/with_awaitable_senders.hpp @@ -37,6 +37,9 @@ struct with_awaitable_senders { } private: + friend Promise; + with_awaitable_senders() = default; + [[noreturn]] static auto default_unhandled_stopped(void*) noexcept -> ::std::coroutine_handle<> { ::std::terminate(); } diff --git a/src/beman/execution26/execution.cpp b/src/beman/execution26/execution.cpp index 14b64107..31aa1b4d 100644 --- a/src/beman/execution26/execution.cpp +++ b/src/beman/execution26/execution.cpp @@ -3,5 +3,6 @@ // ---------------------------------------------------------------------------- namespace beman::execution26 { +extern int version; int version{000}; } \ No newline at end of file diff --git a/tests/beman/execution26/bounded-queue.test.cpp b/tests/beman/execution26/bounded-queue.test.cpp index d13ee447..5be0410e 100644 --- a/tests/beman/execution26/bounded-queue.test.cpp +++ b/tests/beman/execution26/bounded-queue.test.cpp @@ -11,7 +11,7 @@ namespace { template -auto test_close(auto one, auto two) { +auto test_close(const auto& one, const auto& two) { test_std::bounded_queue queue(1); ASSERT(noexcept(const_cast&>(queue).is_closed())); ASSERT(not queue.is_closed()); @@ -39,7 +39,7 @@ auto test_close(auto one, auto two) { } template -auto test_push(const auto one, auto two, const auto three, auto four, auto five) -> void { +auto test_push(const auto& one, auto two, const auto& three, auto four, auto five) -> void { test_std::bounded_queue queue(4); queue.push(one); queue.push(std::move(two)); @@ -58,12 +58,14 @@ auto test_push(const auto one, auto two, const auto three, auto four, auto five) ASSERT(error.code().value() == static_cast(test_std::conqueue_errc::closed)); } ASSERT(not_reached); + auto five_copy(five); try { queue.push(std::move(five)); not_reached = false; } catch (const test_std::conqueue_error& error) { ASSERT(error.code().value() == static_cast(test_std::conqueue_errc::closed)); } + five = five_copy; ASSERT(not_reached); std::error_code ec0{}; ASSERT(queue.push(five, ec0) == false); @@ -75,7 +77,7 @@ auto test_push(const auto one, auto two, const auto three, auto four, auto five) } template -auto test_pop(const auto one, auto two, const auto three, auto four, auto five) -> void { +auto test_pop(const auto& one, const auto& two, const auto& three, const auto& four, auto five) -> void { test_std::bounded_queue queue(4); queue.push(one); queue.push(two); @@ -155,7 +157,7 @@ auto test_pop(const auto one, auto two, const auto three, auto four, auto five) } template -auto test_async_push(auto one, auto two, auto three, auto four, auto five) -> void { +auto test_async_push(const auto& one, const auto& two, const auto& three, const auto& four, const auto& five) -> void { struct receiver { using receiver_concept = test_std::receiver_t; int& complete; @@ -210,16 +212,21 @@ auto test_async_push(auto one, auto two, auto three, auto four, auto five) -> vo } template -auto test_async_pop(auto one, auto two, auto three, auto four, auto) -> void { +auto test_async_pop(const auto& one, const auto& two, const auto& three, const auto& four, const auto&) -> void { struct receiver { using receiver_concept = test_std::receiver_t; int& complete; std::vector& vals; - auto set_value(T val) && noexcept -> void { - receiver_concept c{}; - test::use(c); - this->complete = 1; - vals.push_back(val); + auto set_value(const T& val) && noexcept -> void { + try { + receiver_concept c{}; + test::use(c); + this->complete = 1; + vals.push_back(val); + + } catch (...) { + abort(); + } } auto set_error(test_std::conqueue_errc) && noexcept -> void { this->complete = 2; } auto set_error(const std::exception_ptr&) && noexcept -> void { this->complete = 3; } diff --git a/tests/beman/execution26/exec-split.test.cpp b/tests/beman/execution26/exec-split.test.cpp index 0962061c..df95a2e0 100644 --- a/tests/beman/execution26/exec-split.test.cpp +++ b/tests/beman/execution26/exec-split.test.cpp @@ -11,6 +11,8 @@ // ---------------------------------------------------------------------------- +namespace { + struct timed_scheduler_t : beman::execution26::scheduler_t {}; class some_thread_pool { @@ -182,6 +184,7 @@ void test_completion_from_another_thread() { void test_multiple_completions_from_other_threads() { using namespace std::chrono_literals; auto context = some_thread_pool{}; + ASSERT(some_thread_pool_scheduler(context) == some_thread_pool_scheduler(context)); auto result = [&] { auto scheduler = some_thread_pool_scheduler{context}; auto deadline = scheduler.now() + 20ms; @@ -199,6 +202,7 @@ void test_multiple_completions_from_other_threads() { ASSERT(val2 == 42); } } +} // namespace TEST(exec_split) { test_destroy_unused_split(); diff --git a/tests/beman/execution26/exec-with-awaitable-senders.test.cpp b/tests/beman/execution26/exec-with-awaitable-senders.test.cpp index e7c0e06d..a80c8e8a 100644 --- a/tests/beman/execution26/exec-with-awaitable-senders.test.cpp +++ b/tests/beman/execution26/exec-with-awaitable-senders.test.cpp @@ -15,6 +15,7 @@ namespace exec = beman::execution26; +namespace { struct promise; struct awaitable { @@ -44,12 +45,14 @@ coroutine test_await_tuple() { coroutine test_await_void() { co_await exec::just(); } void test_sync_wait_awaitable() { + bool not_reached{true}; try { - auto [v] = exec::sync_wait(awaitable{}).value(); + auto [v] = exec::sync_wait(awaitable{}).value_or(::std::tuple(0)); ASSERT(v == 1); } catch (...) { - ASSERT(false); + not_reached = false; } + ASSERT(not_reached); } coroutine test_mix_awaitable_and_sender() { @@ -58,6 +61,8 @@ coroutine test_mix_awaitable_and_sender() { ASSERT(value == 1); } +} // namespace + TEST(exec_with_awaitable_senders) { test_await_tuple().resume(); test_await_void().resume(); From 2ab503aba2c4c01043b735a0193f329cf167779b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Thu, 5 Dec 2024 06:50:18 +0000 Subject: [PATCH 18/41] suppressing a seemingly spurious analyser warning --- include/beman/execution26/detail/split.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/beman/execution26/detail/split.hpp b/include/beman/execution26/detail/split.hpp index 4c38418c..8eb20029 100644 --- a/include/beman/execution26/detail/split.hpp +++ b/include/beman/execution26/detail/split.hpp @@ -294,9 +294,11 @@ struct shared_wrapper { } ~shared_wrapper() noexcept { + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDelete) if (sh_state) { sh_state->dec_ref(); } + // NOLINTEND(clang-analyzer-cplusplus.NewDelete) } shared_wrapper(const shared_wrapper& other) noexcept : sh_state(other.sh_state) { From bd521289b738831c3e85d01e1316d0d341bf7648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Thu, 5 Dec 2024 07:00:06 +0000 Subject: [PATCH 19/41] trying to skip over errors from unknown options --- .clang-tidy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index 11f26a30..65d67265 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -27,7 +27,7 @@ Checks: -*-braces-around-statements ' HeaderFilterRegex: '.*/execution26/(include|src|example|tests)/.*\.(hpp)$' -WarningsAsErrors: 'clang*' +WarningsAsErrors: 'clang-analyzer*' FormatStyle: file CheckOptions: From c0bf0bd2b3f26f678112500c30cca8ac4c535b65 Mon Sep 17 00:00:00 2001 From: Maikel Nadolski Date: Sun, 1 Dec 2024 13:28:11 +0100 Subject: [PATCH 20/41] exec.awaitables: fix completion sigs for void awaitables (#96) --- .../detail/get_completion_signatures.hpp | 19 +++++++++++++------ .../exec-with-awaitable-senders.test.cpp | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/include/beman/execution26/detail/get_completion_signatures.hpp b/include/beman/execution26/detail/get_completion_signatures.hpp index 5ad453ab..58c1c70c 100644 --- a/include/beman/execution26/detail/get_completion_signatures.hpp +++ b/include/beman/execution26/detail/get_completion_signatures.hpp @@ -36,12 +36,19 @@ struct get_completion_signatures_t { return typename sender_type::completion_signatures{}; else if constexpr (::beman::execution26::detail:: is_awaitable>) { - return ::beman::execution26::completion_signatures< - ::beman::execution26::set_value_t( - ::beman::execution26::detail:: - await_result_type>), - ::beman::execution26::set_error_t(::std::exception_ptr), - ::beman::execution26::set_stopped_t()>{}; + using result_type = ::beman::execution26::detail:: + await_result_type>; + if constexpr (::std::same_as) { + return ::beman::execution26::completion_signatures<::beman::execution26::set_value_t(), + ::beman::execution26::set_error_t( + ::std::exception_ptr), + ::beman::execution26::set_stopped_t()>{}; + } else { + return ::beman::execution26::completion_signatures<::beman::execution26::set_value_t(result_type), + ::beman::execution26::set_error_t( + ::std::exception_ptr), + ::beman::execution26::set_stopped_t()>{}; + } } } diff --git a/tests/beman/execution26/exec-with-awaitable-senders.test.cpp b/tests/beman/execution26/exec-with-awaitable-senders.test.cpp index a80c8e8a..46a967b5 100644 --- a/tests/beman/execution26/exec-with-awaitable-senders.test.cpp +++ b/tests/beman/execution26/exec-with-awaitable-senders.test.cpp @@ -24,6 +24,12 @@ struct awaitable { int await_resume() { return 1; } }; +struct void_awaitable { + bool await_ready() { return false; } + void await_suspend(std::coroutine_handle<> h) { h.resume(); } + void await_resume() {} +}; + struct coroutine : std::coroutine_handle { using promise_type = ::promise; }; @@ -55,6 +61,14 @@ void test_sync_wait_awaitable() { ASSERT(not_reached); } +void test_sync_wait_void_awaitable() { + try { + ASSERT(exec::sync_wait(void_awaitable{})); + } catch (...) { + ASSERT(false); + } +} + coroutine test_mix_awaitable_and_sender() { auto [just, value] = co_await exec::when_all(exec::just(0), awaitable{}); ASSERT(just == 0); @@ -67,5 +81,6 @@ TEST(exec_with_awaitable_senders) { test_await_tuple().resume(); test_await_void().resume(); test_sync_wait_awaitable(); + test_sync_wait_void_awaitable(); test_mix_awaitable_and_sender().resume(); } \ No newline at end of file From 0c3ae22ad9b0ce07809f21ade590f7447f0870b0 Mon Sep 17 00:00:00 2001 From: Maikel Nadolski Date: Mon, 2 Dec 2024 19:36:40 +0100 Subject: [PATCH 21/41] Remove unused print header in test (#99) --- tests/beman/execution26/exec-with-awaitable-senders.test.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/beman/execution26/exec-with-awaitable-senders.test.cpp b/tests/beman/execution26/exec-with-awaitable-senders.test.cpp index 46a967b5..88e6bb3d 100644 --- a/tests/beman/execution26/exec-with-awaitable-senders.test.cpp +++ b/tests/beman/execution26/exec-with-awaitable-senders.test.cpp @@ -8,7 +8,6 @@ #include #include -#include // This examples shows how to await on senders // but also how to use awaitables with sender algorithms From 65983ef7b7cbb29a3724063262755ce844746a90 Mon Sep 17 00:00:00 2001 From: Claus Klein Date: Thu, 12 Dec 2024 00:36:48 +0100 Subject: [PATCH 22/41] Add pre-commit hooks (#102) --- .codespellignore | 5 +++ .codespellrc | 2 +- .devcontainer/devcontainer.json | 34 +++++++-------- .github/CODEOWNERS | 1 - .gitignore | 2 +- .markdownlint.yaml | 9 ++++ .pre-commit-config.yaml | 40 +++++++++++++++++ include/beman/execution26/detail/common.hpp | 2 +- .../detail/completion_signatures_for.hpp | 2 +- .../execution26/detail/forwarding_query.hpp | 2 +- pre-commit.yml | 43 +++++++++++++++++++ tests/beman/execution26/exec-just.test.cpp | 2 +- tests/beman/execution26/exec-then.test.cpp | 2 +- 13 files changed, 121 insertions(+), 25 deletions(-) create mode 100644 .codespellignore create mode 100644 .markdownlint.yaml create mode 100644 .pre-commit-config.yaml create mode 100644 pre-commit.yml diff --git a/.codespellignore b/.codespellignore new file mode 100644 index 00000000..2fddd5ed --- /dev/null +++ b/.codespellignore @@ -0,0 +1,5 @@ +cancelled +copyable +pullrequest +snd +statics diff --git a/.codespellrc b/.codespellrc index 7d233d00..31a9163a 100644 --- a/.codespellrc +++ b/.codespellrc @@ -3,4 +3,4 @@ builtin = clear,rare,en-GB_to_en-US,names,informal,code check-hidden = skip = ./.git,./build/*,./stagedir/*,./docs/html/*,./docs/latex/*,*.log,.*.swp,*~,*.bak,Makefile quiet-level = 2 -# ignore-words = .ignore-words +ignore-words = .codespellignore diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0c82ca08..1a6f3146 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,19 +1,19 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// For format details, see https://aka.ms/devcontainer.json. For config options, see the -// README at: https://github.com/devcontainers/templates/tree/main/src/cpp + // For format details, see https://aka.ms/devcontainer.json. For config options, see the + // README at: https://github.com/devcontainers/templates/tree/main/src/cpp -{ - "name": "Beman Project Generic Devcontainer", - "build": { - "dockerfile": "Dockerfile" - }, - "postCreateCommand": "bash .devcontainer/postcreate.sh", - "customizations": { - "vscode": { - "extensions": [ - "ms-vscode.cpptools", - "ms-vscode.cmake-tools" - ] - } - } -} + { + "name": "Beman Project Generic Devcontainer", + "build": { + "dockerfile": "Dockerfile" + }, + "postCreateCommand": "bash .devcontainer/postcreate.sh", + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools", + "ms-vscode.cmake-tools" + ] + } + } + } diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 066bcefa..49fa500f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,4 +2,3 @@ # Codeowners for reviews on PRs * @dietmarkuehl @camio @neatudarius - diff --git a/.gitignore b/.gitignore index 9eef479b..df999a8e 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,4 @@ CMakeCache.txt CMakeFiles/ docs/html -docs/latex \ No newline at end of file +docs/latex diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 00000000..81f5fcd7 --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,9 @@ +# MD033/no-inline-html : Inline HTML : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md033.md +# Disable inline html linter is needed for
+MD033: false + +# MD013/line-length : Line length : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md013.md +# Conforms to .clang-format ColumnLimit +# Update the comment in .clang-format if we no-longer tie these two column limits. +MD013: + line_length: 119 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..db70bf25 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,40 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + # TODO: - id: trailing-whitespace + # TODO: - id: end-of-file-fixer + # FIXME: - id: check-yaml + - id: check-added-large-files + + # TODO: Clang-format for C++ + # This brings in a portable version of clang-format. + # See also: https://github.com/ssciwr/clang-format-wheel + # - repo: https://github.com/pre-commit/mirrors-clang-format + # rev: v19.1.4 + # hooks: + # - id: clang-format + # types_or: [c++, c, json] + + # TODO: CMake linting and formatting + # - repo: https://github.com/BlankSpruce/gersemi + # rev: 0.17.1 + # hooks: + # - id: gersemi + # name: CMake linting + + # TODO: Markdown linting + # Config file: .markdownlint.yaml + # - repo: https://github.com/igorshubovych/markdownlint-cli + # rev: v0.43.0 + # hooks: + # - id: markdownlint + + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + files: ^.*\.(cmake|cpp|hpp|txt|md|json|in|yaml|yml)$ + args: ["--ignore-words", ".codespellignore" ] diff --git a/include/beman/execution26/detail/common.hpp b/include/beman/execution26/detail/common.hpp index dfe65886..b2463b20 100644 --- a/include/beman/execution26/detail/common.hpp +++ b/include/beman/execution26/detail/common.hpp @@ -21,7 +21,7 @@ * * There are a few ingredients to using `std::execution`: * - * - Sender algoritms to composes work into an asynchronous workflow. + * - Sender algorithms to composes work into an asynchronous workflow. * - Something holding and starting senders like `sync_wait()` * or `counting_scope`. * - A coroutine binding like `task` to make sender composition diff --git a/include/beman/execution26/detail/completion_signatures_for.hpp b/include/beman/execution26/detail/completion_signatures_for.hpp index aea24d53..c191de2c 100644 --- a/include/beman/execution26/detail/completion_signatures_for.hpp +++ b/include/beman/execution26/detail/completion_signatures_for.hpp @@ -21,7 +21,7 @@ namespace beman::execution26::detail { struct no_completion_signatures_defined_in_sender {}; /*! - * \brief Primary template declaration for the customisation of sender completion signatures. + * \brief Primary template declaration for the customization of sender completion signatures. * \headerfile beman/execution26/execution.hpp * \internal */ diff --git a/include/beman/execution26/detail/forwarding_query.hpp b/include/beman/execution26/detail/forwarding_query.hpp index 16e67464..6f460560 100644 --- a/include/beman/execution26/detail/forwarding_query.hpp +++ b/include/beman/execution26/detail/forwarding_query.hpp @@ -39,7 +39,7 @@ namespace beman::execution26 { */ using forwarding_query_t = beman::execution26::detail::forwarding_query_t; /*! - * \brief The customizatoin point object to determine whether querys should be forwarded + * \brief The customizatoin point object to determine whether queries should be forwarded * \headerfile beman/execution26/execution.hpp * * \details diff --git a/pre-commit.yml b/pre-commit.yml new file mode 100644 index 00000000..cc129492 --- /dev/null +++ b/pre-commit.yml @@ -0,0 +1,43 @@ +name: Lint Check (pre-commit) + +on: + pull_request: + push: + +jobs: + pre-commit: + runs-on: ubuntu-latest + name: pre-commit + permissions: + contents: read + checks: write + issues: write + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.13 + + - name: Get Changed Files + id: changed-files + uses: tj-actions/changed-files@v45 + + # See: + # https://github.com/tj-actions/changed-files?tab=readme-ov-file#using-local-git-directory- + - uses: pre-commit/action@v3.0.1 + with: + extra_args: --files ${{ steps.changed-files.outputs.all_changed_files }} + continue-on-error: true + + - name: suggester / pre-commit + if: ${{ github.event_name == 'pull_request' }} + uses: reviewdog/action-suggester@v1 + with: + tool_name: pre-commit + level: warning + reviewdog_flags: "-fail-level=error" diff --git a/tests/beman/execution26/exec-just.test.cpp b/tests/beman/execution26/exec-just.test.cpp index 161bb997..3108e2d0 100644 --- a/tests/beman/execution26/exec-just.test.cpp +++ b/tests/beman/execution26/exec-just.test.cpp @@ -212,7 +212,7 @@ TEST(exec_just) { test_just_constraints(); test_just(); #ifndef _MSC_VER - //-dk:TODO reenable allocator test for MSVC++ + //-dk:TODO re-enable allocator test for MSVC++ test_just_allocator(); #endif } catch (...) { diff --git a/tests/beman/execution26/exec-then.test.cpp b/tests/beman/execution26/exec-then.test.cpp index 1a02e58b..6938c02e 100644 --- a/tests/beman/execution26/exec-then.test.cpp +++ b/tests/beman/execution26/exec-then.test.cpp @@ -49,7 +49,7 @@ auto test_has(auto cpo, auto in_sender, auto fun) -> void { { in_sender | cpo(fun) } -> test_std::sender; }); #ifndef _MSC_VER - //-dk:TODO reenable this test + //-dk:TODO re-enable this test static_assert(requires { { in_sender | cpo(fun) | cpo([](auto&&...) {}) From 73872dc0d03e937fa94fc18e5fc4773625ea4623 Mon Sep 17 00:00:00 2001 From: Claus Klein Date: Fri, 13 Dec 2024 18:43:12 +0100 Subject: [PATCH 23/41] Check and format json and c++ files (#103) * Check and format json and c++ files * Enable more checks * Add pre-commint actions to CI --- .devcontainer/devcontainer.json | 32 ++++++++----------- .../workflows/pre-commit.yml | 0 .pre-commit-config.yaml | 20 ++++++------ docs/TODO.md | 2 +- docs/dependency.txt | 2 +- docs/overview.md | 22 ++++++------- docs/questions.md | 2 +- examples/doc-just.cpp | 2 +- examples/doc-just_error.cpp | 2 +- examples/doc-just_stopped.cpp | 2 +- examples/playground.cpp | 5 ++- examples/stop_token.cpp | 6 ++-- .../detail/atomic_intrusive_stack.hpp | 4 +-- .../execution26/detail/awaitable_sender.hpp | 2 +- .../detail/completion_signatures_of_t.hpp | 2 +- .../execution26/detail/completion_tag.hpp | 2 +- .../execution26/detail/connect_all_result.hpp | 2 +- .../execution26/detail/decayed_tuple.hpp | 2 +- .../execution26/detail/decayed_typeof.hpp | 2 +- .../beman/execution26/detail/decays_to.hpp | 2 +- include/beman/execution26/detail/env_of_t.hpp | 2 +- include/beman/execution26/detail/env_type.hpp | 2 +- .../execution26/detail/error_types_of_t.hpp | 2 +- .../detail/get_completion_signatures.hpp | 8 ++--- .../detail/inplace_stop_source.hpp | 4 +-- .../execution26/detail/intrusive_stack.hpp | 2 +- include/beman/execution26/detail/join_env.hpp | 4 +-- include/beman/execution26/detail/just.hpp | 22 ++++++------- include/beman/execution26/detail/run_loop.hpp | 4 +-- .../execution26/detail/schedule_from.hpp | 2 +- .../execution26/detail/sender_awaitable.hpp | 2 +- include/beman/execution26/detail/split.hpp | 2 +- .../beman/execution26/detail/stop_source.hpp | 2 +- .../beman/execution26/detail/suppress_pop.hpp | 2 +- .../execution26/detail/suppress_push.hpp | 2 +- .../detail/unspecified_promise.hpp | 2 +- .../detail/with_awaitable_senders.hpp | 2 +- src/beman/execution26/execution.cpp | 2 +- tests/beman/execution26/exec-connect.test.cpp | 8 ++--- tests/beman/execution26/exec-general.test.cpp | 6 ++-- tests/beman/execution26/exec-just.test.cpp | 9 ++---- tests/beman/execution26/exec-let.test.cpp | 2 +- tests/beman/execution26/exec-recv.test.cpp | 16 +++++----- tests/beman/execution26/exec-sched.test.cpp | 8 ++--- .../execution26/exec-snd-concepts.test.cpp | 2 +- .../exec-with-awaitable-senders.test.cpp | 2 +- .../beman/execution26/functional-syn.test.cpp | 2 +- .../execution26/include/test/stop_token.hpp | 2 +- .../execution26/stoptoken-concepts.test.cpp | 4 +-- .../beman/execution26/stoptoken-mem.test.cpp | 2 +- .../stoptoken-never-general.test.cpp | 2 +- .../thread-stoptoken-intro.test.cpp | 4 +-- 52 files changed, 121 insertions(+), 131 deletions(-) rename pre-commit.yml => .github/workflows/pre-commit.yml (100%) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1a6f3146..e0cbb9cf 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,19 +1,15 @@ -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - // For format details, see https://aka.ms/devcontainer.json. For config options, see the - // README at: https://github.com/devcontainers/templates/tree/main/src/cpp - - { - "name": "Beman Project Generic Devcontainer", - "build": { - "dockerfile": "Dockerfile" - }, - "postCreateCommand": "bash .devcontainer/postcreate.sh", - "customizations": { - "vscode": { - "extensions": [ - "ms-vscode.cpptools", - "ms-vscode.cmake-tools" - ] - } - } +{ + "name": "Beman Project Generic Devcontainer", + "build": { + "dockerfile": "Dockerfile" + }, + "postCreateCommand": "bash .devcontainer/postcreate.sh", + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools", + "ms-vscode.cmake-tools" + ] } + } +} diff --git a/pre-commit.yml b/.github/workflows/pre-commit.yml similarity index 100% rename from pre-commit.yml rename to .github/workflows/pre-commit.yml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index db70bf25..bffa58ed 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,19 +4,21 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - # TODO: - id: trailing-whitespace - # TODO: - id: end-of-file-fixer - # FIXME: - id: check-yaml + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-json + - id: check-yaml + exclude: ^\.clang-(format|tidy)$ - id: check-added-large-files - # TODO: Clang-format for C++ # This brings in a portable version of clang-format. # See also: https://github.com/ssciwr/clang-format-wheel - # - repo: https://github.com/pre-commit/mirrors-clang-format - # rev: v19.1.4 - # hooks: - # - id: clang-format - # types_or: [c++, c, json] + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v19.1.4 + hooks: + - id: clang-format + types_or: [c++, c, json] + exclude: docs/TODO.json # TODO: CMake linting and formatting # - repo: https://github.com/BlankSpruce/gersemi diff --git a/docs/TODO.md b/docs/TODO.md index 5ce79912..a161b0d6 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -1,4 +1,4 @@ -# ToDo +# ToDo | Section | Code | Test | Doc | Comment | | ------- |:----:|:----:|:---:| ------- | diff --git a/docs/dependency.txt b/docs/dependency.txt index e6fea924..b88965b1 100644 --- a/docs/dependency.txt +++ b/docs/dependency.txt @@ -515,4 +515,4 @@ when_all_with_variant into_variant when_all_with_variant when_all with_awaitable_senders as_awaitable write_env make_sender -write_env queryable \ No newline at end of file +write_env queryable diff --git a/docs/overview.md b/docs/overview.md index 99d4384d..67fd7c07 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -20,7 +20,7 @@ When an asynchronous operation completes it _signals_ its completion by calling
environment The term _enviroment_ refers to the bag of properties associated with an _object_ by the call std::execution::get_env(_object_). By default the environment for objects is empty (std::execution::empty_env). In particular, environments associated with receivers are used to provide access to properties like the stop token, scheduler, or allocator associated with the receiver. The various properties associated with an object are accessed via queries. -
+
## Concepts This section lists the concepts from `std::execution`. @@ -46,7 +46,7 @@ struct example_state { using operation_state_concept = std::execution::operation_state_t; std::remove_cvref_t receiver; - + auto start() & noexcept { std::execution::set_value(std::move(this->receiver)); } @@ -54,7 +54,7 @@ struct example_state static_assert(std::execution::operation_state>); ``` - +
receiver<Receiver> @@ -90,7 +90,7 @@ struct example_receiver { using receiver_concept = std::execution::receiver_t; std::remove_cvref_t nested; - + auto get_env() const noexcept { return std::execution::get_env(this->nested); } @@ -112,7 +112,7 @@ struct example_receiver static_assert(std::execution::receiver>); ``` -
+
receiver_of<Receiver, Completions> @@ -129,7 +129,7 @@ The example defines a simple receiver a struct example_receiver { using receiver_concept = std::execution::receiver_t; - + auto set_value(int) && noexcept ->void {} auto set_stopped() && noexcept ->void {} }; @@ -141,7 +141,7 @@ static_assert(std::execution::receiver_of); -// providing a superset of signal models models receiver_of: +// providing a superset of signal models models receiver_of: static_assert(std::execution::receiver_of; - + int value{}; template auto connect(Receiver&& receiver) const -> state { @@ -238,7 +238,7 @@ To determine if _Receiver_ can receive all sends_stopped<Sender, Env = std::execution::empty_env> -The concept sends_stopped<Sender, Env> determines if _Sender_ may send a stopped completion signal. To do so, the concepts determines if std::execution::get_completion_signals(_sender_, _env_) contains the signatures std::execution::set_stopped_t(). +The concept sends_stopped<Sender, Env> determines if _Sender_ may send a stopped completion signal. To do so, the concepts determines if std::execution::get_completion_signals(_sender_, _env_) contains the signatures std::execution::set_stopped_t().
stoppable_token<_Token_> @@ -284,7 +284,7 @@ void compute(std::stoppable_token auto token) Example: inactive
This example shows how an operation_state can use the callback_type together with a _token_ to get notified when cancellation is requested. - + ```c++ template struct example_state @@ -328,7 +328,7 @@ struct example_state std::execution::set_value(std::move(this->receiver)); } } -}; +}; ```
diff --git a/docs/questions.md b/docs/questions.md index fd070514..7e120b04 100644 --- a/docs/questions.md +++ b/docs/questions.md @@ -49,4 +49,4 @@ likely observable. - [exec.when.all] uses on-stop-request without saying what it actually does. most likely on-stop-request is supposed to call stop_src.request_stop - [exec.when.all] p11.1 uses a non-existing conversion for tuple to implement its "tie" - actually, that one may be new in C++23! \ No newline at end of file + actually, that one may be new in C++23! diff --git a/examples/doc-just.cpp b/examples/doc-just.cpp index f62298ab..fc5f5aba 100644 --- a/examples/doc-just.cpp +++ b/examples/doc-just.cpp @@ -11,4 +11,4 @@ int main() { auto result = ex::sync_wait(ex::just(17, "hello"s, true)); assert(result); assert(*result == std::tuple(17, "hello"s, true)); -} \ No newline at end of file +} diff --git a/examples/doc-just_error.cpp b/examples/doc-just_error.cpp index 88a8bc0f..0f3fd8dd 100644 --- a/examples/doc-just_error.cpp +++ b/examples/doc-just_error.cpp @@ -15,4 +15,4 @@ int main() { })); assert(result); assert(had_error); -} \ No newline at end of file +} diff --git a/examples/doc-just_stopped.cpp b/examples/doc-just_stopped.cpp index b2195b58..b68578cd 100644 --- a/examples/doc-just_stopped.cpp +++ b/examples/doc-just_stopped.cpp @@ -13,4 +13,4 @@ int main() { auto result = ex::sync_wait(ex::just_stopped() | ex::upon_stopped([&] { stopped = true; })); assert(result); assert(stopped); -} \ No newline at end of file +} diff --git a/examples/playground.cpp b/examples/playground.cpp index eb8a343b..472d57a1 100644 --- a/examples/playground.cpp +++ b/examples/playground.cpp @@ -10,10 +10,9 @@ namespace ex = ::beman::execution26; // ---------------------------------------------------------------------------- -int main() -{ +int main() { auto [result] = ex::sync_wait(ex::when_all(ex::just(std::string("hello, ")), ex::just(std::string("world"))) | ex::then([](const auto& s1, const auto& s2) { return s1 + s2; })) .value_or(std::tuple(std::string("oops"))); std::cout << "result='" << result << "'\n"; -} \ No newline at end of file +} diff --git a/examples/stop_token.cpp b/examples/stop_token.cpp index 925eeb5d..9a0748b0 100644 --- a/examples/stop_token.cpp +++ b/examples/stop_token.cpp @@ -46,8 +46,8 @@ namespace exec = beman::execution26; // - std::print isn't available everywhere, yet. Let's try a simple // placeholder. namespace { -::std::mutex io_lock; -void print(std::string_view text, auto&&...) { +::std::mutex io_lock; +void print(std::string_view text, auto&&...) { const std::lock_guard guard(io_lock); ::std::cout << text; } @@ -71,7 +71,7 @@ struct stop_callback_for_t { #ifdef __cpp_lib_latch template auto inactive(const Token& token) -> void { - ::std::latch latch(1); + ::std::latch latch(1); const stop_callback_for_t cb(token, [&latch] { latch.count_down(); }); latch.wait(); diff --git a/include/beman/execution26/detail/atomic_intrusive_stack.hpp b/include/beman/execution26/detail/atomic_intrusive_stack.hpp index e579c4dc..445c1fb8 100644 --- a/include/beman/execution26/detail/atomic_intrusive_stack.hpp +++ b/include/beman/execution26/detail/atomic_intrusive_stack.hpp @@ -31,7 +31,7 @@ class atomic_intrusive_stack; //! //! @tparam Item The type of the item in the stack. //! @tparam Next The pointer to the next item in the stack. -template +template class atomic_intrusive_stack { public: atomic_intrusive_stack() = default; @@ -85,4 +85,4 @@ class atomic_intrusive_stack { } // namespace beman::execution26::detail -#endif \ No newline at end of file +#endif diff --git a/include/beman/execution26/detail/awaitable_sender.hpp b/include/beman/execution26/detail/awaitable_sender.hpp index 0eeb9004..3de4e804 100644 --- a/include/beman/execution26/detail/awaitable_sender.hpp +++ b/include/beman/execution26/detail/awaitable_sender.hpp @@ -19,4 +19,4 @@ concept awaitable_sender = }; } // namespace beman::execution26::detail -#endif \ No newline at end of file +#endif diff --git a/include/beman/execution26/detail/completion_signatures_of_t.hpp b/include/beman/execution26/detail/completion_signatures_of_t.hpp index a7d74fe5..d6d08770 100644 --- a/include/beman/execution26/detail/completion_signatures_of_t.hpp +++ b/include/beman/execution26/detail/completion_signatures_of_t.hpp @@ -20,7 +20,7 @@ template requires ::beman::execution26::sender_in using completion_signatures_of_t = ::beman::execution26::detail::call_result_t< ::beman::execution26::get_completion_signatures_t, Sender, Env>; -} +} // namespace beman::execution26 // ---------------------------------------------------------------------------- diff --git a/include/beman/execution26/detail/completion_tag.hpp b/include/beman/execution26/detail/completion_tag.hpp index 8dca471a..f1e64174 100644 --- a/include/beman/execution26/detail/completion_tag.hpp +++ b/include/beman/execution26/detail/completion_tag.hpp @@ -21,7 +21,7 @@ template concept completion_tag = ::std::same_as || ::std::same_as || ::std::same_as; -} +} // namespace beman::execution26::detail // ---------------------------------------------------------------------------- diff --git a/include/beman/execution26/detail/connect_all_result.hpp b/include/beman/execution26/detail/connect_all_result.hpp index b9c3b110..109bf06e 100644 --- a/include/beman/execution26/detail/connect_all_result.hpp +++ b/include/beman/execution26/detail/connect_all_result.hpp @@ -23,7 +23,7 @@ using connect_all_result = ::beman::execution26::detail::basic_state*, Sender, ::beman::execution26::detail::indices_for >; -} +} // namespace beman::execution26::detail // ---------------------------------------------------------------------------- diff --git a/include/beman/execution26/detail/decayed_tuple.hpp b/include/beman/execution26/detail/decayed_tuple.hpp index 5c11d271..bd071fbd 100644 --- a/include/beman/execution26/detail/decayed_tuple.hpp +++ b/include/beman/execution26/detail/decayed_tuple.hpp @@ -17,7 +17,7 @@ namespace beman::execution26::detail { */ template using decayed_tuple = ::std::tuple<::std::decay_t...>; -} +} // namespace beman::execution26::detail // ---------------------------------------------------------------------------- diff --git a/include/beman/execution26/detail/decayed_typeof.hpp b/include/beman/execution26/detail/decayed_typeof.hpp index fad49e25..602a6132 100644 --- a/include/beman/execution26/detail/decayed_typeof.hpp +++ b/include/beman/execution26/detail/decayed_typeof.hpp @@ -16,7 +16,7 @@ namespace beman::execution26::detail { */ template using decayed_typeof = ::std::decay_t; -} +} // namespace beman::execution26::detail // ---------------------------------------------------------------------------- diff --git a/include/beman/execution26/detail/decays_to.hpp b/include/beman/execution26/detail/decays_to.hpp index 9cf1d4a6..98deb351 100644 --- a/include/beman/execution26/detail/decays_to.hpp +++ b/include/beman/execution26/detail/decays_to.hpp @@ -17,7 +17,7 @@ namespace beman::execution26::detail { */ template concept decays_to = ::std::same_as<::std::decay_t, To>; -} +} // namespace beman::execution26::detail // ---------------------------------------------------------------------------- diff --git a/include/beman/execution26/detail/env_of_t.hpp b/include/beman/execution26/detail/env_of_t.hpp index 4e002d6f..11390919 100644 --- a/include/beman/execution26/detail/env_of_t.hpp +++ b/include/beman/execution26/detail/env_of_t.hpp @@ -16,7 +16,7 @@ namespace beman::execution26 { */ template using env_of_t = decltype(::beman::execution26::get_env(::std::declval())); -} +} // namespace beman::execution26 // ---------------------------------------------------------------------------- diff --git a/include/beman/execution26/detail/env_type.hpp b/include/beman/execution26/detail/env_type.hpp index 7d5b4e19..f0da379f 100644 --- a/include/beman/execution26/detail/env_type.hpp +++ b/include/beman/execution26/detail/env_type.hpp @@ -24,7 +24,7 @@ using env_type = ::beman::execution26::detail::call_result_t< Index, ::beman::execution26::detail::state_type&, const Receiver&>; -} +} // namespace beman::execution26::detail // ---------------------------------------------------------------------------- diff --git a/include/beman/execution26/detail/error_types_of_t.hpp b/include/beman/execution26/detail/error_types_of_t.hpp index 5503d2e8..b9d47f71 100644 --- a/include/beman/execution26/detail/error_types_of_t.hpp +++ b/include/beman/execution26/detail/error_types_of_t.hpp @@ -28,7 +28,7 @@ using error_types_of_t = ::beman::execution26::completion_signatures_of_t, ::std::type_identity_t, Variant>; -} +} // namespace beman::execution26 // ---------------------------------------------------------------------------- diff --git a/include/beman/execution26/detail/get_completion_signatures.hpp b/include/beman/execution26/detail/get_completion_signatures.hpp index 58c1c70c..b97adb4e 100644 --- a/include/beman/execution26/detail/get_completion_signatures.hpp +++ b/include/beman/execution26/detail/get_completion_signatures.hpp @@ -54,9 +54,9 @@ struct get_completion_signatures_t { public: template - requires(not ::std::same_as(), - ::std::declval()))>) + requires(not::std::same_as(), + ::std::declval()))>) auto operator()(Sender&& sender, Env&& env) const noexcept { return this->get(::std::forward(sender), ::std::forward(env)); } @@ -66,4 +66,4 @@ inline constexpr get_completion_signatures_t get_completion_signatures{}; // ---------------------------------------------------------------------------- -#endif \ No newline at end of file +#endif diff --git a/include/beman/execution26/detail/inplace_stop_source.hpp b/include/beman/execution26/detail/inplace_stop_source.hpp index 77cfbfd3..c81696c3 100644 --- a/include/beman/execution26/detail/inplace_stop_source.hpp +++ b/include/beman/execution26/detail/inplace_stop_source.hpp @@ -85,8 +85,8 @@ class beman::execution26::inplace_stop_callback final template inplace_stop_callback(::beman::execution26::inplace_stop_token, Init&&); inplace_stop_callback(const inplace_stop_callback&) = delete; - inplace_stop_callback(inplace_stop_callback&&) = delete; - ~inplace_stop_callback() override { + inplace_stop_callback(inplace_stop_callback&&) = delete; + ~inplace_stop_callback() { if (this->source) { this->source->deregister(this); } diff --git a/include/beman/execution26/detail/intrusive_stack.hpp b/include/beman/execution26/detail/intrusive_stack.hpp index bd6c108c..71e3b3af 100644 --- a/include/beman/execution26/detail/intrusive_stack.hpp +++ b/include/beman/execution26/detail/intrusive_stack.hpp @@ -16,7 +16,7 @@ template class intrusive_stack; //! @brief This data structure is an intrusive stack that is not thread-safe. -template +template class intrusive_stack { public: //! @brief Pushes an item to the stack. diff --git a/include/beman/execution26/detail/join_env.hpp b/include/beman/execution26/detail/join_env.hpp index ab9cce6b..4bcb0e7e 100644 --- a/include/beman/execution26/detail/join_env.hpp +++ b/include/beman/execution26/detail/join_env.hpp @@ -25,9 +25,7 @@ class join_env { requires(Env1&, const Query& query, Args&&... args) { env1.query(query, ::std::forward(args)...); } || - requires(Env2& e2, const Query& query, Args&&... args) { - e2.query(query, ::std::forward(args)...); - }) + requires(Env2& e2, const Query& query, Args&&... args) { e2.query(query, ::std::forward(args)...); }) auto query(const Query& query, Args&&... args) noexcept -> decltype(auto) { if constexpr (requires { env1.query(query, ::std::forward(args)...); }) { return env1.query(query, ::std::forward(args)...); diff --git a/include/beman/execution26/detail/just.hpp b/include/beman/execution26/detail/just.hpp index 13188155..8296e87d 100644 --- a/include/beman/execution26/detail/just.hpp +++ b/include/beman/execution26/detail/just.hpp @@ -108,15 +108,15 @@ using just_stopped_t = ::beman::execution26::detail::just_t<::beman::execution26 * } * */ -inline constexpr ::beman::execution26::just_t just{}; +inline constexpr ::beman::execution26::just_t just{}; /*! * \brief just_error(_error_) yields a sender completing with set_error_t(_Error_) * \headerfile beman/execution26/execution.hpp * * \details - * `just_error` is a callable object of type `just_error_t`. Invoking just_error(_error_) yields a sender which - * stores its argument and produces an error completion with this error when started. This sender completes + * `just_error` is a callable object of type `just_error_t`. Invoking just_error(_error_) yields a sender + * which stores its argument and produces an error completion with this error when started. This sender completes * synchronously when started. * *

Usage

@@ -145,10 +145,9 @@ inline constexpr ::beman::execution26::just_t just{}; * uses that as the input for `upon_error` consuming the error and producing * a value completion: using sync_wait(just_error(_error_)) * directly doesn't work because `sync_wait` requires exactly one value completion - * from its argument and `set_error` only has an error completion. The function used with `upon_error` verifies that the - * expected code was produced and also sets the flag `had_error` indicating it - * was called at all. This flag is checked after waiting for the result - * in `sync_wait`. + * from its argument and `set_error` only has an error completion. The function used with `upon_error` verifies that + * the expected code was produced and also sets the flag `had_error` indicating it was called at all. This flag is + * checked after waiting for the result in `sync_wait`. * *
  * #include 
@@ -167,16 +166,15 @@ inline constexpr ::beman::execution26::just_t         just{};
  * }
  * 
*/ -inline constexpr ::beman::execution26::just_error_t just_error{}; +inline constexpr ::beman::execution26::just_error_t just_error{}; /*! * \brief just_stopped(_) yields a sender completing with set_stopped_t() * \headerfile beman/execution26/execution.hpp * * \details - * `just_stopped` is a callable object of type `just_stopped_t`. Invoking just_stopped() yields a sender which - * produces a cancellation completion when started. This sender completes - * synchronously when started. + * `just_stopped` is a callable object of type `just_stopped_t`. Invoking just_stopped() yields a sender + * which produces a cancellation completion when started. This sender completes synchronously when started. * *

Usage

*
@@ -200,7 +198,7 @@ inline constexpr ::beman::execution26::just_error_t   just_error{};
  * uses that as the input for `upon_stopped` consuming the cancellation and producing
  * a value completion: using sync_wait(just_stopped())
  * directly doesn't work because `sync_wait` requires exactly one value completion
- * from its argument and `set_stopped` only has a cancellation completion. The function used with `upon_stopped` 
+ * from its argument and `set_stopped` only has a cancellation completion. The function used with `upon_stopped`
  * sets the flag `had_stopped` indicating it
  * was called at all. This flag is checked after waiting for the result
  * in `sync_wait`.
diff --git a/include/beman/execution26/detail/run_loop.hpp b/include/beman/execution26/detail/run_loop.hpp
index af65d20e..2b854ad1 100644
--- a/include/beman/execution26/detail/run_loop.hpp
+++ b/include/beman/execution26/detail/run_loop.hpp
@@ -116,9 +116,9 @@ class run_loop {
     }
 
   public:
-    run_loop() noexcept  = default;
+    run_loop() noexcept       = default;
     run_loop(const run_loop&) = delete;
-    run_loop(run_loop&&) = delete;
+    run_loop(run_loop&&)      = delete;
     ~run_loop() {
         ::std::lock_guard guard(this->mutex);
         if (this->front != nullptr || this->current_state == state::running)
diff --git a/include/beman/execution26/detail/schedule_from.hpp b/include/beman/execution26/detail/schedule_from.hpp
index 6f053561..b0ac6e20 100644
--- a/include/beman/execution26/detail/schedule_from.hpp
+++ b/include/beman/execution26/detail/schedule_from.hpp
@@ -81,7 +81,7 @@ struct impls_for<::beman::execution26::detail::schedule_from_t> : ::beman::execu
             try {
                 ::std::visit(
                     [this](Tuple& result) noexcept -> void {
-                        if constexpr (not ::std::same_as<::std::monostate, Tuple>) {
+                        if constexpr (not::std::same_as<::std::monostate, Tuple>) {
                             ::std::apply(
                                 [this](auto&& tag, auto&&... args) {
                                     tag(::std::move(this->state->receiver), ::std::move(args)...);
diff --git a/include/beman/execution26/detail/sender_awaitable.hpp b/include/beman/execution26/detail/sender_awaitable.hpp
index 6a5cf9b0..0dec9264 100644
--- a/include/beman/execution26/detail/sender_awaitable.hpp
+++ b/include/beman/execution26/detail/sender_awaitable.hpp
@@ -85,4 +85,4 @@ class sender_awaitable {
 };
 } // namespace beman::execution26::detail
 
-#endif
\ No newline at end of file
+#endif
diff --git a/include/beman/execution26/detail/split.hpp b/include/beman/execution26/detail/split.hpp
index 8eb20029..3b861de9 100644
--- a/include/beman/execution26/detail/split.hpp
+++ b/include/beman/execution26/detail/split.hpp
@@ -246,7 +246,7 @@ struct impls_for : ::beman::execution26::detail::default_impls {
                 try {
                     ::std::visit(
                         [&](const Arg& arg) noexcept -> void {
-                            if constexpr (not ::std::same_as<::std::decay_t, ::std::monostate>) {
+                            if constexpr (not::std::same_as<::std::decay_t, ::std::monostate>) {
                                 ::std::apply(
                                     [&](auto tag, const auto&... args) noexcept -> void {
                                         tag(::std::move(*receiver), args...);
diff --git a/include/beman/execution26/detail/stop_source.hpp b/include/beman/execution26/detail/stop_source.hpp
index 24b42c48..2ef2c52c 100644
--- a/include/beman/execution26/detail/stop_source.hpp
+++ b/include/beman/execution26/detail/stop_source.hpp
@@ -144,7 +144,7 @@ class beman::execution26::stop_callback final : private CallbackFun, beman::exec
         this->setup();
     }
     stop_callback(const stop_callback&) = delete;
-    stop_callback(stop_callback&&) = delete;
+    stop_callback(stop_callback&&)      = delete;
     ~stop_callback() { this->deregister(); }
     auto operator=(stop_callback&&) -> stop_callback&      = delete;
     auto operator=(const stop_callback&) -> stop_callback& = delete;
diff --git a/include/beman/execution26/detail/suppress_pop.hpp b/include/beman/execution26/detail/suppress_pop.hpp
index 562a323e..1ae33d58 100644
--- a/include/beman/execution26/detail/suppress_pop.hpp
+++ b/include/beman/execution26/detail/suppress_pop.hpp
@@ -11,4 +11,4 @@
 #pragma clang diagnostic pop
 #endif
 #undef BEMAN_EXECUTION26_DIAGNOSTIC_PUSHED
-#endif
\ No newline at end of file
+#endif
diff --git a/include/beman/execution26/detail/suppress_push.hpp b/include/beman/execution26/detail/suppress_push.hpp
index f81bcd76..aba7e80c 100644
--- a/include/beman/execution26/detail/suppress_push.hpp
+++ b/include/beman/execution26/detail/suppress_push.hpp
@@ -16,4 +16,4 @@
 #pragma clang diagnostic ignored "-Wunknown-warning-option"
 #pragma clang diagnostic ignored "-Wmissing-braces"
 #pragma clang diagnostic ignored "-Wc++26-extensions"
-#endif
\ No newline at end of file
+#endif
diff --git a/include/beman/execution26/detail/unspecified_promise.hpp b/include/beman/execution26/detail/unspecified_promise.hpp
index e22ddff2..5f18c9a5 100644
--- a/include/beman/execution26/detail/unspecified_promise.hpp
+++ b/include/beman/execution26/detail/unspecified_promise.hpp
@@ -17,4 +17,4 @@ struct unspecified_promise {
 };
 } // namespace beman::execution26::detail
 
-#endif
\ No newline at end of file
+#endif
diff --git a/include/beman/execution26/detail/with_awaitable_senders.hpp b/include/beman/execution26/detail/with_awaitable_senders.hpp
index d1b76d9d..eb2d63b9 100644
--- a/include/beman/execution26/detail/with_awaitable_senders.hpp
+++ b/include/beman/execution26/detail/with_awaitable_senders.hpp
@@ -50,4 +50,4 @@ struct with_awaitable_senders {
 
 } // namespace beman::execution26
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/beman/execution26/execution.cpp b/src/beman/execution26/execution.cpp
index 31aa1b4d..3f453411 100644
--- a/src/beman/execution26/execution.cpp
+++ b/src/beman/execution26/execution.cpp
@@ -5,4 +5,4 @@
 namespace beman::execution26 {
 extern int version;
 int version{000};
-}
\ No newline at end of file
+}
diff --git a/tests/beman/execution26/exec-connect.test.cpp b/tests/beman/execution26/exec-connect.test.cpp
index aaedde78..76a2d697 100644
--- a/tests/beman/execution26/exec-connect.test.cpp
+++ b/tests/beman/execution26/exec-connect.test.cpp
@@ -24,7 +24,7 @@ struct state {
     template 
     state(int val, R&& r) : value(val), receiver(std::forward(r)) {}
     state(const state&)                    = delete;
-    state(state&&) = delete;
+    state(state&&)                         = delete;
     ~state()                               = default;
     auto operator=(const state&) -> state& = delete;
     auto operator=(state&&) -> state&      = delete;
@@ -47,7 +47,7 @@ struct rvalue_sender {
 
     explicit rvalue_sender(int val) : value(val) {}
     rvalue_sender(const rvalue_sender&)                    = delete;
-    rvalue_sender(rvalue_sender&&) = default;
+    rvalue_sender(rvalue_sender&&)                         = default;
     auto operator=(const rvalue_sender&) -> rvalue_sender& = delete;
     auto operator=(rvalue_sender&&) -> rvalue_sender&      = default;
     ~rvalue_sender()                                       = default;
@@ -103,8 +103,8 @@ struct domain_receiver {
     using receiver_concept = test_std::receiver_t;
     int value{};
 
-    explicit domain_receiver(int val) : value(val) {}
-    domain_receiver(domain_receiver&&)                    = default;
+    explicit domain_receiver(int value) : value(value) {}
+    domain_receiver(domain_receiver&&)                         = default;
     domain_receiver(const domain_receiver&)                    = delete;
     ~domain_receiver()                                         = default;
     auto operator=(domain_receiver&&) -> domain_receiver&      = default;
diff --git a/tests/beman/execution26/exec-general.test.cpp b/tests/beman/execution26/exec-general.test.cpp
index a433cd09..5b71ea8b 100644
--- a/tests/beman/execution26/exec-general.test.cpp
+++ b/tests/beman/execution26/exec-general.test.cpp
@@ -15,15 +15,15 @@ struct error {
 };
 
 struct non_movable {
-    non_movable(non_movable&&) = delete;
+    non_movable(non_movable&&)                         = delete;
     non_movable(const non_movable&)                    = delete;
     ~non_movable()                                     = default;
     auto operator=(non_movable&&) -> non_movable&      = delete;
     auto operator=(const non_movable&) -> non_movable& = delete;
 };
 struct non_copyable {
-    non_copyable(non_copyable&&)      = default;
-    non_copyable(const non_copyable&) = delete;
+    non_copyable(non_copyable&&)                         = default;
+    non_copyable(const non_copyable&)                    = delete;
     ~non_copyable()                                      = default;
     auto operator=(non_copyable&&) -> non_copyable&      = default;
     auto operator=(const non_copyable&) -> non_copyable& = delete;
diff --git a/tests/beman/execution26/exec-just.test.cpp b/tests/beman/execution26/exec-just.test.cpp
index 3108e2d0..dd0fc7ce 100644
--- a/tests/beman/execution26/exec-just.test.cpp
+++ b/tests/beman/execution26/exec-just.test.cpp
@@ -16,9 +16,9 @@
 
 namespace {
 struct not_movable {
-    not_movable()              = default;
+    not_movable()                                      = default;
     not_movable(const not_movable&)                    = delete;
-    not_movable(not_movable&&) = delete;
+    not_movable(not_movable&&)                         = delete;
     ~not_movable()                                     = default;
     auto operator=(const not_movable&) -> not_movable& = delete;
     auto operator=(not_movable&&) -> not_movable&      = delete;
@@ -173,10 +173,7 @@ struct counting_resource : std::pmr::memory_resource {
         ++this->count;
         return operator new(size);
     }
-    auto do_deallocate(void* p, std::size_t, std::size_t) -> void override
-    {
-        operator delete(p);
-    }
+    auto do_deallocate(void* p, std::size_t, std::size_t) -> void override { operator delete(p); }
     auto do_is_equal(const std::pmr::memory_resource& other) const noexcept -> bool override { return this == &other; }
 };
 auto test_just_allocator() -> void {
diff --git a/tests/beman/execution26/exec-let.test.cpp b/tests/beman/execution26/exec-let.test.cpp
index 6610d9d3..5a2ac0f6 100644
--- a/tests/beman/execution26/exec-let.test.cpp
+++ b/tests/beman/execution26/exec-let.test.cpp
@@ -72,7 +72,7 @@ auto test_let_value() {
 template 
 struct inline_resource : std::pmr::memory_resource {
     std::array buffer;
-    std::byte* next{+this->buffer};
+    std::byte*                  next{+this->buffer};
 
     void* do_allocate(std::size_t size, std::size_t) override {
         if (size <= std::size_t(std::distance(next, std::end(buffer)))) {
diff --git a/tests/beman/execution26/exec-recv.test.cpp b/tests/beman/execution26/exec-recv.test.cpp
index b64c28ff..95f31356 100644
--- a/tests/beman/execution26/exec-recv.test.cpp
+++ b/tests/beman/execution26/exec-recv.test.cpp
@@ -37,19 +37,19 @@ struct no_get_env {
 };
 
 struct not_move_constructible {
-    using receiver_concept                                = test_std::receiver_t;
-    not_move_constructible()                              = default;
-    not_move_constructible(const not_move_constructible&) = default;
-    not_move_constructible(not_move_constructible&&)      = delete;
+    using receiver_concept                                                   = test_std::receiver_t;
+    not_move_constructible()                                                 = default;
+    not_move_constructible(const not_move_constructible&)                    = default;
+    not_move_constructible(not_move_constructible&&)                         = delete;
     ~not_move_constructible()                                                = default;
     auto operator=(const not_move_constructible&) -> not_move_constructible& = default;
     auto operator=(not_move_constructible&&) -> not_move_constructible&      = delete;
 };
 struct not_copy_constructible {
-    using receiver_concept                                = test_std::receiver_t;
-    not_copy_constructible()                              = default;
-    not_copy_constructible(const not_copy_constructible&) = delete;
-    not_copy_constructible(not_copy_constructible&&)      = default;
+    using receiver_concept                                                   = test_std::receiver_t;
+    not_copy_constructible()                                                 = default;
+    not_copy_constructible(const not_copy_constructible&)                    = delete;
+    not_copy_constructible(not_copy_constructible&&)                         = default;
     ~not_copy_constructible()                                                = default;
     auto operator=(const not_copy_constructible&) -> not_copy_constructible& = delete;
     auto operator=(not_copy_constructible&&) -> not_copy_constructible&      = default;
diff --git a/tests/beman/execution26/exec-sched.test.cpp b/tests/beman/execution26/exec-sched.test.cpp
index 07ddcaff..83f0b4bc 100644
--- a/tests/beman/execution26/exec-sched.test.cpp
+++ b/tests/beman/execution26/exec-sched.test.cpp
@@ -30,11 +30,11 @@ struct no_scheduler_concept {
 };
 
 struct not_queryable {
-    using scheduler_concept = test_std::scheduler_t;
+    using scheduler_concept                                = test_std::scheduler_t;
     not_queryable()                                        = default;
     not_queryable(const not_queryable&)                    = default;
     not_queryable(not_queryable&&)                         = default;
-    ~not_queryable()        = delete;
+    ~not_queryable()                                       = delete;
     auto operator=(const not_queryable&) -> not_queryable& = default;
     auto operator=(not_queryable&&) -> not_queryable&      = default;
     auto schedule() -> sender> { return {}; }
@@ -52,8 +52,8 @@ struct not_equality_comparable {
 };
 
 struct not_copy_constructible {
-    using scheduler_concept                               = test_std::scheduler_t;
-    not_copy_constructible(const not_copy_constructible&) = delete;
+    using scheduler_concept                                                  = test_std::scheduler_t;
+    not_copy_constructible(const not_copy_constructible&)                    = delete;
     not_copy_constructible(not_copy_constructible&&)                         = default;
     ~not_copy_constructible()                                                = default;
     auto operator=(const not_copy_constructible&) -> not_copy_constructible& = delete;
diff --git a/tests/beman/execution26/exec-snd-concepts.test.cpp b/tests/beman/execution26/exec-snd-concepts.test.cpp
index b55d5643..deadf98d 100644
--- a/tests/beman/execution26/exec-snd-concepts.test.cpp
+++ b/tests/beman/execution26/exec-snd-concepts.test.cpp
@@ -142,7 +142,7 @@ auto test_sender_in() -> void {
         non_queryable()                                        = default;
         non_queryable(non_queryable&&)                         = default;
         non_queryable(const non_queryable&)                    = default;
-        ~non_queryable() = delete;
+        ~non_queryable()                                       = delete;
         auto operator=(non_queryable&&) -> non_queryable&      = default;
         auto operator=(const non_queryable&) -> non_queryable& = default;
     };
diff --git a/tests/beman/execution26/exec-with-awaitable-senders.test.cpp b/tests/beman/execution26/exec-with-awaitable-senders.test.cpp
index 88e6bb3d..bedbaaa0 100644
--- a/tests/beman/execution26/exec-with-awaitable-senders.test.cpp
+++ b/tests/beman/execution26/exec-with-awaitable-senders.test.cpp
@@ -82,4 +82,4 @@ TEST(exec_with_awaitable_senders) {
     test_sync_wait_awaitable();
     test_sync_wait_void_awaitable();
     test_mix_awaitable_and_sender().resume();
-}
\ No newline at end of file
+}
diff --git a/tests/beman/execution26/functional-syn.test.cpp b/tests/beman/execution26/functional-syn.test.cpp
index 294c4cc6..ed7afb64 100644
--- a/tests/beman/execution26/functional-syn.test.cpp
+++ b/tests/beman/execution26/functional-syn.test.cpp
@@ -19,7 +19,7 @@ struct non_destructible {
 struct non_copyable {
     non_copyable()                                       = default;
     non_copyable(const non_copyable&)                    = delete;
-    non_copyable(non_copyable&&) = delete;
+    non_copyable(non_copyable&&)                         = delete;
     ~non_copyable()                                      = default;
     auto operator=(const non_copyable&) -> non_copyable& = delete;
     auto operator=(non_copyable&&) -> non_copyable&      = delete;
diff --git a/tests/beman/execution26/include/test/stop_token.hpp b/tests/beman/execution26/include/test/stop_token.hpp
index 0113a082..fde7f51d 100644
--- a/tests/beman/execution26/include/test/stop_token.hpp
+++ b/tests/beman/execution26/include/test/stop_token.hpp
@@ -215,7 +215,7 @@ inline auto test::stop_callback_dtor_same_thread(Token token, Stop stop) -> void
         Base()                               = default;
         Base(Base&&)                         = default;
         Base(const Base&)                    = default;
-        virtual ~Base() = default;
+        virtual ~Base()                      = default;
         auto operator=(Base&&) -> Base&      = default;
         auto operator=(const Base&) -> Base& = default;
     };
diff --git a/tests/beman/execution26/stoptoken-concepts.test.cpp b/tests/beman/execution26/stoptoken-concepts.test.cpp
index 4c5cfa7d..8d2ff0b9 100644
--- a/tests/beman/execution26/stoptoken-concepts.test.cpp
+++ b/tests/beman/execution26/stoptoken-concepts.test.cpp
@@ -123,11 +123,11 @@ struct non_swappable {
     template 
     struct callback_type {};
 
-    non_swappable(const non_swappable&) noexcept               = default;
+    non_swappable(const non_swappable&) noexcept                    = default;
     non_swappable(non_swappable&&) noexcept                         = delete;
     ~non_swappable() noexcept                                       = default;
     auto operator=(const non_swappable&) noexcept -> non_swappable& = delete;
-    auto operator=(non_swappable&&) noexcept -> non_swappable& = delete;
+    auto operator=(non_swappable&&) noexcept -> non_swappable&      = delete;
     auto stop_requested() const noexcept -> bool;
     auto stop_possible() const noexcept -> bool;
     auto operator==(const non_swappable&) const -> bool = default;
diff --git a/tests/beman/execution26/stoptoken-mem.test.cpp b/tests/beman/execution26/stoptoken-mem.test.cpp
index 1c733b98..88600b4e 100644
--- a/tests/beman/execution26/stoptoken-mem.test.cpp
+++ b/tests/beman/execution26/stoptoken-mem.test.cpp
@@ -16,7 +16,7 @@ auto test_stop_token_swap() -> void {
     // - Then the tokens compare equal to the respective other member.
     // Reference: [stoptoken.mem] p1
 
-    ::test_std::stop_source s0, s1;
+    ::test_std::stop_source                 s0, s1;
     ::std::array<::test_std::stop_token, 2> pair0 = {s0.get_token(), s0.get_token()};
     ::std::array<::test_std::stop_token, 2> pair1 = {s1.get_token(), s1.get_token()};
 
diff --git a/tests/beman/execution26/stoptoken-never-general.test.cpp b/tests/beman/execution26/stoptoken-never-general.test.cpp
index 01fde390..56a6423c 100644
--- a/tests/beman/execution26/stoptoken-never-general.test.cpp
+++ b/tests/beman/execution26/stoptoken-never-general.test.cpp
@@ -8,7 +8,7 @@ TEST(stoptoken_never_general) {
     // Reference: [stoptoken.never.general]
     static_assert(::test_std::unstoppable_token<::test_std::never_stop_token>);
 
-    ::test_std::never_stop_token token;
+    ::test_std::never_stop_token       token;
     const ::test_std::never_stop_token other(token);
     static_assert(false == token.stop_requested());
     static_assert(false == token.stop_possible());
diff --git a/tests/beman/execution26/thread-stoptoken-intro.test.cpp b/tests/beman/execution26/thread-stoptoken-intro.test.cpp
index 6e176872..5fffd7b8 100644
--- a/tests/beman/execution26/thread-stoptoken-intro.test.cpp
+++ b/tests/beman/execution26/thread-stoptoken-intro.test.cpp
@@ -13,10 +13,10 @@ struct test_source : test_detail::immovable {
         callback_base()                                                = default;
         callback_base(callback_base&&)                                 = default;
         callback_base(const callback_base&)                            = default;
-        virtual ~callback_base()    = default;
+        virtual ~callback_base()                                       = default;
         auto         operator=(callback_base&&) -> callback_base&      = default;
         auto         operator=(const callback_base&) -> callback_base& = default;
-        virtual auto call() -> void = 0;
+        virtual auto call() -> void                                    = 0;
     };
     struct token {
         template 

From ee2bdae7069c15ae3c52fbfb6d6699377e97f4c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dietmar=20K=C3=BChl?= 
Date: Sun, 22 Dec 2024 02:09:52 +0100
Subject: [PATCH 24/41] Fix clang tidy warnings (#105)

- fixed various clang-tidy warnings
- fixed various gcc warnings
- implemented missing noexcept specifications in some places to fix clang-tidy warnings
---
 Makefile                                      |  10 +-
 examples/allocator.cpp                        |   4 +-
 examples/doc-just_error.cpp                   |   6 +
 examples/doc-just_stopped.cpp                 |   5 +
 examples/sender-demo.cpp                      |  34 +--
 examples/stop_token.cpp                       |  28 ++-
 examples/stopping.cpp                         |   4 +-
 examples/when_all-cancel.cpp                  |  12 +-
 .../beman/execution26/detail/as_awaitable.hpp |  10 +-
 .../execution26/detail/basic_operation.hpp    |   7 +-
 .../beman/execution26/detail/basic_sender.hpp |  17 +-
 .../beman/execution26/detail/basic_state.hpp  |   2 +-
 include/beman/execution26/detail/connect.hpp  |  37 ++-
 .../beman/execution26/detail/connect_all.hpp  |  58 +++--
 .../detail/get_completion_signatures.hpp      |   5 +-
 .../detail/inplace_stop_source.hpp            |   2 +-
 include/beman/execution26/detail/join_env.hpp |   2 +-
 include/beman/execution26/detail/make_env.hpp |   2 +-
 include/beman/execution26/detail/run_loop.hpp |   2 +-
 .../beman/execution26/detail/sched_attrs.hpp  |   2 +-
 include/beman/execution26/detail/then.hpp     |   5 +
 tests/beman/execution26/exec-connect.test.cpp |  19 +-
 tests/beman/execution26/exec-general.test.cpp |   3 +-
 .../beman/execution26/exec-snd-expos.test.cpp | 216 +++++++++---------
 tests/beman/execution26/exec-split.test.cpp   |   8 +-
 .../beman/execution26/exec-sync-wait.test.cpp |  27 +--
 tests/beman/execution26/exec-then.test.cpp    |   2 +-
 .../exec-with-awaitable-senders.test.cpp      |   6 +-
 .../execution26/include/test/execution.hpp    |   3 +
 .../execution26/include/test/stop_token.hpp   |   8 +-
 30 files changed, 327 insertions(+), 219 deletions(-)

diff --git a/Makefile b/Makefile
index 02a714fa..df8947b4 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ SANITIZERS := run
 #     SANITIZERS += asan # TODO: tsan msan
 # endif
 
-.PHONY: default release debug doc run update check ce todo distclean clean codespell clang-tidy build test install all format $(SANITIZERS)
+.PHONY: default release debug doc run update check ce todo distclean clean codespell clang-tidy build test install all format unstage $(SANITIZERS)
 
 SYSROOT   ?=
 TOOLCHAIN ?=
@@ -79,6 +79,7 @@ doc:
 
 build:
 	CC=$(CXX) cmake --fresh -G Ninja -S $(SOURCEDIR) -B  $(BUILD) $(TOOLCHAIN) $(SYSROOT) \
+	  -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \
       -DCMAKE_CXX_COMPILER=$(CXX) # XXX -DCMAKE_CXX_FLAGS="$(CXX_FLAGS) $(SAN_FLAGS)"
 	cmake --build $(BUILD)
 
@@ -111,8 +112,8 @@ check:
 	done | tsort > /dev/null
 
 build/$(SANITIZER)/compile_commands.json: $(SANITIZER)
-clang-tidy: build/$(SANITIZER)/compile_commands.json
-	run-clang-tidy -p build/$(SANITIZER) tests examples
+clang-tidy: $(BUILD)/compile_commands.json
+	run-clang-tidy -p $(BUILD) tests examples
 
 codespell:
 	codespell -L statics,snd,copyable,cancelled
@@ -128,6 +129,9 @@ clang-format:
 todo:
 	bin/mk-todo.py
 
+unstage:
+	git restore --staged tests/beman/execution26/CMakeLists.txt
+
 .PHONY: clean-doc
 clean-doc:
 	$(RM) -r docs/html docs/latex
diff --git a/examples/allocator.cpp b/examples/allocator.cpp
index 92557950..cd4655e7 100644
--- a/examples/allocator.cpp
+++ b/examples/allocator.cpp
@@ -12,7 +12,7 @@ namespace {
 template 
 struct inline_resource : std::pmr::memory_resource {
     const char* name;
-    explicit inline_resource(const char* nm) : name(nm) {}
+    explicit inline_resource(const char* n) : name(n) {}
     std::byte  buffer[Size]{};      // NOLINT(hicpp-avoid-c-arrays)
     std::byte* next{+this->buffer}; // NOLINT(hicpp-no-array-decay)
 
@@ -39,7 +39,7 @@ struct allocator_aware_fun {
 
     template 
         requires std::same_as, std::remove_cvref_t>
-    explicit allocator_aware_fun(F&& fn) : fun(std::forward(fn)) {}
+    explicit allocator_aware_fun(F&& f) : fun(std::forward(f)) {}
     allocator_aware_fun(const allocator_aware_fun& other, allocator_type alloc = {})
         : fun(other.fun), allocator(alloc) {}
     allocator_aware_fun(allocator_aware_fun&& other) noexcept
diff --git a/examples/doc-just_error.cpp b/examples/doc-just_error.cpp
index 0f3fd8dd..24e75b6c 100644
--- a/examples/doc-just_error.cpp
+++ b/examples/doc-just_error.cpp
@@ -6,13 +6,19 @@
 #include 
 namespace ex = beman::execution26;
 
+namespace {
+void use(auto&&...) {}
+} // namespace
+
 int main() {
     bool had_error{false};
     auto result = ex::sync_wait(ex::just_error(std::error_code(17, std::system_category())) |
                                 ex::upon_error([&](std::error_code ec) {
+                                    use(ec);
                                     assert(ec.value() == 17);
                                     had_error = true;
                                 }));
+    use(result, had_error);
     assert(result);
     assert(had_error);
 }
diff --git a/examples/doc-just_stopped.cpp b/examples/doc-just_stopped.cpp
index b68578cd..5f48cb2d 100644
--- a/examples/doc-just_stopped.cpp
+++ b/examples/doc-just_stopped.cpp
@@ -7,10 +7,15 @@
 #include  //-dk:TODO remove
 namespace ex = beman::execution26;
 
+namespace {
+void use(auto&&...) {}
+} // namespace
+
 int main() {
     bool stopped{false};
 
     auto result = ex::sync_wait(ex::just_stopped() | ex::upon_stopped([&] { stopped = true; }));
+    use(result, stopped);
     assert(result);
     assert(stopped);
 }
diff --git a/examples/sender-demo.cpp b/examples/sender-demo.cpp
index b735e591..384c71aa 100644
--- a/examples/sender-demo.cpp
+++ b/examples/sender-demo.cpp
@@ -45,21 +45,23 @@ struct just_sender {
 static_assert(ex::sender>);
 static_assert(ex::sender_in>);
 
-int main() try {
-    auto j = just_sender{std::pmr::string("value")};
-    auto t = std::move(j) | ex::then([](const std::pmr::string& v) { return v + " then"; });
-    auto w = ex::when_all(std::move(t));
-    auto e = ex::detail::write_env(std::move(w),
-                                   ex::detail::make_env(ex::get_allocator, std::pmr::polymorphic_allocator<>()));
+int main() {
+    try {
+        auto j = just_sender{std::pmr::string("value")};
+        auto t = std::move(j) | ex::then([](const std::pmr::string& v) { return v + " then"; });
+        auto w = ex::when_all(std::move(t));
+        auto e = ex::detail::write_env(std::move(w),
+                                       ex::detail::make_env(ex::get_allocator, std::pmr::polymorphic_allocator<>()));
 
-    std::cout << "before start\n";
-    auto r = ex::sync_wait(std::move(e));
-    if (r) {
-        auto [v] = *r;
-        std::cout << "produced='" << v << "'\n";
-    } else
-        std::cout << "operation was cancelled\n";
-    std::cout << "after start\n";
-} catch (...) {
-    abort();
+        std::cout << "before start\n";
+        auto r = ex::sync_wait(std::move(e));
+        if (r) {
+            auto [v] = *r;
+            std::cout << "produced='" << v << "'\n";
+        } else
+            std::cout << "operation was cancelled\n";
+        std::cout << "after start\n";
+    } catch (const std::exception& ex) {
+        std::cout << "ERROR: " << ex.what() << "\n";
+    }
 }
diff --git a/examples/stop_token.cpp b/examples/stop_token.cpp
index 9a0748b0..9100e284 100644
--- a/examples/stop_token.cpp
+++ b/examples/stop_token.cpp
@@ -4,6 +4,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -91,18 +92,21 @@ auto inactive(Token token) -> void {
 #endif
 } // namespace
 
-auto main() -> int try {
-    exec::stop_source source;
-    ::std::thread     act([token = source.get_token()] { active(token); });
-    ::std::thread     inact([token = source.get_token()] { inactive(token); });
+auto main() -> int {
+    try {
 
-    print("threads started\n");
-    source.request_stop();
-    print("threads cancelled\n");
+        exec::stop_source source;
+        ::std::thread     act([token = source.get_token()] { active(token); });
+        ::std::thread     inact([token = source.get_token()] { inactive(token); });
 
-    act.join();
-    inact.join();
-    print("done\n");
-} catch (...) {
-    abort();
+        print("threads started\n");
+        source.request_stop();
+        print("threads cancelled\n");
+
+        act.join();
+        inact.join();
+        print("done\n");
+    } catch (const std::exception& ex) {
+        std::cout << "ERROR: " << ex.what() << "\n";
+    }
 }
diff --git a/examples/stopping.cpp b/examples/stopping.cpp
index deec0d58..4b43c3a2 100644
--- a/examples/stopping.cpp
+++ b/examples/stopping.cpp
@@ -22,7 +22,7 @@ namespace {
 struct env {
     ex::inplace_stop_token token;
 
-    env(ex::inplace_stop_token tok) : token(tok) {} // NOLINT(hicpp-explicit-conversions)
+    explicit env(ex::inplace_stop_token t) : token(t) {} // NOLINT(hicpp-explicit-conversions)
 
     auto query(const ex::get_stop_token_t&) const noexcept { return this->token; }
 };
@@ -38,7 +38,7 @@ struct inject_cancel_sender {
         std::remove_cvref_t inner_receiver;
         ex::inplace_stop_token        token{};
 
-        auto get_env() const noexcept -> env { return {this->token}; }
+        auto get_env() const noexcept -> env { return env(this->token); }
 
         template 
         auto set_value(T&&... t) noexcept -> void {
diff --git a/examples/when_all-cancel.cpp b/examples/when_all-cancel.cpp
index f9e257f9..f7daca8d 100644
--- a/examples/when_all-cancel.cpp
+++ b/examples/when_all-cancel.cpp
@@ -7,6 +7,7 @@
 #include 
 #include 
 #include 
+#include 
 
 namespace ex = beman::execution26;
 
@@ -106,13 +107,14 @@ struct eager {
         std::optional inner_state;
 
         template 
-        state(R&& r, S&& s) : outer_receiver(std::forward(r)), inner_state() {
-            inner_state.emplace(std::forward(s), receiver{this});
-        }
-        // TODO on next line: bugprone-unchecked-optional-access
+        state(R&& r, S&& s)
+            : outer_receiver(std::forward(r)), inner_state(std::in_place, std::forward(s), receiver{this}) {}
         auto start() & noexcept -> void {
-            if (this->inner_state)
+            if (this->inner_state) {
                 ex::start((*this->inner_state).st);
+            } else {
+                assert(this->inner_state);
+            }
         }
     };
     template 
diff --git a/include/beman/execution26/detail/as_awaitable.hpp b/include/beman/execution26/detail/as_awaitable.hpp
index d0f2af31..5da05f5e 100644
--- a/include/beman/execution26/detail/as_awaitable.hpp
+++ b/include/beman/execution26/detail/as_awaitable.hpp
@@ -29,12 +29,12 @@ struct as_awaitable_t {
                                                            Promise>,
                 "as_awaitable must return an awaitable");
             return ::std::forward(expr).as_awaitable(promise);
-        } else if constexpr (!::beman::execution26::detail::
-                                 is_awaitable &&
-                             ::beman::execution26::detail::awaitable_sender) {
-            return ::beman::execution26::detail::sender_awaitable{::std::forward(expr), promise};
-        } else {
+        } else if constexpr (::beman::execution26::detail::
+                                 is_awaitable ||
+                             not::beman::execution26::detail::awaitable_sender) {
             return ::std::forward(expr);
+        } else {
+            return ::beman::execution26::detail::sender_awaitable{::std::forward(expr), promise};
         }
     }
 };
diff --git a/include/beman/execution26/detail/basic_operation.hpp b/include/beman/execution26/detail/basic_operation.hpp
index 1921195c..27f045a9 100644
--- a/include/beman/execution26/detail/basic_operation.hpp
+++ b/include/beman/execution26/detail/basic_operation.hpp
@@ -35,7 +35,12 @@ template 
     using inner_ops_t = ::beman::execution26::detail::connect_all_result;
     inner_ops_t inner_ops;
 
-    basic_operation(Sender&& sender, Receiver&& receiver) noexcept(true /*-dk:TODO*/)
+    basic_operation(Sender&& sender, Receiver&& receiver) noexcept(
+        noexcept(::beman::execution26::detail::basic_state(::std::forward(sender),
+                                                                             ::std::move(receiver))) &&
+        noexcept(::beman::execution26::detail::connect_all(this,
+                                                           ::std::forward(sender),
+                                                           ::beman::execution26::detail::indices_for())))
         : ::beman::execution26::detail::basic_state(::std::forward(sender),
                                                                       ::std::move(receiver)),
           // NOLINTBEGIN(bugprone-use-after-move,hicpp-invalid-access-moved)
diff --git a/include/beman/execution26/detail/basic_sender.hpp b/include/beman/execution26/detail/basic_sender.hpp
index 71776f3e..af0d0272 100644
--- a/include/beman/execution26/detail/basic_sender.hpp
+++ b/include/beman/execution26/detail/basic_sender.hpp
@@ -44,25 +44,32 @@ struct basic_sender : ::beman::execution26::detail::product_type
-    auto connect(Receiver receiver) & noexcept(true /*-dk:TODO*/)
+    auto connect(Receiver receiver) & noexcept(
+        noexcept(::beman::execution26::detail::basic_operation{*this, ::std::move(receiver)}))
         -> ::beman::execution26::detail::basic_operation {
         return {*this, ::std::move(receiver)};
     }
     template <::beman::execution26::receiver Receiver>
-    auto connect(Receiver receiver) const& noexcept(true /*-dk:TODO*/)
+    auto connect(Receiver receiver) const& noexcept(noexcept(
+        ::beman::execution26::detail::basic_operation{*this, ::std::move(receiver)}))
         -> ::beman::execution26::detail::basic_operation {
         return {*this, ::std::move(receiver)};
     }
     template <::beman::execution26::receiver Receiver>
-    auto connect(Receiver receiver) && noexcept(true /*-dk:TODO*/)
+    auto connect(Receiver receiver) && noexcept(
+        noexcept(::beman::execution26::detail::basic_operation{::std::move(*this),
+                                                                                       ::std::move(receiver)}))
         -> ::beman::execution26::detail::basic_operation {
         return {::std::move(*this), ::std::move(receiver)};
     }
 #else
     template <::beman::execution26::detail::decays_to Self, ::beman::execution26::receiver Receiver>
-    auto connect(this Self&& self, Receiver receiver) noexcept(true /*-dk:TODO*/)
+    auto
+    connect(this Self&& self,
+            Receiver receiver) noexcept(noexcept(::beman::execution26::detail::basic_operation{
+        ::std::forward(self), ::std::move(receiver)}))
         -> ::beman::execution26::detail::basic_operation {
         return {::std::forward(self), ::std::move(receiver)};
     }
diff --git a/include/beman/execution26/detail/basic_state.hpp b/include/beman/execution26/detail/basic_state.hpp
index 73a030c9..b1b53594 100644
--- a/include/beman/execution26/detail/basic_state.hpp
+++ b/include/beman/execution26/detail/basic_state.hpp
@@ -20,7 +20,7 @@ namespace beman::execution26::detail {
  */
 template 
 struct basic_state {
-    basic_state(Sender&& sndr, Receiver&& rcvr) noexcept(true)
+    basic_state(Sender&& sender, Receiver&& rcvr) noexcept(true)
         : receiver(::std::move(rcvr)),
           state(::beman::execution26::detail::impls_for< ::beman::execution26::tag_of_t >::get_state(
               ::std::forward(sndr), this->receiver)) {}
diff --git a/include/beman/execution26/detail/connect.hpp b/include/beman/execution26/detail/connect.hpp
index a7b33300..0ec5a2c8 100644
--- a/include/beman/execution26/detail/connect.hpp
+++ b/include/beman/execution26/detail/connect.hpp
@@ -21,14 +21,39 @@ namespace beman::execution26::detail {
  * \internal
  */
 struct connect_t {
+  private:
     template 
-    auto operator()(Sender&& sender, Receiver&& receiver) const noexcept(true /*-dk:TODO*/) {
+    static auto make_new_sender(Sender&& sender, Receiver&& receiver) noexcept(true) -> decltype(auto) {
+        return ::beman::execution26::transform_sender(
+            decltype(::beman::execution26::detail::get_domain_late(::std::forward(sender),
+                                                                   ::beman::execution26::get_env(receiver))){},
+            ::std::forward(sender),
+            ::beman::execution26::get_env(receiver));
+    }
+    template 
+    static constexpr auto connect_noexcept() -> bool {
+        if constexpr (requires {
+                          make_new_sender(::std::declval(), ::std::declval())
+                              .connect(::std::declval());
+                      }) {
+            return noexcept(make_new_sender(::std::declval(), ::std::declval())
+                                .connect(::std::declval()));
+        } else if constexpr (requires {
+                                 ::beman::execution26::detail::connect_awaitable(
+                                     make_new_sender(::std::declval(), ::std::declval()),
+                                     ::std::declval());
+                             }) {
+            return noexcept(::beman::execution26::detail::connect_awaitable(
+                make_new_sender(::std::declval(), ::std::declval()), ::std::declval()));
+        }
+        return true;
+    }
+
+  public:
+    template 
+    auto operator()(Sender&& sender, Receiver&& receiver) const noexcept(connect_noexcept()) {
         auto new_sender = [&sender, &receiver]() -> decltype(auto) {
-            return ::beman::execution26::transform_sender(
-                decltype(::beman::execution26::detail::get_domain_late(::std::forward(sender),
-                                                                       ::beman::execution26::get_env(receiver))){},
-                ::std::forward(sender),
-                ::beman::execution26::get_env(receiver));
+            return make_new_sender(::std::forward(sender), ::std::forward(receiver));
         };
 
         if constexpr (requires { new_sender().connect(::std::forward(receiver)); }) {
diff --git a/include/beman/execution26/detail/connect_all.hpp b/include/beman/execution26/detail/connect_all.hpp
index 766fa319..057bb6c7 100644
--- a/include/beman/execution26/detail/connect_all.hpp
+++ b/include/beman/execution26/detail/connect_all.hpp
@@ -10,6 +10,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -25,24 +26,55 @@ namespace beman::execution26::detail {
  * \internal
  */
 struct connect_all_t {
+  private:
+    template 
+    static auto apply_with_index_helper(::std::index_sequence seq, Fun&& fun, Tuple&& tuple) noexcept(noexcept(
+        ::std::forward(fun)(seq, ::beman::execution26::detail::forward_like(::std::get(tuple))...)))
+        -> decltype(auto) {
+        return ::std::forward(fun)(seq,
+                                        ::beman::execution26::detail::forward_like(::std::get(tuple))...);
+    }
+    template 
+    static auto apply_with_index(Fun&& fun, Tuple&& tuple) noexcept(
+        noexcept(apply_with_index_helper(::std::make_index_sequence<::std::tuple_size_v<::std::decay_t>>{},
+                                         ::std::forward(fun),
+                                         ::std::forward(tuple)))) -> decltype(auto) {
+        return apply_with_index_helper(::std::make_index_sequence<::std::tuple_size_v<::std::decay_t>>{},
+                                       ::std::forward(fun),
+                                       ::std::forward(tuple));
+    }
+
+    template 
+    struct connect_helper {
+        ::beman::execution26::detail::basic_state* op;
+
+        template <::std::size_t... J, typename... C>
+        auto operator()(::std::index_sequence, C&&... c) noexcept(
+            (noexcept(::beman::execution26::connect(
+                 ::beman::execution26::detail::forward_like(c),
+                 ::beman::execution26::detail::basic_receiver>{
+                     this->op})) &&
+             ... && true)) -> decltype(auto) {
+            return ::beman::execution26::detail::product_type{::beman::execution26::connect(
+                ::beman::execution26::detail::forward_like(c),
+                ::beman::execution26::detail::basic_receiver>{
+                    this->op})...};
+        }
+    };
+
     static auto use(auto&&...) {}
+
+  public:
     //-dk:TODO is the S parameter deviating from the spec?
     template 
     auto operator()(::beman::execution26::detail::basic_state* op,
                     S&&                                                          sender,
-                    ::std::index_sequence) const noexcept(true /*-dk:TODO*/) {
-        auto data{::beman::execution26::detail::get_sender_data(::std::forward(sender))};
-        return ::std::apply(
-            [&op](auto&&... c) {
-                return [&op]<::std::size_t... J>(::std::index_sequence, auto&&... cc) {
-                    use(op);
-                    return ::beman::execution26::detail::product_type{::beman::execution26::connect(
-                        ::beman::execution26::detail::forward_like(cc),
-                        ::beman::execution26::detail::
-                            basic_receiver>{op})...};
-                }(::std::make_index_sequence<::std::tuple_size_v<::std::decay_t>>{}, c...);
-            },
-            data.children);
+                    ::std::index_sequence) const
+        noexcept(noexcept(apply_with_index(
+            connect_helper{op},
+            ::beman::execution26::detail::get_sender_data(::std::forward(sender)).children))) -> decltype(auto) {
+        return apply_with_index(connect_helper{op},
+                                ::beman::execution26::detail::get_sender_data(::std::forward(sender)).children);
     }
 };
 
diff --git a/include/beman/execution26/detail/get_completion_signatures.hpp b/include/beman/execution26/detail/get_completion_signatures.hpp
index b97adb4e..4ab5aa44 100644
--- a/include/beman/execution26/detail/get_completion_signatures.hpp
+++ b/include/beman/execution26/detail/get_completion_signatures.hpp
@@ -24,8 +24,9 @@ struct get_completion_signatures_t {
     template 
     static auto get(Sender&& sender, Env&& env) noexcept {
         auto new_sender{[](auto&& sndr, auto&& e) -> decltype(auto) {
-            auto dom{::beman::execution26::detail::get_domain_late(sndr, e)};
-            return ::beman::execution26::transform_sender(dom, ::std::forward(sndr), ::std::forward(e));
+            auto domain{::beman::execution26::detail::get_domain_late(sndr, e)};
+            return ::beman::execution26::transform_sender(
+                domain, ::std::forward(sndr), ::std::forward(e));
         }};
 
         using sender_type = ::std::remove_cvref_t;
diff --git a/include/beman/execution26/detail/inplace_stop_source.hpp b/include/beman/execution26/detail/inplace_stop_source.hpp
index c81696c3..615cf155 100644
--- a/include/beman/execution26/detail/inplace_stop_source.hpp
+++ b/include/beman/execution26/detail/inplace_stop_source.hpp
@@ -86,7 +86,7 @@ class beman::execution26::inplace_stop_callback final
     inplace_stop_callback(::beman::execution26::inplace_stop_token, Init&&);
     inplace_stop_callback(const inplace_stop_callback&) = delete;
     inplace_stop_callback(inplace_stop_callback&&)      = delete;
-    ~inplace_stop_callback() {
+    ~inplace_stop_callback() override {
         if (this->source) {
             this->source->deregister(this);
         }
diff --git a/include/beman/execution26/detail/join_env.hpp b/include/beman/execution26/detail/join_env.hpp
index 4bcb0e7e..1cc32dcc 100644
--- a/include/beman/execution26/detail/join_env.hpp
+++ b/include/beman/execution26/detail/join_env.hpp
@@ -18,7 +18,7 @@ class join_env {
 
   public:
     template 
-    join_env(E1&& ev1, E2&& ev2) : env1(::std::forward(ev1)), env2(::std::forward(ev2)) {}
+    join_env(E1&& e1, E2&& e2) : env1(::std::forward(e1)), env2(::std::forward(e2)) {}
 
     template 
         requires(
diff --git a/include/beman/execution26/detail/make_env.hpp b/include/beman/execution26/detail/make_env.hpp
index b836e84b..1b8cb020 100644
--- a/include/beman/execution26/detail/make_env.hpp
+++ b/include/beman/execution26/detail/make_env.hpp
@@ -17,7 +17,7 @@ class make_env {
 
   public:
     template 
-    make_env(const Query&, V&& val) : value(::std::forward(val)) {}
+    make_env(const Query&, V&& v) : value(::std::forward(v)) {}
     constexpr auto query(const Query&) const noexcept -> const Value& { return this->value; }
     constexpr auto query(const Query&) noexcept -> Value& { return this->value; }
 };
diff --git a/include/beman/execution26/detail/run_loop.hpp b/include/beman/execution26/detail/run_loop.hpp
index 2b854ad1..b7913f2e 100644
--- a/include/beman/execution26/detail/run_loop.hpp
+++ b/include/beman/execution26/detail/run_loop.hpp
@@ -51,7 +51,7 @@ class run_loop {
         Receiver  receiver;
 
         template 
-        opstate(run_loop* lp, R&& rcvr) : loop(lp), receiver(::std::forward(rcvr)) {}
+        opstate(run_loop* l, R&& rcvr) : loop(l), receiver(::std::forward(rcvr)) {}
         auto start() & noexcept -> void {
             try {
                 this->loop->push_back(this);
diff --git a/include/beman/execution26/detail/sched_attrs.hpp b/include/beman/execution26/detail/sched_attrs.hpp
index f56818f3..55f7a709 100644
--- a/include/beman/execution26/detail/sched_attrs.hpp
+++ b/include/beman/execution26/detail/sched_attrs.hpp
@@ -24,7 +24,7 @@ class sched_attrs {
 
   public:
     template 
-    explicit sched_attrs(S sch) : sched(::std::move(sch)) {}
+    explicit sched_attrs(S s) : sched(::std::move(s)) {}
 
     template 
     auto query(const ::beman::execution26::get_completion_scheduler_t&) const noexcept {
diff --git a/include/beman/execution26/detail/then.hpp b/include/beman/execution26/detail/then.hpp
index 973f65f5..43788605 100644
--- a/include/beman/execution26/detail/then.hpp
+++ b/include/beman/execution26/detail/then.hpp
@@ -51,6 +51,7 @@ struct then_t : ::beman::execution26::sender_adaptor_closure>
 
 template 
 struct impls_for> : ::beman::execution26::detail::default_impls {
+    // NOLINTBEGIN(bugprone-exception-escape)
     static constexpr auto complete =
         [](auto, auto& fun, auto& receiver, Tag, Args&&... args) noexcept -> void {
         if constexpr (::std::same_as) {
@@ -66,13 +67,17 @@ struct impls_for> : ::beman::execution26::detail::default_imp
                 if constexpr (not noexcept(::std::invoke(::std::move(fun), ::std::forward(args)...)
 
                                                )) {
+                    static_assert(
+                        noexcept(::beman::execution26::set_error(::std::move(receiver), ::std::current_exception())));
                     ::beman::execution26::set_error(::std::move(receiver), ::std::current_exception());
                 }
             }
         } else {
+            static_assert(noexcept(Tag()(::std::move(receiver), ::std::forward(args)...)));
             Tag()(::std::move(receiver), ::std::forward(args)...);
         }
     };
+    // NOLINTEND(bugprone-exception-escape)
 };
 
 template 
diff --git a/tests/beman/execution26/exec-connect.test.cpp b/tests/beman/execution26/exec-connect.test.cpp
index 76a2d697..9a116646 100644
--- a/tests/beman/execution26/exec-connect.test.cpp
+++ b/tests/beman/execution26/exec-connect.test.cpp
@@ -103,7 +103,7 @@ struct domain_receiver {
     using receiver_concept = test_std::receiver_t;
     int value{};
 
-    explicit domain_receiver(int value) : value(value) {}
+    explicit domain_receiver(int val) : value(val) {}
     domain_receiver(domain_receiver&&)                         = default;
     domain_receiver(const domain_receiver&)                    = delete;
     ~domain_receiver()                                         = default;
@@ -166,7 +166,6 @@ auto test_operation_state_task() -> void {
     static_assert(
         std::same_as<::beman::execution26::detail::connect_awaitable_promise, state_t::promise_type>);
     static_assert(noexcept(state_t(std::coroutine_handle<>{})));
-    static_assert(noexcept(state_t(std::coroutine_handle<>{})));
     state_t state(::std::coroutine_handle<>{});
     static_assert(noexcept(state.start()));
 }
@@ -200,7 +199,7 @@ auto test_suspend_complete() -> void {
 }
 
 auto test_connect_awaitable() -> void {
-    struct awaiter_t {
+    struct local_awaiter {
         ::std::coroutine_handle<>& handle;
         int&                       result;
 
@@ -224,7 +223,7 @@ auto test_connect_awaitable() -> void {
         auto await_resume() -> void {}
     };
 
-    struct areceiver {
+    struct local_receiver {
         using receiver_concept = test_std::receiver_t;
 
         int&  iv;
@@ -252,7 +251,7 @@ auto test_connect_awaitable() -> void {
         int                       iv{};
         bool                      bv{};
 
-        auto op1{test_detail::connect_awaitable(awaiter_t{handle, result}, areceiver{iv, bv})};
+        auto op1{test_detail::connect_awaitable(local_awaiter{handle, result}, local_receiver{iv, bv})};
         ASSERT(handle == std::coroutine_handle<>());
         op1.start();
         ASSERT(handle != std::coroutine_handle<>());
@@ -268,7 +267,7 @@ auto test_connect_awaitable() -> void {
         int                       iv{};
         bool                      bv{};
 
-        auto op1{test_detail::connect_awaitable(awaiter_t{handle, result}, areceiver{iv, bv})};
+        auto op1{test_detail::connect_awaitable(local_awaiter{handle, result}, local_receiver{iv, bv})};
         ASSERT(handle == std::coroutine_handle<>());
         op1.start();
         ASSERT(handle != std::coroutine_handle<>());
@@ -284,7 +283,7 @@ auto test_connect_awaitable() -> void {
         int                       iv{};
         bool                      bv{};
 
-        auto op1{test_detail::connect_awaitable(void_awaiter{handle}, areceiver{iv, bv})};
+        auto op1{test_detail::connect_awaitable(void_awaiter{handle}, local_receiver{iv, bv})};
         ASSERT(handle == std::coroutine_handle<>());
         op1.start();
         ASSERT(handle != std::coroutine_handle<>());
@@ -295,13 +294,13 @@ auto test_connect_awaitable() -> void {
 }
 
 auto test_connect_with_awaiter() -> void {
-    struct awaiter_t {
+    struct local_awaiter {
         ::std::coroutine_handle<>& handle;
         auto                       await_ready() -> bool { return {}; }
         auto                       await_suspend(std::coroutine_handle<> h) -> void { this->handle = h; }
         auto                       await_resume() -> int { return 17; }
     };
-    struct areceiver {
+    struct local_receiver {
         using receiver_concept = test_std::receiver_t;
         bool& result;
         auto  set_value(int i) && noexcept -> void { this->result = i == 17; }
@@ -311,7 +310,7 @@ auto test_connect_with_awaiter() -> void {
 
     std::coroutine_handle<> handle{};
     bool                    result{};
-    auto                    op{test_std::connect(awaiter_t{handle}, areceiver{result})};
+    auto                    op{test_std::connect(local_awaiter{handle}, local_receiver{result})};
     ASSERT(handle == std::coroutine_handle{});
     test_std::start(op);
     ASSERT(handle != std::coroutine_handle{});
diff --git a/tests/beman/execution26/exec-general.test.cpp b/tests/beman/execution26/exec-general.test.cpp
index 5b71ea8b..48dec8a0 100644
--- a/tests/beman/execution26/exec-general.test.cpp
+++ b/tests/beman/execution26/exec-general.test.cpp
@@ -10,8 +10,9 @@
 // ----------------------------------------------------------------------------
 
 namespace {
-struct error {
+struct error : std::exception {
     int value;
+    explicit error(int v) : value(v) {}
 };
 
 struct non_movable {
diff --git a/tests/beman/execution26/exec-snd-expos.test.cpp b/tests/beman/execution26/exec-snd-expos.test.cpp
index 94d54dbf..f438472d 100644
--- a/tests/beman/execution26/exec-snd-expos.test.cpp
+++ b/tests/beman/execution26/exec-snd-expos.test.cpp
@@ -499,14 +499,14 @@ auto test_get_domain_late() -> void {
 }
 
 auto test_default_impls_get_attrs() -> void {
-    struct aenv {
+    struct local_env {
         int value;
     };
     struct child1 {
-        auto get_env() const noexcept { return aenv{1}; }
+        auto get_env() const noexcept { return local_env{1}; }
     };
     struct child2 {
-        auto get_env() const noexcept { return aenv{2}; }
+        auto get_env() const noexcept { return local_env{2}; }
     };
 
     static_assert(noexcept(test_detail::default_impls::get_attrs(0, child1{})));
@@ -517,21 +517,21 @@ auto test_default_impls_get_attrs() -> void {
 }
 
 auto test_default_impls_get_env() -> void {
-    struct genv {
+    struct local_env {
         int value;
     };
-    struct greceiver {
-        auto get_env() const noexcept { return genv{1}; }
+    struct local_receiver {
+        auto get_env() const noexcept { return local_env{1}; }
     };
 
     int arg{};
-    static_assert(noexcept(test_detail::default_impls::get_env(0, arg, greceiver{})));
-    static_assert(
-        std::same_as, decltype(test_detail::default_impls::get_env(0, arg, greceiver{}))>);
+    static_assert(noexcept(test_detail::default_impls::get_env(0, arg, local_receiver{})));
+    static_assert(std::same_as,
+                               decltype(test_detail::default_impls::get_env(0, arg, local_receiver{}))>);
 }
 
 auto test_default_impls_get_state() -> void {
-    struct gtag {
+    struct local_tag {
         static auto name() { return "test_default_impls_get_state"; }
     };
     struct data {
@@ -539,52 +539,52 @@ auto test_default_impls_get_state() -> void {
         int  v2{};
         auto operator==(const data&) const -> bool = default;
     };
-    struct gsender0 {
-        gtag t{};
-        data d{1, 2};
+    struct local_sender0 {
+        local_tag t{};
+        data      d{1, 2};
     };
-    struct gsender1 {
-        gtag t{};
-        data d{1, 2};
-        int  i1{};
+    struct local_sender1 {
+        local_tag t{};
+        data      d{1, 2};
+        int       i1{};
     };
-    struct gsender2 {
-        gtag t{};
-        data d{1, 2};
-        int  i1{};
-        int  i2{};
+    struct local_sender2 {
+        local_tag t{};
+        data      d{1, 2};
+        int       i1{};
+        int       i2{};
     };
-    struct gsender3 {
-        gtag t{};
-        data d{1, 2};
-        int  i1{};
-        int  i2{};
-        int  i3{};
+    struct local_sender3 {
+        local_tag t{};
+        data      d{1, 2};
+        int       i1{};
+        int       i2{};
+        int       i3{};
     };
-    struct gsender4 {
-        gtag t{};
-        data d{1, 2};
-        int  i1{};
-        int  i2{};
-        int  i3{};
-        int  i4{};
+    struct local_sender4 {
+        local_tag t{};
+        data      d{1, 2};
+        int       i1{};
+        int       i2{};
+        int       i3{};
+        int       i4{};
     };
-    struct greceiver {};
-
-    gsender0       s{};
-    const gsender0 cs{};
-    greceiver      r{};
-    static_assert(noexcept(test_detail::default_impls::get_state(gsender0{}, r)));
-    static_assert(std::same_as);
-    ASSERT((data{1, 2}) == test_detail::default_impls::get_state(gsender0{}, r));
-    static_assert(std::same_as);
-    ASSERT((data{1, 2}) == test_detail::default_impls::get_state(gsender1{}, r));
-    static_assert(std::same_as);
-    ASSERT((data{1, 2}) == test_detail::default_impls::get_state(gsender2{}, r));
-    static_assert(std::same_as);
-    ASSERT((data{1, 2}) == test_detail::default_impls::get_state(gsender3{}, r));
-    static_assert(std::same_as);
-    ASSERT((data{1, 2}) == test_detail::default_impls::get_state(gsender4{}, r));
+    struct local_receiver {};
+
+    local_sender0       s{};
+    const local_sender0 cs{};
+    local_receiver      r{};
+    static_assert(noexcept(test_detail::default_impls::get_state(local_sender0{}, r)));
+    static_assert(std::same_as);
+    ASSERT((data{1, 2}) == test_detail::default_impls::get_state(local_sender0{}, r));
+    static_assert(std::same_as);
+    ASSERT((data{1, 2}) == test_detail::default_impls::get_state(local_sender1{}, r));
+    static_assert(std::same_as);
+    ASSERT((data{1, 2}) == test_detail::default_impls::get_state(local_sender2{}, r));
+    static_assert(std::same_as);
+    ASSERT((data{1, 2}) == test_detail::default_impls::get_state(local_sender3{}, r));
+    static_assert(std::same_as);
+    ASSERT((data{1, 2}) == test_detail::default_impls::get_state(local_sender4{}, r));
     static_assert(std::same_as);
     static_assert(std::same_as);
 }
@@ -616,16 +616,16 @@ auto test_default_impls_start() -> void {
 template 
 auto test_default_impls_complete(Impls) -> void {
     struct arg {};
-    struct dreceiver {
+    struct local_receiver {
         bool& called;
     };
     struct state {};
 
-    bool     called{false};
-    auto      non_tag = [](dreceiver&&, int) {};
-    auto      tag     = [](dreceiver&& r, int, arg) { r.called = true; };
-    dreceiver r{called};
-    state    s{};
+    bool           called{false};
+    auto           non_tag = [](local_receiver&&, int) {};
+    auto           tag     = [](local_receiver&& r, int, arg) { r.called = true; };
+    local_receiver r{called};
+    state          s{};
 
     static_assert(not requires { Impls::complete(::std::integral_constant{}, s, r, non_tag, 0, arg{}); });
     static_assert(requires { Impls::complete(::std::integral_constant{}, s, r, tag, 0, arg{}); });
@@ -645,39 +645,39 @@ auto test_default_impls() -> void {
 }
 
 auto test_impls_for() -> void {
-    struct itag {
+    struct local_tag {
         static auto name() { return "test_impls_for"; }
     };
 
-    static_assert(std::derived_from, test_detail::default_impls>);
+    static_assert(std::derived_from, test_detail::default_impls>);
 }
 
 auto test_state_type() -> void {
-    struct stag {
+    struct local_tag {
         static auto name() { return "test_state_type"; }
     };
     struct state {};
     struct sender {
-        stag  t;
-        state s;
+        local_tag t;
+        state     s;
     };
-    struct sreceiver {};
+    struct local_receiver {};
 
-    static_assert(std::same_as>);
+    static_assert(std::same_as>);
 }
 
 auto test_basic_state() -> void {
-    struct stag {
+    struct local_tag {
         static auto name() { return "test_basic_state"; }
     };
     struct data {};
-    struct sender {
-        stag t;
-        data d;
+    struct local_sender {
+        local_tag t;
+        data      d;
     };
-    struct sreceiver {};
+    struct local_receiver {};
 
-    test_detail::basic_state state(sender{}, sreceiver{});
+    test_detail::basic_state state(local_sender{}, local_receiver{});
 }
 
 auto test_indices_for() -> void {
@@ -704,35 +704,37 @@ auto test_valid_specialization() -> void {
 
 auto test_env_type() -> void {
     using index = std::integral_constant;
-    struct ttag {
+    struct local_tag {
         static auto name() { return "test_env_type"; }
     };
     struct data {};
-    struct sender {
-        ttag t;
-        data d;
+    struct local_env {};
+    struct local_sender {
+        local_tag t;
+        data      d;
     };
     struct sender_with_env {
-        ttag t;
-        data d;
-        auto get_env() const noexcept -> env { return {}; }
+        local_tag t;
+        data      d;
+        auto      get_env() const noexcept -> local_env { return {}; }
     };
-    struct treceiver {};
+    struct local_receiver {};
     struct receiver_with_env {
-        auto get_env() const noexcept -> env { return {}; }
+        auto get_env() const noexcept -> local_env { return {}; }
     };
 
-    static_assert(
-        std::same_as, test_detail::env_type>);
     static_assert(std::same_as,
-                               test_detail::env_type>);
-    static_assert(std::same_as, test_detail::env_type>);
+                               test_detail::env_type>);
+    static_assert(std::same_as,
+                               test_detail::env_type>);
+    static_assert(
+        std::same_as, test_detail::env_type>);
 }
 
 template 
 auto test_basic_receiver() -> void {
     using index = std::integral_constant;
-    struct btag {
+    struct local_tag {
         static auto name() { return "test_basic_receiver"; }
     };
     struct data {};
@@ -740,11 +742,11 @@ auto test_basic_receiver() -> void {
         int  value{};
         auto operator==(const err&) const -> bool = default;
     };
-    struct sender {
-        btag t{};
-        data d{};
+    struct local_sender {
+        local_tag t{};
+        data      d{};
     };
-    struct breceiver {
+    struct local_receiver {
         T    value{};
         err  error{};
         bool stopped{};
@@ -756,15 +758,16 @@ auto test_basic_receiver() -> void {
     struct unstoppable_receiver {
         T value;
     };
-    using basic_receiver = test_detail::basic_receiver;
+    using basic_receiver = test_detail::basic_receiver;
     static_assert(test_std::receiver);
-    static_assert(std::same_as);
-    static_assert(std::same_as, typename basic_receiver::state_t>);
+    static_assert(std::same_as);
+    static_assert(
+        std::same_as, typename basic_receiver::state_t>);
     ASSERT(&basic_receiver::complete == &test_detail::default_impls::complete);
 
     {
-        test_detail::basic_state op(sender{}, breceiver{});
-        basic_receiver                             br{&op};
+        test_detail::basic_state op(local_sender{}, local_receiver{});
+        basic_receiver                                         br{&op};
         static_assert(not requires { test_std::set_value(std::move(br)); });
         static_assert(not requires { test_std::set_value(std::move(br), 42, 1); });
         static_assert(requires { test_std::set_value(std::move(br), 42); });
@@ -774,8 +777,8 @@ auto test_basic_receiver() -> void {
         ASSERT(op.receiver.value == 42);
     }
     {
-        test_detail::basic_state op(sender{}, breceiver{});
-        basic_receiver                             br{&op};
+        test_detail::basic_state op(local_sender{}, local_receiver{});
+        basic_receiver                                         br{&op};
         static_assert(not requires { test_std::set_error(std::move(br)); });
         static_assert(not requires { test_std::set_error(std::move(br), 0); });
         static_assert(requires { test_std::set_error(std::move(br), err{42}); });
@@ -785,8 +788,8 @@ auto test_basic_receiver() -> void {
         ASSERT(op.receiver.error == err{42});
     }
     {
-        test_detail::basic_state op(sender{}, breceiver{});
-        basic_receiver                             br{&op};
+        test_detail::basic_state op(local_sender{}, local_receiver{});
+        basic_receiver                                         br{&op};
         static_assert(requires { test_std::set_stopped(std::move(br)); });
         static_assert(noexcept(test_std::set_stopped(std::move(br))));
         ASSERT(op.receiver.stopped == false);
@@ -794,8 +797,8 @@ auto test_basic_receiver() -> void {
         ASSERT(op.receiver.stopped == true);
     }
     {
-        test_detail::basic_state           op(sender{}, unstoppable_receiver{});
-        test_detail::basic_receiver br{&op};
+        test_detail::basic_state op(local_sender{}, unstoppable_receiver{});
+        test_detail::basic_receiver br{&op};
         static_assert(not requires { std::move(br).set_stopped(); });
     }
     //-dk:TODO test basic_receiver::get_env
@@ -994,6 +997,7 @@ auto test_basic_operation() -> void {
 
 auto test_completion_signatures_for() -> void {
     struct arg {};
+    struct local_env {};
     struct bad_env {};
     struct sender {
         using sender_concept = test_std::sender_t;
@@ -1001,11 +1005,11 @@ auto test_completion_signatures_for() -> void {
         using env_sigs       = test_std::completion_signatures;
 
         auto get_completion_signatures(const test_std::empty_env&) -> empty_env_sigs { return {}; }
-        auto get_completion_signatures(const env&) -> env_sigs { return {}; }
+        auto get_completion_signatures(const local_env&) -> env_sigs { return {}; }
     };
 
     static_assert(test_std::sender_in);
-    static_assert(test_std::sender_in);
+    static_assert(test_std::sender_in);
     static_assert(not test_std::sender_in);
 
 #if 0
@@ -1015,7 +1019,7 @@ auto test_completion_signatures_for() -> void {
             sender::empty_env_sigs
         >);
         static_assert(std::same_as<
-            test_detail::completion_signatures_for,
+            test_detail::completion_signatures_for,
             sender::env_sigs
         >);
 #endif
@@ -1057,6 +1061,8 @@ struct tuple_element {
 } // namespace std
 namespace {
 auto test_basic_sender() -> void {
+    struct local_env {};
+
     {
         auto&& [a, b, c] = tagged_sender{basic_sender_tag{}, data{}, sender0{}};
         test::use(a);
@@ -1067,13 +1073,13 @@ auto test_basic_sender() -> void {
 
     static_assert(test_std::sender);
     static_assert(test_std::sender_in);
-    static_assert(test_std::sender_in);
+    static_assert(test_std::sender_in);
     static_assert(test_std::operation_state>);
     static_assert(test_std::sender);
     static_assert(std::same_as>);
     static_assert(
         std::same_as);
+                     decltype(test_std::transform_sender(test_std::default_domain{}, tagged_sender{}, local_env{}))>);
 
     using basic_sender = test_detail::basic_sender;
     static_assert(test_std::sender);
@@ -1089,7 +1095,7 @@ auto test_basic_sender() -> void {
     static_assert(std::same_as>);
     static_assert(
         std::same_as);
+                     decltype(test_std::transform_sender(test_std::default_domain{}, basic_sender{}, local_env{}))>);
     static_assert(test_std::sender_in);
 #if 0
         //-dk:TODO restore completion_sigatures_for test
diff --git a/tests/beman/execution26/exec-split.test.cpp b/tests/beman/execution26/exec-split.test.cpp
index df95a2e0..41bdc00f 100644
--- a/tests/beman/execution26/exec-split.test.cpp
+++ b/tests/beman/execution26/exec-split.test.cpp
@@ -142,10 +142,9 @@ template 
 using to_set_value_t = type_list;
 
 void test_completion_sigs_and_sync_wait_on_split() {
-    auto just          = beman::execution26::just(NonCopyable{});
-    auto split         = beman::execution26::split(std::move(just));
-    using split_sender = std::decay_t;
-    struct local_empty_env {};
+    auto just                        = beman::execution26::just(NonCopyable{});
+    auto split                       = beman::execution26::split(std::move(just));
+    using split_sender               = std::decay_t;
     using expected_value_completions = type_list;
     using value_completions =
         beman::execution26::value_types_of_t;
@@ -174,6 +173,7 @@ void test_completion_from_another_thread() {
     auto split     = beman::execution26::split(scheduler.schedule_after(1ms));
     auto return_42 = beman::execution26::then(split, [] { return 42; });
     auto result    = beman::execution26::sync_wait(return_42);
+    ASSERT(scheduler == scheduler); // avoid a warning about the required op== being unused
     ASSERT(result.has_value());
     if (result.has_value()) {
         auto [val] = *result;
diff --git a/tests/beman/execution26/exec-sync-wait.test.cpp b/tests/beman/execution26/exec-sync-wait.test.cpp
index 8b73d87a..5a650b42 100644
--- a/tests/beman/execution26/exec-sync-wait.test.cpp
+++ b/tests/beman/execution26/exec-sync-wait.test.cpp
@@ -29,8 +29,9 @@ struct arg {
     int  value{};
     auto operator==(const arg&) const -> bool = default;
 };
-struct error {
+struct error : std::exception {
     int value{};
+    explicit error(int v) : value(v) {}
 };
 struct sender {
     using sender_concept = test_std::sender_t;
@@ -136,21 +137,21 @@ auto test_sync_wait_state() -> void {
 
 auto test_sync_wait_receiver() -> void {
     {
-        using sender_t = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{}));
-        test_detail::sync_wait_state state{};
+        using local_sender = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{}));
+        test_detail::sync_wait_state state{};
         ASSERT(not state.result);
         ASSERT(not state.error);
-        test_std::set_value(test_detail::sync_wait_receiver{&state}, arg<0>{2}, arg<1>{3}, arg<2>{5});
+        test_std::set_value(test_detail::sync_wait_receiver{&state}, arg<0>{2}, arg<1>{3}, arg<2>{5});
         ASSERT(state.result);
         ASSERT(not state.error);
         ASSERT(*state.result == (std::tuple{arg<0>{2}, arg<1>{3}, arg<2>{5}}));
     }
     {
-        using sender_t = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{}));
-        test_detail::sync_wait_state state{};
+        using local_sender = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{}));
+        test_detail::sync_wait_state state{};
         ASSERT(not state.result);
         ASSERT(not state.error);
-        test_std::set_error(test_detail::sync_wait_receiver{&state}, error{17});
+        test_std::set_error(test_detail::sync_wait_receiver{&state}, error{17});
         ASSERT(not state.result);
         ASSERT(state.error);
         try {
@@ -163,11 +164,11 @@ auto test_sync_wait_receiver() -> void {
         }
     }
     {
-        using sender_t = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{}));
-        test_detail::sync_wait_state state{};
+        using local_sender = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{}));
+        test_detail::sync_wait_state state{};
         ASSERT(not state.result);
         ASSERT(not state.error);
-        test_std::set_error(test_detail::sync_wait_receiver{&state}, std::make_exception_ptr(error{17}));
+        test_std::set_error(test_detail::sync_wait_receiver{&state}, std::make_exception_ptr(error{17}));
         ASSERT(not state.result);
         ASSERT(state.error);
         try {
@@ -180,11 +181,11 @@ auto test_sync_wait_receiver() -> void {
         }
     }
     {
-        using sender_t = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{}));
-        test_detail::sync_wait_state state{};
+        using local_sender = decltype(test_std::just(arg<0>{}, arg<1>{}, arg<2>{}));
+        test_detail::sync_wait_state state{};
         ASSERT(not state.result);
         ASSERT(not state.error);
-        test_std::set_stopped(test_detail::sync_wait_receiver{&state});
+        test_std::set_stopped(test_detail::sync_wait_receiver{&state});
         ASSERT(not state.result);
         ASSERT(not state.error);
     }
diff --git a/tests/beman/execution26/exec-then.test.cpp b/tests/beman/execution26/exec-then.test.cpp
index 6938c02e..6875fecf 100644
--- a/tests/beman/execution26/exec-then.test.cpp
+++ b/tests/beman/execution26/exec-then.test.cpp
@@ -191,7 +191,7 @@ struct allocator_fun {
     allocator_fun(const allocator_fun&, std::pmr::polymorphic_allocator<> = {}) {}
     allocator_fun(allocator_fun&& other) noexcept : alloc(other.alloc), data(std::exchange(other.data, nullptr)) {}
     allocator_fun(allocator_fun&& other, std::pmr::polymorphic_allocator<> all)
-        : alloc(all), data(all == other.alloc ? std::exchange(other.data, nullptr) : alloc.allocate(1)) {}
+        : alloc(all), data(alloc == other.alloc ? std::exchange(other.data, nullptr) : alloc.allocate(1)) {}
     ~allocator_fun() {
         if (this->data)
             this->alloc.deallocate(this->data, 1u);
diff --git a/tests/beman/execution26/exec-with-awaitable-senders.test.cpp b/tests/beman/execution26/exec-with-awaitable-senders.test.cpp
index bedbaaa0..97c8eb57 100644
--- a/tests/beman/execution26/exec-with-awaitable-senders.test.cpp
+++ b/tests/beman/execution26/exec-with-awaitable-senders.test.cpp
@@ -52,10 +52,10 @@ coroutine test_await_void() { co_await exec::just(); }
 void test_sync_wait_awaitable() {
     bool not_reached{true};
     try {
-        auto [v] = exec::sync_wait(awaitable{}).value_or(::std::tuple(0));
+        auto [v] = exec::sync_wait(awaitable{}).value_or(std::tuple(0));
         ASSERT(v == 1);
     } catch (...) {
-        not_reached = false;
+        ASSERT_UNREACHABLE();
     }
     ASSERT(not_reached);
 }
@@ -64,7 +64,7 @@ void test_sync_wait_void_awaitable() {
     try {
         ASSERT(exec::sync_wait(void_awaitable{}));
     } catch (...) {
-        ASSERT(false);
+        ASSERT_UNREACHABLE();
     }
 }
 
diff --git a/tests/beman/execution26/include/test/execution.hpp b/tests/beman/execution26/include/test/execution.hpp
index aa87ee3d..55d0c190 100644
--- a/tests/beman/execution26/include/test/execution.hpp
+++ b/tests/beman/execution26/include/test/execution.hpp
@@ -12,6 +12,7 @@
 #include 
 
 #define ASSERT(condition) assert(condition)
+#define ASSERT_UNREACHABLE() assert(::test::unreachable_helper())
 #define TEST(name) auto main() -> int
 
 namespace beman::execution26 {}
@@ -20,6 +21,8 @@ namespace test_std    = ::beman::execution26;
 namespace test_detail = ::beman::execution26::detail;
 
 namespace test {
+inline bool unreachable_helper() { return false; }
+
 template 
 auto type_exists() {}
 template 
diff --git a/tests/beman/execution26/include/test/stop_token.hpp b/tests/beman/execution26/include/test/stop_token.hpp
index fde7f51d..6a126e7f 100644
--- a/tests/beman/execution26/include/test/stop_token.hpp
+++ b/tests/beman/execution26/include/test/stop_token.hpp
@@ -84,7 +84,7 @@ inline auto test::stop_callback(const Token& token, Stop stop) -> void {
 
     struct Callback {
         Data* data;
-        explicit Callback(Data* dat) : data(dat) {}
+        explicit Callback(Data* d) : data(d) {}
         auto operator()() {
             ++this->data->count;
             this->data->stop_requested = this->data->token.stop_requested();
@@ -124,7 +124,7 @@ auto test::stop_callback_dtor_deregisters(const Token& token, Stop stop) -> void
 
     struct Callback {
         bool* ptr;
-        explicit Callback(bool* pt) : ptr(pt) {}
+        explicit Callback(bool* p) : ptr(p) {}
         auto operator()() { *this->ptr = true; }
     };
 
@@ -166,7 +166,7 @@ inline auto test::stop_callback_dtor_other_thread(const Token& token, Stop stop)
     };
     struct Callback {
         Data* data;
-        explicit Callback(Data* dat) : data(dat) {}
+        explicit Callback(Data* d) : data(d) {}
         auto operator()() -> void {
             using namespace ::std::chrono_literals;
             {
@@ -221,7 +221,7 @@ inline auto test::stop_callback_dtor_same_thread(Token token, Stop stop) -> void
     };
     struct Callback {
         ::std::unique_ptr* self;
-        explicit Callback(::std::unique_ptr* slf) : self(slf) {}
+        explicit Callback(::std::unique_ptr* s) : self(s) {}
         auto operator()() { this->self->reset(); }
     };
     struct Object : Base {

From 8a9686ba186189ea3d3371cf351cec8a4b39af30 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dietmar=20K=C3=BChl?= 
Date: Sun, 22 Dec 2024 17:59:55 +0100
Subject: [PATCH 25/41] fixed the return type of getting an element from an
 rvalue product_type (#107)

---
 include/beman/execution26/detail/product_type.hpp |  2 +-
 tests/beman/execution26/exec-snd-expos.test.cpp   | 10 +++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/include/beman/execution26/detail/product_type.hpp b/include/beman/execution26/detail/product_type.hpp
index 414acff1..8b1d9e49 100644
--- a/include/beman/execution26/detail/product_type.hpp
+++ b/include/beman/execution26/detail/product_type.hpp
@@ -29,7 +29,7 @@ struct product_type_base<::std::index_sequence, T...>
         return self.value;
     }
     template <::std::size_t J, typename S>
-    static auto element_get(::beman::execution26::detail::product_type_element&& self) noexcept -> S {
+    static auto element_get(::beman::execution26::detail::product_type_element&& self) noexcept -> S&& {
         return ::std::move(self.value);
     }
     template <::std::size_t J, typename S>
diff --git a/tests/beman/execution26/exec-snd-expos.test.cpp b/tests/beman/execution26/exec-snd-expos.test.cpp
index f438472d..f27a2149 100644
--- a/tests/beman/execution26/exec-snd-expos.test.cpp
+++ b/tests/beman/execution26/exec-snd-expos.test.cpp
@@ -1253,11 +1253,11 @@ auto test_write_env() -> void {
 template 
 struct child_sender : test_detail::product_type {};
 auto test_child_type() -> void {
-    static_assert(std::same_as>>);
-    static_assert(std::same_as>&>);
-    static_assert(std::same_as>&>);
-    static_assert(std::same_as, 0>>);
-    static_assert(std::same_as, 1>>);
+    static_assert(std::same_as>>);
+    static_assert(std::same_as&>>);
+    static_assert(std::same_as&>>);
+    static_assert(std::same_as, 0>>);
+    static_assert(std::same_as, 1>>);
 }
 } // namespace
 

From 01af87e618271cf603b73f0f416a4c9d2b38a2a5 Mon Sep 17 00:00:00 2001
From: Radu Nichita <45298861+RaduNichita@users.noreply.github.com>
Date: Sun, 22 Dec 2024 23:34:01 +0200
Subject: [PATCH 26/41] beman.execution26: change library status to under
 development (#104)

Signed-off-by: Radu Nichita 
---
 README.md | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 037dc173..510241c9 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 -->
 # beman.execution26: Building Block For Asynchronous Programs
 
+
+
 `beman.execution26` provides the basic vocabulary for asynchronous
 programming as well as important algorithms implemented in terms
 of this vocabulary. The key entities of the vocabulary are:
@@ -33,7 +35,9 @@ e.g.:
     completed.
 - `bulk(...)` to executed execute work, potentially concurrently.
 
-**Implements:** [`std::execution` (P2300)](http://wg21.link/p2300).
+**Implements:** [`std::execution` (P2300R10)](http://wg21.link/P2300R10).
+
+**Status**: [Under development and not yet ready for production use.](https://github.com/bemanproject/beman/blob/main/docs/BEMAN_LIBRARY_MATURITY_MODEL.md#under-development-and-not-yet-ready-for-production-use)
 
 ## Help Welcome!
 

From fc84954824a0319fad4761220990b864cac96ba9 Mon Sep 17 00:00:00 2001
From: Maikel Nadolski 
Date: Mon, 23 Dec 2024 14:26:13 +0100
Subject: [PATCH 27/41] Fix missing environment in sync_wait (#108)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Fix missing environment in sync_wait

* sync_wait: add tests for its environment

* fixed a problem with env in sync_wait (and fixed the tests)

---------

Co-authored-by: Dietmar Kühl 
---
 include/beman/execution26/detail/sync_wait.hpp  |  5 ++++-
 tests/beman/execution26/exec-sync-wait.test.cpp | 14 ++++++++++++++
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/include/beman/execution26/detail/sync_wait.hpp b/include/beman/execution26/detail/sync_wait.hpp
index 694eb15a..b474d988 100644
--- a/include/beman/execution26/detail/sync_wait.hpp
+++ b/include/beman/execution26/detail/sync_wait.hpp
@@ -32,7 +32,7 @@ struct sync_wait_env {
     auto query(::beman::execution26::get_delegation_scheduler_t) const noexcept { return this->loop->get_scheduler(); }
 };
 
-template <::beman::execution26::sender_in Sender>
+template <::beman::execution26::sender_in<::beman::execution26::detail::sync_wait_env> Sender>
 using sync_wait_result_type =
     ::std::optional<::beman::execution26::value_types_of_tstate->loop.finish();
     }
+    auto get_env() const noexcept -> ::beman::execution26::detail::sync_wait_env {
+        return ::beman::execution26::detail::sync_wait_env{&this->state->loop};
+    }
 };
 
 struct sync_wait_t {
diff --git a/tests/beman/execution26/exec-sync-wait.test.cpp b/tests/beman/execution26/exec-sync-wait.test.cpp
index 5a650b42..f6fe31ee 100644
--- a/tests/beman/execution26/exec-sync-wait.test.cpp
+++ b/tests/beman/execution26/exec-sync-wait.test.cpp
@@ -13,6 +13,9 @@
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
 #include 
@@ -223,6 +226,15 @@ auto test_sync_wait() -> void {
         // NOLINTEND(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
     }
 }
+
+auto test_provides_scheduler() -> void {
+    ASSERT(test_std::sync_wait(test_std::then(test_std::read_env(test_std::get_scheduler), [](auto&&) noexcept {})));
+}
+
+auto test_provides_delegation_scheduler() -> void {
+    ASSERT(test_std::sync_wait(
+        test_std::then(test_std::read_env(test_std::get_delegation_scheduler), [](auto&&) noexcept {})));
+}
 } // namespace
 
 TEST(exec_sync_wait) {
@@ -236,4 +248,6 @@ TEST(exec_sync_wait) {
     test_sync_wait_state();
     test_sync_wait_receiver();
     test_sync_wait();
+    test_provides_scheduler();
+    test_provides_delegation_scheduler();
 }

From 35b70679d3787a0c6adf37db244d7ec4cfb8e9b1 Mon Sep 17 00:00:00 2001
From: Maikel Nadolski 
Date: Mon, 23 Dec 2024 20:46:17 +0100
Subject: [PATCH 28/41] Change exported install name to beman::execution26
 (#109)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Change exported install name to beman::execution26

* adjust the header update script and add missing headers

---------

Co-authored-by: Dietmar Kühl 
---
 CMakeLists.txt                         |  6 +++---
 bin/update-cmake-headers.py            |  2 +-
 examples/CMakeLists.txt                |  2 +-
 src/beman/execution26/CMakeLists.txt   | 30 +++++++++++++-------------
 tests/beman/execution26/CMakeLists.txt |  2 +-
 5 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 28b6a427..b71de488 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,10 +12,10 @@ if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
 endif()
 
 set(TARGET_NAME execution26)
-set(TARGET_NAMESPACE beman) # FIXME: not used in install(EXPORT ...) CK?
+set(TARGET_NAMESPACE beman)
 set(TARGET_PREFIX ${TARGET_NAMESPACE}.${TARGET_NAME})
 set(TARGET_LIBRARY ${PROJECT_NAME})
-set(TARGET_ALIAS ${TARGET_LIBRARY}::${TARGET_LIBRARY})
+set(TARGET_ALIAS ${TARGET_NAMESPACE}::${TARGET_NAME})
 set(TARGET_PACKAGE_NAME ${PROJECT_NAME}-config)
 set(TARGETS_EXPORT_NAME ${PROJECT_NAME}-targets)
 
@@ -40,7 +40,7 @@ if(CMAKE_BUILD_TYPE STREQUAL Debug)
   # uncomment to enable the options. Some of them accept one or more inputs:
   project_options(
     PREFIX
-    ${PROJECT_NAME}
+    ${TARGET_NAME}
     ENABLE_CACHE
     ENABLE_CLANG_TIDY
     # NO! ENABLE_VS_ANALYSIS
diff --git a/bin/update-cmake-headers.py b/bin/update-cmake-headers.py
index 69e0095a..7c53b0ce 100755
--- a/bin/update-cmake-headers.py
+++ b/bin/update-cmake-headers.py
@@ -29,7 +29,7 @@ def get_headers(dir):
 }
 
 file_set_re = re.compile(" *FILE_SET.*")
-section_re = re.compile(" *\${TARGET_LIBRARY}_(?P
.*)_headers$") +section_re = re.compile(" *\${TARGET_NAME}_(?P
.*)_headers$") header_re = re.compile(" *\${PROJECT_SOURCE_DIR}/include/beman/.*/.*\.hpp") if len(sys.argv) != 2: diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3a64e258..a5a991f2 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -21,5 +21,5 @@ foreach(EXAMPLE ${EXAMPLES}) set(EXAMPLE_TARGET ${TARGET_PREFIX}.examples.${EXAMPLE}) add_executable(${EXAMPLE_TARGET}) target_sources(${EXAMPLE_TARGET} PRIVATE ${EXAMPLE}.cpp) - target_link_libraries(${EXAMPLE_TARGET} PRIVATE ${TARGET_LIBRARY}) + target_link_libraries(${EXAMPLE_TARGET} PRIVATE ${TARGET_NAMESPACE}::${TARGET_NAME}) endforeach() diff --git a/src/beman/execution26/CMakeLists.txt b/src/beman/execution26/CMakeLists.txt index 3c3c1721..bc620f94 100644 --- a/src/beman/execution26/CMakeLists.txt +++ b/src/beman/execution26/CMakeLists.txt @@ -3,22 +3,22 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # cmake-format: on -add_library(${TARGET_LIBRARY} STATIC) -add_library(${TARGET_ALIAS} ALIAS ${TARGET_LIBRARY}) +add_library(${TARGET_NAME} STATIC) +add_library(${TARGET_ALIAS} ALIAS ${TARGET_NAME}) if(CMAKE_BUILD_TYPE STREQUAL Debug) - target_link_libraries(${TARGET_LIBRARY} PUBLIC $) - target_link_libraries(${TARGET_LIBRARY} PUBLIC $) + target_link_libraries(${TARGET_NAME} PUBLIC $) + target_link_libraries(${TARGET_NAME} PUBLIC $) endif() include(CMakePrintHelpers) -cmake_print_variables(TARGET_ALIAS TARGET_LIBRARY TARGET_PREFIX PROJECT_SOURCE_DIR) +cmake_print_variables(TARGET_ALIAS TARGET_NAME TARGET_PREFIX PROJECT_SOURCE_DIR) target_sources( - ${TARGET_LIBRARY} + ${TARGET_NAME} PRIVATE execution.cpp PUBLIC FILE_SET - ${TARGET_LIBRARY}_public_headers + ${TARGET_NAME}_public_headers TYPE HEADERS BASE_DIRS @@ -29,7 +29,7 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution26/functional.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/stop_token.hpp PUBLIC FILE_SET - ${TARGET_LIBRARY}_detail_headers + ${TARGET_NAME}_detail_headers TYPE HEADERS BASE_DIRS @@ -198,22 +198,22 @@ target_sources( ) # cmake-format: off -get_property(DETAIL_HEADER_FILES TARGET ${TARGET_LIBRARY} PROPERTY HEADER_SET_${TARGET_LIBRARY}_detail_headers) +get_property(DETAIL_HEADER_FILES TARGET ${TARGET_NAME} PROPERTY HEADER_SET_${TARGET_NAME}_detail_headers) source_group("Header Files\\detail" FILES ${DETAIL_HEADER_FILES}) -set_target_properties(${TARGET_LIBRARY} PROPERTIES VERIFY_INTERFACE_HEADER_SETS ON) +set_target_properties(${TARGET_NAME} PROPERTIES VERIFY_INTERFACE_HEADER_SETS ON) -target_compile_features(${TARGET_LIBRARY} PUBLIC +target_compile_features(${TARGET_NAME} PUBLIC "$<$:cxx_std_26>" "$<$>:cxx_std_23>" ) install( - TARGETS ${TARGET_LIBRARY} + TARGETS ${TARGET_NAME} EXPORT ${TARGETS_EXPORT_NAME}1 ARCHIVE DESTINATION lib/$ - FILE_SET ${TARGET_LIBRARY}_public_headers - FILE_SET ${TARGET_LIBRARY}_detail_headers + FILE_SET ${TARGET_NAME}_public_headers + FILE_SET ${TARGET_NAME}_detail_headers ) # cmake-format: on @@ -221,5 +221,5 @@ install( EXPORT ${TARGETS_EXPORT_NAME}1 FILE ${TARGETS_EXPORT_NAME}.cmake DESTINATION "${INSTALL_CONFIGDIR}" - NAMESPACE ${TARGET_LIBRARY}:: + NAMESPACE ${TARGET_NAMESPACE}:: ) diff --git a/tests/beman/execution26/CMakeLists.txt b/tests/beman/execution26/CMakeLists.txt index 2ddb2dc5..4a2f6822 100644 --- a/tests/beman/execution26/CMakeLists.txt +++ b/tests/beman/execution26/CMakeLists.txt @@ -131,7 +131,7 @@ remove_definitions(-DNDEBUG) # NOTE: we want ASSERT statements in Release too! C foreach(test ${execution_tests}) set(TEST_EXE ${TARGET_PREFIX}.${test}) add_executable(${TEST_EXE} ${test}.cpp) - target_link_libraries(${TEST_EXE} PRIVATE beman_execution26::beman_execution26) + target_link_libraries(${TEST_EXE} PRIVATE beman::execution26) add_test(NAME ${TEST_EXE} COMMAND $) endforeach() From 9c1d681ce8b3faf4172388b5cdbe6acfc18587a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Sat, 4 Jan 2025 15:07:00 +0000 Subject: [PATCH 29/41] Add on algorithm (#110) * started to implement on * added structured binding support for types derived from product type * added on algorithm and applies various fixes * fix a silly formatting error * fixed a few warnings --- .../beman/execution26/detail/basic_sender.hpp | 6 +- include/beman/execution26/detail/connect.hpp | 4 +- include/beman/execution26/detail/on.hpp | 169 ++++++++++++++++++ .../beman/execution26/detail/product_type.hpp | 22 ++- .../execution26/detail/schedule_from.hpp | 26 ++- .../detail/sender_adaptor_closure.hpp | 8 + .../execution26/detail/transform_sender.hpp | 25 ++- include/beman/execution26/execution.hpp | 3 +- src/beman/execution26/CMakeLists.txt | 1 + tests/beman/execution26/CMakeLists.txt | 1 + tests/beman/execution26/exec-on.test.cpp | 134 ++++++++++++++ .../beman/execution26/exec-snd-expos.test.cpp | 21 +++ .../execution26/include/test/thread_pool.hpp | 117 ++++++++++++ 13 files changed, 505 insertions(+), 32 deletions(-) create mode 100644 include/beman/execution26/detail/on.hpp create mode 100644 tests/beman/execution26/exec-on.test.cpp create mode 100644 tests/beman/execution26/include/test/thread_pool.hpp diff --git a/include/beman/execution26/detail/basic_sender.hpp b/include/beman/execution26/detail/basic_sender.hpp index af0d0272..ce765f1e 100644 --- a/include/beman/execution26/detail/basic_sender.hpp +++ b/include/beman/execution26/detail/basic_sender.hpp @@ -74,7 +74,7 @@ struct basic_sender : ::beman::execution26::detail::product_type(self), ::std::move(receiver)}; } #endif -#if __cpp_explicit_this_parameter < 202110L +#if __cpp_explicit_this_parameter < 302110L template auto get_completion_signatures(Env&&) && -> ::beman::execution26::detail::completion_signatures_for { @@ -82,7 +82,7 @@ struct basic_sender : ::beman::execution26::detail::product_type auto get_completion_signatures( - Env&&) const&& -> ::beman::execution26::detail::completion_signatures_for { + Env&&) const&& -> ::beman::execution26::detail::completion_signatures_for { return {}; } template @@ -92,7 +92,7 @@ struct basic_sender : ::beman::execution26::detail::product_type auto get_completion_signatures( - Env&&) const& -> ::beman::execution26::detail::completion_signatures_for { + Env&&) const& -> ::beman::execution26::detail::completion_signatures_for { return {}; } #else diff --git a/include/beman/execution26/detail/connect.hpp b/include/beman/execution26/detail/connect.hpp index 0ec5a2c8..f47b3631 100644 --- a/include/beman/execution26/detail/connect.hpp +++ b/include/beman/execution26/detail/connect.hpp @@ -23,7 +23,9 @@ namespace beman::execution26::detail { struct connect_t { private: template - static auto make_new_sender(Sender&& sender, Receiver&& receiver) noexcept(true) -> decltype(auto) { + static auto make_new_sender(Sender&& sender, Receiver&& receiver) + //-dk:TODO this noexcept needs to get confirmed/fixed + noexcept(true) -> decltype(auto) { return ::beman::execution26::transform_sender( decltype(::beman::execution26::detail::get_domain_late(::std::forward(sender), ::beman::execution26::get_env(receiver))){}, diff --git a/include/beman/execution26/detail/on.hpp b/include/beman/execution26/detail/on.hpp new file mode 100644 index 00000000..10d4a443 --- /dev/null +++ b/include/beman/execution26/detail/on.hpp @@ -0,0 +1,169 @@ +// include/beman/execution26/detail/on.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_INCLUDE_BEMAN_EXECUTION26_DETAIL_ON +#define INCLUDED_INCLUDE_BEMAN_EXECUTION26_DETAIL_ON + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// ---------------------------------------------------------------------------- + +namespace beman::execution26::detail { +struct on_t : ::beman::execution26::sender_adaptor_closure { + template <::beman::execution26::detail::sender_for OutSndr, typename Env> + auto transform_env(OutSndr&& out_sndr, Env&& env) const -> decltype(auto) { + auto&& data{out_sndr.template get<1>()}; + + if constexpr (::beman::execution26::scheduler) + return ::beman::execution26::detail::join_env( + ::beman::execution26::detail::sched_env(::beman::execution26::detail::forward_like(data) + + ), + ::beman::execution26::detail::fwd_env(::std::forward(env))); + else + return std::forward(env); + } + + template + struct env_needs_get_scheduler { + using sender_concept = ::beman::execution26::sender_t; + template + auto get_completion_signatures(Env&&) const { + return env_needs_get_scheduler{}; + } + }; + + template <::beman::execution26::detail::sender_for OutSndr, typename Env> + auto transform_sender(OutSndr&& out_sndr, Env&& env) const -> decltype(auto) { + struct not_a_scheduler {}; + auto&& [_, data, child] = out_sndr; + + if constexpr (::beman::execution26::scheduler) { + auto sch{::beman::execution26::detail::query_with_default( + ::beman::execution26::get_scheduler, env, not_a_scheduler{})}; + if constexpr (::std::same_as) { + return env_needs_get_scheduler{}; + } else { + return ::beman::execution26::continues_on( + ::beman::execution26::starts_on(::beman::execution26::detail::forward_like(data), + ::beman::execution26::detail::forward_like(child)), + ::std::move(sch)); + } + } else { + auto& [sch, closure] = data; + auto orig_sch{::beman::execution26::detail::query_with_default( + ::beman::execution26::get_completion_scheduler<::beman::execution26::set_value_t>, + ::beman::execution26::get_env(child), + ::beman::execution26::detail::query_with_default( + ::beman::execution26::get_scheduler, env, not_a_scheduler{}))}; + + if constexpr (::std::same_as) { + return env_needs_get_scheduler{}; + } else { + return ::beman::execution26::detail::write_env( + ::beman::execution26::continues_on( + ::beman::execution26::detail::forward_like(closure)( + ::beman::execution26::continues_on( + ::beman::execution26::detail::write_env( + ::beman::execution26::detail::forward_like(child), + ::beman::execution26::detail::sched_env(orig_sch)), + sch)), + orig_sch), + ::beman::execution26::detail::sched_env(env)); + } + } + } + + template <::beman::execution26::scheduler Sch, ::beman::execution26::sender Sndr> + requires ::beman::execution26::detail::is_sender_adaptor_closure + auto operator()(Sch&&, Sndr&&) const -> void = + BEMAN_EXECUTION26_DELETE("on(sch, sndr) requires that sndr isn't both a sender and sender adaptor closure"); + + template <::beman::execution26::scheduler Sch, + ::beman::execution26::sender Sndr, + ::beman::execution26::detail::is_sender_adaptor_closure Closure> + requires ::beman::execution26::detail::is_sender_adaptor_closure + auto operator()(Sndr&&, Sch&&, Closure&&) const -> void = + BEMAN_EXECUTION26_DELETE("on(sch, sndr) requires that sndr isn't both a sender and sender adaptor closure"); + + template <::beman::execution26::scheduler Sch, ::beman::execution26::sender Sndr> + auto operator()(Sch&& sch, Sndr&& sndr) const { + auto domain{::beman::execution26::detail::query_with_default( + ::beman::execution26::get_domain, sch, ::beman::execution26::default_domain{})}; + return ::beman::execution26::transform_sender( + domain, + ::beman::execution26::detail::make_sender(*this, ::std::forward(sch), ::std::forward(sndr))); + } + template <::beman::execution26::scheduler Sch, + ::beman::execution26::sender Sndr, + ::beman::execution26::detail::is_sender_adaptor_closure Closure> + auto operator()(Sndr&& sndr, Sch&& sch, Closure&& closure) const { + auto domain{::beman::execution26::detail::get_domain_early(sndr)}; + return ::beman::execution26::transform_sender( + domain, + ::beman::execution26::detail::make_sender( + *this, + ::beman::execution26::detail::product_type{::std::forward(sch), ::std::forward(closure)}, + ::std::forward(sndr))); + } + template <::beman::execution26::scheduler Sch, ::beman::execution26::detail::is_sender_adaptor_closure Closure> + auto operator()(Sch&& sch, Closure&& closure) const { + return ::beman::execution26::detail::sender_adaptor{ + *this, ::std::forward(sch), ::std::forward(closure)}; + } +}; + +#if 0 +template +struct completion_signatures_for_impl< + ::beman::execution26::detail::basic_sender<::beman::execution26::detail::on_t, Data, Sender>, + Env> { + //-dk:TODO pick up scheduler errors and merge them in? + using type = +#if 0 + ::beman::execution26::detail::meta::combine< + ::beman::execution26::completion_signatures_of_t, + ::beman::execution26::completion_signatures<::beman::execution26::set_error_t(::std::exception_ptr)> + > +#else + ::beman::execution26::completion_signatures< + ::beman::execution26::set_value_t(), + ::beman::execution26::set_error_t(::std::exception_ptr) + > +#endif + ; +}; +#endif + +} // namespace beman::execution26::detail + +namespace beman::execution26 { +using on_t = ::beman::execution26::detail::on_t; +inline constexpr ::beman::execution26::on_t on{}; +} // namespace beman::execution26 + +// ---------------------------------------------------------------------------- + +#include + +#endif diff --git a/include/beman/execution26/detail/product_type.hpp b/include/beman/execution26/detail/product_type.hpp index 8b1d9e49..fdfaec3b 100644 --- a/include/beman/execution26/detail/product_type.hpp +++ b/include/beman/execution26/detail/product_type.hpp @@ -10,7 +10,8 @@ // ---------------------------------------------------------------------------- namespace beman::execution26::detail { -template <::std::size_t, typename T> + +template <::std::size_t I, typename T> struct product_type_element { T value; auto operator==(const product_type_element&) const -> bool = default; @@ -23,6 +24,7 @@ template <::std::size_t... I, typename... T> struct product_type_base<::std::index_sequence, T...> : ::beman::execution26::detail::product_type_element... { static constexpr ::std::size_t size() { return sizeof...(T); } + static constexpr bool is_product_type{true}; template <::std::size_t J, typename S> static auto element_get(::beman::execution26::detail::product_type_element& self) noexcept -> S& { @@ -64,6 +66,9 @@ struct product_type_base<::std::index_sequence, T...> auto operator==(const product_type_base&) const -> bool = default; }; +template +concept is_product_type_c = requires(const T& t) { T::is_product_type; }; + template struct product_type : ::beman::execution26::detail::product_type_base<::std::index_sequence_for, T...> { template @@ -108,13 +113,14 @@ constexpr auto is_product_type(const ::beman::execution26::detail::product_type< } // namespace beman::execution26::detail namespace std { -template -struct tuple_size<::beman::execution26::detail::product_type> - : ::std::integral_constant {}; -template <::std::size_t I, typename... T> -struct tuple_element> { - using type = - ::std::decay_t>().template get())>; +template + requires ::beman::execution26::detail::is_product_type_c +struct tuple_size : ::std::integral_constant {}; + +template <::std::size_t I, typename T> + requires ::beman::execution26::detail::is_product_type_c +struct tuple_element { + using type = ::std::decay_t().template get())>; }; } // namespace std diff --git a/include/beman/execution26/detail/schedule_from.hpp b/include/beman/execution26/detail/schedule_from.hpp index b0ac6e20..9894e830 100644 --- a/include/beman/execution26/detail/schedule_from.hpp +++ b/include/beman/execution26/detail/schedule_from.hpp @@ -5,11 +5,13 @@ #define INCLUDED_BEMAN_EXECUTION26_DETAIL_SCHEDULE_FROM #include +#include #include #include #include #include #include +#include #include #include #include @@ -144,10 +146,15 @@ struct impls_for<::beman::execution26::detail::schedule_from_t> : ::beman::execu ::std::monostate, ::beman::execution26::detail::meta::transform< ::beman::execution26::detail::as_tuple_t, - ::beman::execution26::detail::meta::to<::std::variant, - ::beman::execution26::completion_signatures_of_t< - ::beman::execution26::detail::child_type, - ::beman::execution26::env_of_t>>>>>; + ::beman::execution26::detail::meta::to< + ::std::variant, + ::beman::execution26::detail::meta::combine< + ::beman::execution26::completion_signatures_of_t< + ::beman::execution26::detail::child_type, + ::beman::execution26::env_of_t>, + //-dk:TODO get proper error completion signatures + ::beman::execution26::completion_signatures<::beman::execution26::set_error_t( + ::std::exception_ptr)>>>>>>; return state_type(sch, receiver); }}; @@ -176,8 +183,15 @@ template struct completion_signatures_for_impl< ::beman::execution26::detail::basic_sender<::beman::execution26::detail::schedule_from_t, Scheduler, Sender>, Env> { - using type = - decltype(::beman::execution26::get_completion_signatures(::std::declval(), ::std::declval())); + using scheduler_sender = decltype(::beman::execution26::schedule(::std::declval())); + template + using as_set_error = ::beman::execution26::completion_signatures<::beman::execution26::set_error_t(E)...>; + using type = ::beman::execution26::detail::meta::combine< + decltype(::beman::execution26::get_completion_signatures(::std::declval(), ::std::declval())), + ::beman::execution26::error_types_of_t, + ::beman::execution26::completion_signatures<::beman::execution26::set_error_t( + ::std::exception_ptr)> //-dk:TODO this one should be deduced + >; }; } // namespace beman::execution26::detail diff --git a/include/beman/execution26/detail/sender_adaptor_closure.hpp b/include/beman/execution26/detail/sender_adaptor_closure.hpp index 53945dc2..23da0705 100644 --- a/include/beman/execution26/detail/sender_adaptor_closure.hpp +++ b/include/beman/execution26/detail/sender_adaptor_closure.hpp @@ -20,8 +20,16 @@ namespace beman::execution26 { template struct sender_adaptor_closure : ::beman::execution26::detail::pipeable::sender_adaptor_closure_base {}; // NOLINTEND(bugprone-crtp-constructor-accessibility) + } // namespace beman::execution26 +namespace beman::execution26::detail { +template +concept is_sender_adaptor_closure = + ::std::derived_from<::std::decay_t, + ::beman::execution26::sender_adaptor_closure<::std::decay_t>>; +} + namespace beman::execution26::detail::pipeable { template <::beman::execution26::sender Sender, typename Adaptor> requires(not::beman::execution26::sender) && diff --git a/include/beman/execution26/detail/transform_sender.hpp b/include/beman/execution26/detail/transform_sender.hpp index 376a3b6b..def72c09 100644 --- a/include/beman/execution26/detail/transform_sender.hpp +++ b/include/beman/execution26/detail/transform_sender.hpp @@ -16,9 +16,9 @@ template requires(Domain dom, Sender&& sender, const Env&... env) { dom.transform_sender(::std::forward(sender), env...); } && - (::std::same_as< - ::std::remove_cvref_t, - std::remove_cvref_t().transform_sender(::std::declval()))>>) + (::std::same_as<::std::remove_cvref_t, + std::remove_cvref_t().transform_sender( + ::std::declval(), ::std::declval()...))>>) constexpr auto transform_sender(Domain, Sender&& sender, const Env&...) noexcept -> ::beman::execution26::sender auto { return ::std::forward(sender); } @@ -28,9 +28,9 @@ template requires(Domain dom, Sender&& sender, const Env&... env) { dom.transform_sender(::std::forward(sender), env...); } && - (not::std::same_as< - ::std::remove_cvref_t, - std::remove_cvref_t().transform_sender(::std::declval()))>>) + (not::std::same_as<::std::remove_cvref_t, + std::remove_cvref_t().transform_sender( + ::std::declval(), ::std::declval()...))>>) constexpr auto transform_sender(Domain dom, Sender&& sender, const Env&... env) noexcept -> ::beman::execution26::sender decltype(auto) { return ::beman::execution26::detail::transform_sender( @@ -60,7 +60,6 @@ template constexpr auto transform_sender(Domain dom, Sender&& sender, const Env&... env) noexcept(noexcept( ::beman::execution26::default_domain{}.transform_sender(::std::declval(), ::std::declval()...))) -> ::beman::execution26::sender decltype(auto) { - (void)dom; return ::beman::execution26::detail::transform_sender( dom, ::beman::execution26::default_domain{}.transform_sender(::std::forward(sender), env...), env...); } @@ -72,9 +71,9 @@ template requires(Domain dom, Sender&& sender, const Env&... env) { dom.transform_sender(::std::forward(sender), env...); } && - (::std::same_as< - ::std::remove_cvref_t, - std::remove_cvref_t().transform_sender(::std::declval()))>>) + (::std::same_as<::std::remove_cvref_t, + std::remove_cvref_t().transform_sender( + ::std::declval(), ::std::declval()...))>>) constexpr auto transform_sender(Domain, Sender&& sender, const Env&...) noexcept -> ::beman::execution26::sender decltype(auto) { return ::std::forward(sender); @@ -85,9 +84,9 @@ template requires(Domain dom, Sender&& sender, const Env&... env) { dom.transform_sender(::std::forward(sender), env...); } && - (not::std::same_as< - ::std::remove_cvref_t, - std::remove_cvref_t().transform_sender(::std::declval()))>>) + (not::std::same_as<::std::remove_cvref_t, + std::remove_cvref_t().transform_sender( + ::std::declval(), ::std::declval()...))>>) constexpr auto transform_sender(Domain dom, Sender&& sender, const Env&... env) noexcept -> ::beman::execution26::sender auto { return ::beman::execution26::detail::transform_sender( diff --git a/include/beman/execution26/execution.hpp b/include/beman/execution26/execution.hpp index 10f33ede..78cfd62e 100644 --- a/include/beman/execution26/execution.hpp +++ b/include/beman/execution26/execution.hpp @@ -42,15 +42,16 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include +#include // ---------------------------------------------------------------------------- diff --git a/src/beman/execution26/CMakeLists.txt b/src/beman/execution26/CMakeLists.txt index bc620f94..47a1c512 100644 --- a/src/beman/execution26/CMakeLists.txt +++ b/src/beman/execution26/CMakeLists.txt @@ -131,6 +131,7 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/nostopstate.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/nothrow_callable.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/notify.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/on.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/on_stop_request.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/operation_state.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution26/detail/operation_state_task.hpp diff --git a/tests/beman/execution26/CMakeLists.txt b/tests/beman/execution26/CMakeLists.txt index 4a2f6822..67f6344c 100644 --- a/tests/beman/execution26/CMakeLists.txt +++ b/tests/beman/execution26/CMakeLists.txt @@ -18,6 +18,7 @@ list( async-concurrent-queue.test concurrent-queue.test basic-concurrent-queue.test + exec-on.test notify.test exec-scounting.test exec-awaitable.test diff --git a/tests/beman/execution26/exec-on.test.cpp b/tests/beman/execution26/exec-on.test.cpp new file mode 100644 index 00000000..be930dc9 --- /dev/null +++ b/tests/beman/execution26/exec-on.test.cpp @@ -0,0 +1,134 @@ +// tests/beman/execution26/exec-on.test.cpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// ---------------------------------------------------------------------------- + +namespace { +struct both : test_std::sender_adaptor_closure { + using sender_concept = test_std::sender_t; +}; + +static_assert(test_std::sender); +static_assert(test_detail::is_sender_adaptor_closure); + +template +auto test_interface(Sch sch, Sndr sndr, Closure closure, Both both) -> void { + static_assert(requires { + { test_std::on(sch, sndr) } -> test_std::sender; + }); + static_assert(not requires { test_std::on(sch, both); }); + static_assert(requires { + { test_std::on(sndr, sch, closure) } -> test_std::sender; + }); + static_assert(not requires { test_std::on(both, sch, closure); }); + + auto sndr1{test_std::on(sch, sndr)}; + auto sndr2{test_std::on(sndr, sch, closure)}; + test::use(sndr1, sndr2); +} + +template OutSndr> +auto test_transform_env(OutSndr out_sndr) -> void { + auto e{test_std::on.transform_env(out_sndr, test_std::empty_env{})}; + test::use(e); +} + +template OutSndr> +auto test_transform_sender(OutSndr out_sndr) -> void { + auto s{test_std::on.transform_sender(std::move(out_sndr), test_std::empty_env{})}; + static_assert(test_std::sender); + auto ts{std::move(s) | test_std::then([](auto&&...) {})}; + static_assert(test_std::sender); +} + +struct on_receiver { + using receiver_concept = test_std::receiver_t; + test::thread_pool& pool; + auto set_value(auto&&...) && noexcept {} + auto set_error(auto&&) && noexcept {} + auto set_stopped() && noexcept {} + auto get_env() const noexcept { return test_detail::make_env(test_std::get_scheduler, pool.get_scheduler()); } +}; +static_assert(test_std::receiver); +} // namespace + +TEST(exec_on) { + test::thread_pool pool{}; + + static_assert(std::same_as); + static_assert(test_detail::is_sender_adaptor_closure); + static_assert(not test_detail::is_sender_adaptor_closure); + test_interface(pool.get_scheduler(), test_std::just(), test_std::then([] {}), both{}); + + test_transform_env(test_detail::make_sender(test_std::on, pool.get_scheduler(), test_std::just())); + test_transform_env(test_detail::make_sender( + test_std::on, + ::beman::execution26::detail::product_type{pool.get_scheduler(), test_std::then([] {})}, + test_std::just())); + + test_transform_sender(test_detail::make_sender(test_std::on, pool.get_scheduler(), test_std::just())); + test_transform_sender(test_detail::make_sender( + test_std::on, + ::beman::execution26::detail::product_type{pool.get_scheduler(), test_std::then([] {})}, + test_std::just())); + + std::thread::id on_id{}; + std::thread::id pool_id{}; + std::thread::id cont_id{}; + + test_std::sync_wait(test_std::starts_on(pool.get_scheduler(), test_std::just() | test_std::then([&pool_id] { + pool_id = std::this_thread::get_id(); + }))); + test_std::sync_wait(test_std::on(pool.get_scheduler(), test_std::just() | test_std::then([&on_id] { + on_id = std::this_thread::get_id(); + return 42; + })) | + test_std::then([&cont_id](int val) { + assert(val == 42); + cont_id = std::this_thread::get_id(); + })); + assert(on_id == pool_id); + assert(cont_id == std::this_thread::get_id()); + assert(on_id != std::this_thread::get_id()); + + test_std::sync_wait(test_std::on(test_std::just(17), pool.get_scheduler(), test_std::then([&on_id](int val) { + assert(val == 17); + on_id = std::this_thread::get_id(); + return 42; + })) | + test_std::then([&cont_id](int val) { + assert(val == 42); + cont_id = std::this_thread::get_id(); + })); + assert(on_id == pool_id); + assert(cont_id == std::this_thread::get_id()); + assert(on_id != std::this_thread::get_id()); + + test_std::sync_wait(test_std::just(17) | test_std::on(pool.get_scheduler(), test_std::then([&on_id](int val) { + assert(val == 17); + on_id = std::this_thread::get_id(); + return 42; + })) | + test_std::then([&cont_id](int val) { + assert(val == 42); + cont_id = std::this_thread::get_id(); + })); + assert(on_id == pool_id); + assert(cont_id == std::this_thread::get_id()); + assert(on_id != std::this_thread::get_id()); +} diff --git a/tests/beman/execution26/exec-snd-expos.test.cpp b/tests/beman/execution26/exec-snd-expos.test.cpp index f27a2149..1e8fa54a 100644 --- a/tests/beman/execution26/exec-snd-expos.test.cpp +++ b/tests/beman/execution26/exec-snd-expos.test.cpp @@ -858,6 +858,27 @@ auto test_product_type() -> void { ASSERT(p5.get<2>() == nm(3)); ASSERT(p5.get<3>() == nm(4)); ASSERT(p5.get<4>() == nm(5)); + + test_detail::product_type prod{1, true, 'c'}; + static_assert(test_detail::is_product_type_c); + static_assert(3u == std::tuple_size::value); + static_assert(std::same_as::type>); + static_assert(std::same_as::type>); + static_assert(std::same_as::type>); + auto&& [i, b, c] = prod; + test::use(i, b, c); + + struct derived : decltype(prod) {}; + static_assert(3u == std::tuple_size::value); + static_assert(std::same_as::type>); + static_assert(std::same_as::type>); + static_assert(std::same_as::type>); + derived d{1, true, 'c'}; + auto&& [di, db, dc] = d; + assert(di == d.get<0>()); + assert(db == d.get<1>()); + assert(dc == d.get<2>()); + test::use(di, db, dc); } auto test_connect_all() -> void { static_assert(test_std::operation_state>); diff --git a/tests/beman/execution26/include/test/thread_pool.hpp b/tests/beman/execution26/include/test/thread_pool.hpp new file mode 100644 index 00000000..5313c128 --- /dev/null +++ b/tests/beman/execution26/include/test/thread_pool.hpp @@ -0,0 +1,117 @@ +// tests/beman/execution26/include/test/thread_pool.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// ---------------------------------------------------------------------------- + +#ifndef INCLUDED_TESTS_BEMAN_EXECUTION26_INCLUDE_TEST_THREAD_POOL +#define INCLUDED_TESTS_BEMAN_EXECUTION26_INCLUDE_TEST_THREAD_POOL + +#include +#include + +#include +#include +#include +#include +// ---------------------------------------------------------------------------- + +namespace test { +struct thread_pool; +} + +struct test::thread_pool { + struct node { + node* next{}; + virtual void run() = 0; + + node(const node&) = delete; + node(node&&) = delete; + node& operator=(const node&) = delete; + node& operator=(node&&) = delete; + + protected: + node() = default; + ~node() = default; + }; + + std::mutex mutex; + std::condition_variable condition; + node* stack{}; + bool stopped{false}; + std::thread driver{[this] { + while (std::optional n = [this] { + std::unique_lock cerberus(mutex); + condition.wait(cerberus, [this] { return stopped || stack; }); + return this->stack ? std::optional(std::exchange(this->stack, this->stack->next)) + : std::optional(); + }()) { + (*n)->run(); + } + }}; + + thread_pool() = default; + thread_pool(thread_pool&&) = delete; + thread_pool(const thread_pool&) = delete; + ~thread_pool() { + this->stop(); + this->driver.join(); + } + thread_pool& operator=(thread_pool&&) = delete; + thread_pool& operator=(const thread_pool&) = delete; + void stop() { + { + std::lock_guard cerberus(this->mutex); + stopped = true; + } + this->condition.notify_one(); + } + + struct scheduler { + using scheduler_concept = test_std::scheduler_t; + struct env { + test::thread_pool* pool; + + template + scheduler query(const test_std::get_completion_scheduler_t&) const noexcept { + return {this->pool}; + } + }; + template + struct state final : test::thread_pool::node { + using operation_state_concept = test_std::operation_state_t; + std::remove_cvref_t receiver; + test::thread_pool* pool; + + template + state(R&& r, test::thread_pool* p) : node{}, receiver(std::forward(r)), pool(p) {} + void start() & noexcept { + { + std::lock_guard cerberus(this->pool->mutex); + this->next = std::exchange(this->pool->stack, this); + } + this->pool->condition.notify_one(); + } + void run() override { test_std::set_value(std::move(this->receiver)); } + }; + struct sender { + using sender_concept = test_std::sender_t; + using completion_signatures = test_std::completion_signatures; + test::thread_pool* pool; + template + state connect(Receiver&& receiver) { + return state(std::forward(receiver), pool); + } + + env get_env() const noexcept { return {this->pool}; } + }; + test::thread_pool* pool; + sender schedule() { return {this->pool}; } + bool operator==(const scheduler&) const = default; + }; + scheduler get_scheduler() { return {this}; } +}; + +static_assert(test_std::scheduler); + +// ---------------------------------------------------------------------------- + +#endif From 7b135bd315b6cc6869be4cfc8ac47c1a893f3a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Thu, 9 Jan 2025 08:13:51 +0000 Subject: [PATCH 30/41] fixed a few coroutine related issues (#113) * fixed a few coroutine related issues * fixed formatting issues * reverted te chnage checking the result from unhandled_stopped(). --- include/beman/execution26/detail/product_type.hpp | 4 ++++ .../beman/execution26/detail/single_sender_value_type.hpp | 2 ++ tests/beman/execution26/execution-syn.test.cpp | 8 ++++---- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/beman/execution26/detail/product_type.hpp b/include/beman/execution26/detail/product_type.hpp index fdfaec3b..b309f95c 100644 --- a/include/beman/execution26/detail/product_type.hpp +++ b/include/beman/execution26/detail/product_type.hpp @@ -7,6 +7,8 @@ #include #include +#include + // ---------------------------------------------------------------------------- namespace beman::execution26::detail { @@ -126,4 +128,6 @@ struct tuple_element { // ---------------------------------------------------------------------------- +#include + #endif diff --git a/include/beman/execution26/detail/single_sender_value_type.hpp b/include/beman/execution26/detail/single_sender_value_type.hpp index e8bb5501..a1055426 100644 --- a/include/beman/execution26/detail/single_sender_value_type.hpp +++ b/include/beman/execution26/detail/single_sender_value_type.hpp @@ -27,6 +27,8 @@ struct single_sender_value_type_helper { template requires ::std::same_as<::std::variant<::std::tuple<>>, + ::beman::execution26::value_types_of_t> || + ::std::same_as<::std::variant<>, ::beman::execution26::value_types_of_t> struct single_sender_value_type_helper { using type = void; diff --git a/tests/beman/execution26/execution-syn.test.cpp b/tests/beman/execution26/execution-syn.test.cpp index 06f5e6b6..8c00562b 100644 --- a/tests/beman/execution26/execution-syn.test.cpp +++ b/tests/beman/execution26/execution-syn.test.cpp @@ -225,18 +225,18 @@ auto test_single_sender_value_type() -> void { test_detail::single_sender_value_type>); test_single_sender_value_type(single_type_sender{}, test_std::empty_env{}); test_single_sender_value_type(single_type_sender{}, test_env{}); - test_single_sender_value_type(single_type_sender{}, no_value_env{}); + test_single_sender_value_type(single_type_sender{}, no_value_env{}); test_single_sender_value_type(multi_single_sender{}, test_std::empty_env{}); - test_single_sender_value_type(no_value_sender{}, test_std::empty_env{}); + test_single_sender_value_type(no_value_sender{}, test_std::empty_env{}); } auto test_single_sender() -> void { static_assert(test_detail::single_sender); - static_assert(not test_detail::single_sender); + static_assert(test_detail::single_sender); static_assert(test_detail::single_sender); static_assert(not test_detail::single_sender); static_assert(test_detail::single_sender); - static_assert(not test_detail::single_sender); + static_assert(test_detail::single_sender); } struct connect_sender { From 9e4bfb3bc49d09a772678a248c7e28a0236fe96a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Fri, 10 Jan 2025 08:31:11 +0000 Subject: [PATCH 31/41] Fix coro issues (#114) * fixed a few coroutine related issues * fixed formatting issues * reverted te chnage checking the result from unhandled_stopped(). * addressed and issue which resulted in requiring a copy ctor * removed a static_assert left for testing * clang-format --- include/beman/execution26/detail/basic_operation.hpp | 6 ++++-- include/beman/execution26/detail/continues_on.hpp | 4 +++- include/beman/execution26/detail/sync_wait.hpp | 6 ++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/include/beman/execution26/detail/basic_operation.hpp b/include/beman/execution26/detail/basic_operation.hpp index 27f045a9..414ff67e 100644 --- a/include/beman/execution26/detail/basic_operation.hpp +++ b/include/beman/execution26/detail/basic_operation.hpp @@ -26,8 +26,10 @@ namespace beman::execution26::detail { */ template requires ::beman::execution26::detail:: - valid_specialization<::beman::execution26::detail::state_type, Sender, Receiver> - struct basic_operation : ::beman::execution26::detail::basic_state { + //-dk:TODO why is the remove_cvref_t needed...? + valid_specialization<::beman::execution26::detail::state_type, std::remove_cvref_t, Receiver> +struct basic_operation : ::beman::execution26::detail::basic_state { + // static_assert(std::same_as>); friend struct ::beman::execution26::start_t; using operation_state_concept = ::beman::execution26::operation_state_t; using tag_t = ::beman::execution26::tag_of_t; diff --git a/include/beman/execution26/detail/continues_on.hpp b/include/beman/execution26/detail/continues_on.hpp index c0ca832a..49bd8c56 100644 --- a/include/beman/execution26/detail/continues_on.hpp +++ b/include/beman/execution26/detail/continues_on.hpp @@ -50,7 +50,9 @@ struct continues_on_t { auto operator()(Sender&& sender, Scheduler&& scheduler) const { auto domain(::beman::execution26::detail::get_domain_early(sender)); return ::beman::execution26::transform_sender( - domain, ::beman::execution26::detail::make_sender(*this, scheduler, ::std::forward(sender))); + domain, + ::beman::execution26::detail::make_sender( + *this, std::forward(scheduler), ::std::forward(sender))); } }; diff --git a/include/beman/execution26/detail/sync_wait.hpp b/include/beman/execution26/detail/sync_wait.hpp index b474d988..8e76695d 100644 --- a/include/beman/execution26/detail/sync_wait.hpp +++ b/include/beman/execution26/detail/sync_wait.hpp @@ -75,7 +75,7 @@ struct sync_wait_receiver { struct sync_wait_t { template - auto apply_sender(Sender&& sender) { + auto apply_sender(Sender&& sender) const { ::beman::execution26::detail::sync_wait_state state; auto op{::beman::execution26::connect(::std::forward(sender), ::beman::execution26::detail::sync_wait_receiver{&state})}; @@ -93,7 +93,9 @@ struct sync_wait_t { typename ::beman::execution26::detail::sync_wait_result_type; { ::beman::execution26::apply_sender( - ::beman::execution26::detail::get_domain_early(sender), self, ::std::forward(sender)) + ::beman::execution26::detail::get_domain_early(std::forward(sender)), + self, + ::std::forward(sender)) } -> ::std::same_as<::beman::execution26::detail::sync_wait_result_type>; } auto operator()(Sender&& sender) const { From 46315042d8aba35806dd2648a5e616f2372d93a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Mon, 13 Jan 2025 07:34:36 +0000 Subject: [PATCH 32/41] simplied gather signatures (#116) * simplied gather signatures * clang-format * revert CMakeList.txt change * fixed formatting issue --- .../execution26/detail/gather_signatures.hpp | 7 +++--- .../beman/execution26/detail/meta_filter.hpp | 24 ++++++++++++++++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/include/beman/execution26/detail/gather_signatures.hpp b/include/beman/execution26/detail/gather_signatures.hpp index 9630e6ce..7c65a2b3 100644 --- a/include/beman/execution26/detail/gather_signatures.hpp +++ b/include/beman/execution26/detail/gather_signatures.hpp @@ -23,6 +23,7 @@ struct same_tag { }; template struct bound_tag { + using type = Tag; template using predicate = ::beman::execution26::detail::same_tag; }; @@ -69,14 +70,12 @@ template class Variant> requires requires { typename ::beman::execution26::detail::gather_signatures_helper< - ::beman::execution26::detail::meta:: - filter<::beman::execution26::detail::bound_tag::template predicate, signatures>, + ::beman::execution26::detail::meta::filter_tag<::beman::execution26::detail::same_tag, Tag, signatures>, Tuple, Variant>::type; } using gather_signatures = ::beman::execution26::detail::gather_signatures_helper< - ::beman::execution26::detail::meta::filter<::beman::execution26::detail::bound_tag::template predicate, - signatures>, + ::beman::execution26::detail::meta::filter_tag<::beman::execution26::detail::same_tag, Tag, signatures>, Tuple, Variant>::type; } // namespace beman::execution26::detail diff --git a/include/beman/execution26/detail/meta_filter.hpp b/include/beman/execution26/detail/meta_filter.hpp index e77c3915..d286e2ac 100644 --- a/include/beman/execution26/detail/meta_filter.hpp +++ b/include/beman/execution26/detail/meta_filter.hpp @@ -13,22 +13,44 @@ namespace beman::execution26::detail::meta::detail { template