stdexec: update to latest commit
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I93e33afb0ca87f425074e86f87e6cde320f20dbc
diff --git a/include/sdbusplus/async/context.hpp b/include/sdbusplus/async/context.hpp
index e60dccf..9ba424d 100644
--- a/include/sdbusplus/async/context.hpp
+++ b/include/sdbusplus/async/context.hpp
@@ -61,8 +61,8 @@
{
check_stop_requested();
- pending_tasks.spawn(
- std::move(execution::on(loop.get_scheduler(), std::move(sender))));
+ pending_tasks.spawn(std::move(
+ execution::starts_on(loop.get_scheduler(), std::move(sender))));
spawn_watcher();
}
diff --git a/include/sdbusplus/async/stdexec/__detail/__basic_sender.hpp b/include/sdbusplus/async/stdexec/__detail/__basic_sender.hpp
index 2505f85..0cdbdd3 100644
--- a/include/sdbusplus/async/stdexec/__detail/__basic_sender.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__basic_sender.hpp
@@ -19,6 +19,7 @@
#include "__diagnostics.hpp"
#include "__env.hpp"
#include "__execution_fwd.hpp"
+#include "__manual_lifetime.hpp"
#include "__meta.hpp"
#include "__sender_introspection.hpp"
#include "__senders_core.hpp"
@@ -61,7 +62,7 @@
}
} // namespace
-#if STDEXEC_NVHPC()
+#if STDEXEC_EDG()
#define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child) \
stdexec::__descriptor_fn<_Tag, _Data, _Child>()
#else
@@ -227,33 +228,120 @@
// __get_desc>::__children>;
template <class _Sexpr, class _Receiver>
+using __state_t = //
+ __state_type_t<typename __decay_t<_Sexpr>::__tag_t, _Sexpr, _Receiver>;
+
+template <class _Sexpr, class _Receiver>
+struct __op_base;
+
+template <class _Receiver>
+struct __receiver_box
+{
+ _Receiver __rcvr_;
+
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto __rcvr() & noexcept -> _Receiver&
+ {
+ return this->__rcvr_;
+ }
+
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto __rcvr() const& noexcept -> const _Receiver&
+ {
+ return this->__rcvr_;
+ }
+};
+
+template <class _Sexpr, class _Receiver>
+struct __state_box
+{
+ using __tag_t = typename __decay_t<_Sexpr>::__tag_t;
+ using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
+
+ __state_box(_Sexpr&& __sndr, _Receiver& __rcvr) //
+ noexcept(__nothrow_callable<decltype(__sexpr_impl<__tag_t>::get_state),
+ _Sexpr, _Receiver>) :
+ __state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr),
+ __rcvr))
+ {}
+
+ __state_t __state_;
+};
+
+template <class _Sexpr, class _Receiver, class _State>
+struct __enable_receiver_from_this
+{
+#if STDEXEC_HAS_FEATURE(undefined_behavior_sanitizer) && STDEXEC_CLANG()
+ // See https://github.com/llvm/llvm-project/issues/101276
+ [[clang::noinline]]
+#endif
+ auto __receiver() noexcept -> decltype(auto)
+ {
+ void* __state = static_cast<_State*>(this);
+ auto* __sbox = static_cast<__state_box<_Sexpr, _Receiver>*>(__state);
+ return (static_cast<__op_base<_Sexpr, _Receiver>*>(__sbox)->__rcvr_);
+ }
+};
+
+template <class _Sexpr, class _Receiver>
+concept __state_uses_receiver = //
+ derived_from<__state_t<_Sexpr, _Receiver>,
+ __enable_receiver_from_this<_Sexpr, _Receiver,
+ __state_t<_Sexpr, _Receiver>>>;
+
+template <class _Sexpr, class _Receiver>
struct __op_base : __immovable
{
using __tag_t = typename __decay_t<_Sexpr>::__tag_t;
using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
- STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Receiver __rcvr_;
- STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS __state_t __state_;
+ STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
+ _Receiver __rcvr_;
+ STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
+ __state_t __state_;
__op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) //
noexcept(__nothrow_decay_copyable<_Receiver> &&
- __nothrow_move_constructible<__state_t>) :
+ __nothrow_callable<decltype(__sexpr_impl<__tag_t>::get_state),
+ _Sexpr, _Receiver>) :
__rcvr_(static_cast<_Receiver&&>(__rcvr)),
__state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr),
__rcvr_))
{}
+ STDEXEC_ATTRIBUTE((always_inline))
auto __rcvr() & noexcept -> _Receiver&
{
return __rcvr_;
}
+ STDEXEC_ATTRIBUTE((always_inline))
auto __rcvr() const& noexcept -> const _Receiver&
{
return __rcvr_;
}
};
+template <class _Sexpr, class _Receiver>
+ requires __state_uses_receiver<_Sexpr, _Receiver>
+struct __op_base<_Sexpr, _Receiver> :
+ __receiver_box<_Receiver>,
+ __state_box<_Sexpr, _Receiver>
+{
+ using __tag_t = typename __decay_t<_Sexpr>::__tag_t;
+ using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
+
+ STDEXEC_IMMOVABLE(__op_base);
+
+ __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) //
+ noexcept(__nothrow_decay_copyable<_Receiver> &&
+ __nothrow_move_constructible<__state_t>) :
+ __receiver_box<_Receiver>{static_cast<_Receiver&&>(__rcvr)},
+ __state_box<_Sexpr, _Receiver>{static_cast<_Sexpr&&>(__sndr),
+ this->__rcvr_}
+ {}
+};
+
// template <class _Sexpr, class _Receiver>
// requires __is_instance_of<__id<_Receiver>, __receiver>
// && __decays_to<_Sexpr, __sexpr_connected_with<_Receiver>>
@@ -277,28 +365,6 @@
// };
STDEXEC_PRAGMA_PUSH()
-STDEXEC_PRAGMA_IGNORE_GNU("-Winvalid-offsetof")
-STDEXEC_PRAGMA_IGNORE_EDG(offset_in_non_POD_nonstandard)
-
-template <class _Sexpr, class _Receiver>
-struct __enable_receiver_from_this
-{
- using __op_base_t = __op_base<_Sexpr, _Receiver>;
-
- auto __receiver() noexcept -> decltype(auto)
- {
- using __derived_t = decltype(__op_base_t::__state_);
- auto* __derived = static_cast<__derived_t*>(this);
- constexpr std::size_t __offset = offsetof(__op_base_t, __state_);
- auto* __base = reinterpret_cast<__op_base_t*>(
- reinterpret_cast<char*>(__derived) - __offset);
- return __base->__rcvr();
- }
-};
-
-STDEXEC_PRAGMA_POP()
-
-STDEXEC_PRAGMA_PUSH()
STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
template <class _Sexpr, class _Receiver>
@@ -429,9 +495,9 @@
constexpr auto __captures(_Tag, _Captures&&... __captures2)
{
return
- [... __captures3 = static_cast<_Captures&&>(
- __captures2)]<class _Cvref, class _Fun>(_Cvref,
- _Fun&& __fun) mutable //
+ [... __captures3 =
+ static_cast<_Captures&&>(__captures2)]<class _Cvref, class _Fun>(
+ _Cvref, _Fun&& __fun) mutable //
noexcept(
__nothrow_callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>) //
-> __call_result_t<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>
@@ -481,20 +547,27 @@
using __get_attrs_fn = __result_of<__detail::__drop_front,
__mtypeof<__sexpr_impl<_Tag>::get_attrs>>;
-//////////////////////////////////////////////////////////////////////////////////////////////////
-// __basic_sender
+//! A dummy type used only for diagnostic purposes.
+//! See `__sexpr` for the implementation of P2300's _`basic-sender`_.
template <class...>
struct __basic_sender
{
+ // See MAINTAINERS.md#class-template-parameters for `__id` and `__t`.
using __id = __basic_sender;
using __t = __basic_sender;
};
+//! A struct template to aid in creating senders.
+//! This struct closely resembles P2300's
+//! [_`basic-sender`_](https://eel.is/c++draft/exec#snd.expos-24), but is not an
+//! exact implementation. Note: The struct named `__basic_sender` is just a
+//! dummy type and is also not _`basic-sender`_.
template <auto _DescriptorFn, class = __anon>
struct __sexpr
{
using sender_concept = sender_t;
+ // See MAINTAINERS.md#class-template-parameters for `__id` and `__t`.
using __id = __sexpr;
using __t = __sexpr;
using __desc_t = decltype(_DescriptorFn());
@@ -583,6 +656,9 @@
//////////////////////////////////////////////////////////////////////////////////////////////////
// __make_sexpr
+//! A tagged function-object
+//! Takes data and children and
+//! returns `__sexpr_t<_Tag, _Data, _Child...>{_Tag(), data, children...}`.
namespace __detail
{
template <class _Tag>
diff --git a/include/sdbusplus/async/stdexec/__detail/__bulk.hpp b/include/sdbusplus/async/stdexec/__detail/__bulk.hpp
index c81e62e..9faa020 100644
--- a/include/sdbusplus/async/stdexec/__detail/__bulk.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__bulk.hpp
@@ -128,15 +128,21 @@
return {};
};
+ //! This implements the core default behavior for `bulk`:
+ //! When setting value, it loops over the shape and invokes the function.
+ //! Note: This is not done in parallel. That is customized by the scheduler.
+ //! See, e.g., static_thread_pool::bulk_receiver::__t.
static constexpr auto complete = //
[]<class _Tag, class _State, class _Receiver, class... _Args>(
__ignore, _State& __state, _Receiver& __rcvr, _Tag,
_Args&&... __args) noexcept -> void {
if constexpr (std::same_as<_Tag, set_value_t>)
{
+ // Intercept set_value and dispatch to the bulk operation.
using __shape_t = decltype(__state.__shape_);
if constexpr (noexcept(__state.__fun_(__shape_t{}, __args...)))
{
+ // The noexcept version that doesn't need try/catch:
for (__shape_t __i{}; __i != __state.__shape_; ++__i)
{
__state.__fun_(__i, __args...);
diff --git a/include/sdbusplus/async/stdexec/__detail/__config.hpp b/include/sdbusplus/async/stdexec/__detail/__config.hpp
index acf6498..479bf9e 100644
--- a/include/sdbusplus/async/stdexec/__detail/__config.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__config.hpp
@@ -130,10 +130,15 @@
// corresponds to the macro name; nothing, otherwise.
#if defined(__NVCC__)
#define STDEXEC_NVCC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
-#elif defined(__NVCOMPILER)
-#define STDEXEC_NVHPC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#elif defined(__EDG__)
#define STDEXEC_EDG(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
+#if defined(__NVCOMPILER)
+#define STDEXEC_NVHPC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
+#endif
+#if defined(__INTELLISENSE__)
+#define STDEXEC_INTELLISENSE(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
+#define STDEXEC_MSVC_HEADERS(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
+#endif
#elif defined(__clang__)
#define STDEXEC_CLANG(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#if defined(_MSC_VER)
@@ -146,6 +151,7 @@
#define STDEXEC_GCC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#elif defined(_MSC_VER)
#define STDEXEC_MSVC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
+#define STDEXEC_MSVC_HEADERS(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#endif
#ifndef STDEXEC_NVCC
@@ -172,6 +178,12 @@
#ifndef STDEXEC_MSVC
#define STDEXEC_MSVC(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
+#ifndef STDEXEC_MSVC_HEADERS
+#define STDEXEC_MSVC_HEADERS(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
+#endif
+#ifndef STDEXEC_INTELLISENSE
+#define STDEXEC_INTELLISENSE(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
+#endif
#if STDEXEC_NVHPC()
#define STDEXEC_NVHPC_VERSION() \
@@ -274,7 +286,7 @@
#define STDEXEC_PRAGMA_POP() _Pragma("nv_diagnostic pop")
#define STDEXEC_PRAGMA_IGNORE_EDG(...) \
_Pragma(STDEXEC_STRINGIZE(nv_diag_suppress __VA_ARGS__))
-#elif STDEXEC_NVHPC() || STDEXEC_EDG()
+#elif STDEXEC_EDG()
#define STDEXEC_PRAGMA_PUSH() \
_Pragma("diagnostic push") STDEXEC_PRAGMA_IGNORE_EDG(invalid_error_number)
#define STDEXEC_PRAGMA_POP() _Pragma("diagnostic pop")
diff --git a/include/sdbusplus/async/stdexec/__detail/__connect_awaitable.hpp b/include/sdbusplus/async/stdexec/__detail/__connect_awaitable.hpp
index b9d2623..7cddb81 100644
--- a/include/sdbusplus/async/stdexec/__detail/__connect_awaitable.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__connect_awaitable.hpp
@@ -117,7 +117,11 @@
{
using __id = __promise;
+#if STDEXEC_EDG()
+ __t(auto&&, _Receiver&& __rcvr) noexcept : __rcvr_(__rcvr) {}
+#else
explicit __t(auto&, _Receiver& __rcvr) noexcept : __rcvr_(__rcvr) {}
+#endif
auto unhandled_stopped() noexcept -> __coro::coroutine_handle<>
{
diff --git a/include/sdbusplus/async/stdexec/__detail/__continues_on.hpp b/include/sdbusplus/async/stdexec/__detail/__continues_on.hpp
new file mode 100644
index 0000000..d8af7af
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__continues_on.hpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2021-2024 NVIDIA Corporation
+ *
+ * Licensed under the Apache License Version 2.0 with LLVM Exceptions
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://llvm.org/LICENSE.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "__execution_fwd.hpp"
+
+// include these after __execution_fwd.hpp
+#include "__basic_sender.hpp"
+#include "__concepts.hpp"
+#include "__env.hpp"
+#include "__meta.hpp"
+#include "__schedule_from.hpp"
+#include "__schedulers.hpp"
+#include "__sender_adaptor_closure.hpp"
+#include "__sender_introspection.hpp"
+#include "__senders.hpp"
+#include "__tag_invoke.hpp"
+#include "__transform_sender.hpp"
+#include "__type_traits.hpp"
+
+#include <utility>
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.continues_on]
+namespace __continues_on
+{
+using __schfr::__environ;
+
+template <class _Env>
+using __scheduler_t = __result_of<get_completion_scheduler<set_value_t>, _Env>;
+
+template <class _Sender>
+using __lowered_t = //
+ __result_of<schedule_from, __scheduler_t<__data_of<_Sender>>,
+ __child_of<_Sender>>;
+
+struct continues_on_t
+{
+ template <sender _Sender, scheduler _Scheduler>
+ auto operator()(_Sender&& __sndr,
+ _Scheduler&& __sched) const -> __well_formed_sender auto
+ {
+ auto __domain = __get_early_domain(__sndr);
+ using _Env = __t<__environ<__id<__decay_t<_Scheduler>>>>;
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<continues_on_t>(
+ _Env{{static_cast<_Scheduler&&>(__sched)}},
+ static_cast<_Sender&&>(__sndr)));
+ }
+
+ template <scheduler _Scheduler>
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto operator()(_Scheduler&& __sched) const
+ -> __binder_back<continues_on_t, __decay_t<_Scheduler>>
+ {
+ return {{static_cast<_Scheduler&&>(__sched)}, {}, {}};
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ using _Env = __0;
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(continues_on_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(const _Sender&)),
+ _Sender,
+ get_completion_scheduler_t<set_value_t>(_Env)),
+ tag_invoke_t(continues_on_t, _Sender,
+ get_completion_scheduler_t<set_value_t>(_Env))>;
+
+ template <class _Env>
+ static auto __transform_sender_fn(const _Env&)
+ {
+ return [&]<class _Data, class _Child>(__ignore, _Data&& __data,
+ _Child&& __child) {
+ auto __sched = get_completion_scheduler<set_value_t>(__data);
+ return schedule_from(std::move(__sched),
+ static_cast<_Child&&>(__child));
+ };
+ }
+
+ template <class _Sender, class _Env>
+ static auto transform_sender(_Sender&& __sndr, const _Env& __env)
+ {
+ return __sexpr_apply(static_cast<_Sender&&>(__sndr),
+ __transform_sender_fn(__env));
+ }
+};
+
+struct __continues_on_impl : __sexpr_defaults
+{
+ static constexpr auto get_attrs = //
+ []<class _Data, class _Child>(
+ const _Data& __data,
+ const _Child& __child) noexcept -> decltype(auto) {
+ return __env::__join(__data, stdexec::get_env(__child));
+ };
+
+ static constexpr auto get_completion_signatures = //
+ []<class _Sender>(_Sender&&) noexcept //
+ -> __completion_signatures_of_t< //
+ transform_sender_result_t<default_domain, _Sender, empty_env>> {};
+};
+} // namespace __continues_on
+
+using __continues_on::continues_on_t;
+inline constexpr continues_on_t continues_on{};
+
+using transfer_t = continues_on_t;
+inline constexpr continues_on_t transfer{};
+
+using continue_on_t = continues_on_t;
+inline constexpr continues_on_t continue_on{};
+
+template <>
+struct __sexpr_impl<continues_on_t> : __continues_on::__continues_on_impl
+{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__domain.hpp b/include/sdbusplus/async/stdexec/__detail/__domain.hpp
index 329483b..42ea9e1 100644
--- a/include/sdbusplus/async/stdexec/__detail/__domain.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__domain.hpp
@@ -245,6 +245,11 @@
} // namespace __detail
/////////////////////////////////////////////////////////////////////////////
+//! Function object implementing `get-domain-early(snd)`
+//! from [exec.snd.general] item 3.9. It is the first well-formed expression of
+//! a) `get_domain(get_env(sndr))`
+//! b) `completion-domain(sndr)`
+//! c) `default_domain()`
inline constexpr struct __get_early_domain_t
{
template <class _Sender, class _Default = default_domain>
@@ -299,10 +304,10 @@
}
}
- // The transfer algorithm is the exception to the rule. It ignores the
+ // The continues_on algorithm is the exception to the rule. It ignores the
// domain of the predecessor, and dispatches based on the domain of the
// scheduler to which execution is being transferred.
- template <sender_expr_for<transfer_t> _Sender, class _Env>
+ template <sender_expr_for<continues_on_t> _Sender, class _Env>
auto operator()(const _Sender& __sndr, const _Env&) const noexcept
{
return __sexpr_apply(__sndr, [](__ignore, auto& __data,
diff --git a/include/sdbusplus/async/stdexec/__detail/__env.hpp b/include/sdbusplus/async/stdexec/__detail/__env.hpp
index 5ff161f..6f655f7 100644
--- a/include/sdbusplus/async/stdexec/__detail/__env.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__env.hpp
@@ -192,7 +192,10 @@
auto operator()() const noexcept;
};
-struct get_delegatee_scheduler_t : __query<get_delegatee_scheduler_t>
+//! The type for `get_delegation_scheduler` [exec.get.delegation.scheduler]
+//! A query object that asks for a scheduler that can be used to delegate
+//! work to for the purpose of forward progress delegation ([intro.progress]).
+struct get_delegation_scheduler_t : __query<get_delegation_scheduler_t>
{
static constexpr auto query(forwarding_query_t) noexcept -> bool
{
@@ -200,11 +203,11 @@
}
template <class _Env>
- requires tag_invocable<get_delegatee_scheduler_t, const _Env&>
+ requires tag_invocable<get_delegation_scheduler_t, const _Env&>
auto operator()(const _Env& __t) const noexcept
- -> tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>;
+ -> tag_invoke_result_t<get_delegation_scheduler_t, const _Env&>;
- template <class _Tag = get_delegatee_scheduler_t>
+ template <class _Tag = get_delegation_scheduler_t>
auto operator()() const noexcept;
};
@@ -353,19 +356,22 @@
} // namespace __queries
using __queries::__has_algorithm_customizations_t;
-using __queries::__is_scheduler_affine_t;
-using __queries::__root_env;
-using __queries::__root_t;
using __queries::execute_may_block_caller_t;
using __queries::forwarding_query_t;
using __queries::get_allocator_t;
-using __queries::get_completion_scheduler_t;
-using __queries::get_delegatee_scheduler_t;
-using __queries::get_domain_t;
+using __queries::get_delegation_scheduler_t;
using __queries::get_forward_progress_guarantee_t;
using __queries::get_scheduler_t;
-using __queries::get_stop_token_t;
using __queries::query_or_t;
+using get_delegatee_scheduler_t [[deprecated(
+ "get_delegatee_scheduler_t has been renamed get_delegation_scheduler_t")]] =
+ get_delegation_scheduler_t;
+using __queries::__is_scheduler_affine_t;
+using __queries::__root_env;
+using __queries::__root_t;
+using __queries::get_completion_scheduler_t;
+using __queries::get_domain_t;
+using __queries::get_stop_token_t;
inline constexpr forwarding_query_t forwarding_query{};
inline constexpr query_or_t query_or{}; // NOT TO SPEC
@@ -375,7 +381,10 @@
inline constexpr get_forward_progress_guarantee_t
get_forward_progress_guarantee{};
inline constexpr get_scheduler_t get_scheduler{};
-inline constexpr get_delegatee_scheduler_t get_delegatee_scheduler{};
+inline constexpr get_delegation_scheduler_t get_delegation_scheduler{};
+inline constexpr auto& get_delegatee_scheduler [[deprecated(
+ "get_delegatee_scheduler has been renamed get_delegation_scheduler")]] =
+ get_delegation_scheduler;
inline constexpr get_allocator_t get_allocator{};
inline constexpr get_stop_token_t get_stop_token{};
#if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__)
diff --git a/include/sdbusplus/async/stdexec/__detail/__execution_fwd.hpp b/include/sdbusplus/async/stdexec/__detail/__execution_fwd.hpp
index 7b25130..b5e58df 100644
--- a/include/sdbusplus/async/stdexec/__detail/__execution_fwd.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__execution_fwd.hpp
@@ -91,7 +91,7 @@
struct get_forward_progress_guarantee_t;
struct __has_algorithm_customizations_t;
struct get_scheduler_t;
-struct get_delegatee_scheduler_t;
+struct get_delegation_scheduler_t;
struct get_allocator_t;
struct get_stop_token_t;
template <__completion_tag _CPO>
@@ -103,7 +103,7 @@
using __queries::forwarding_query_t;
using __queries::get_allocator_t;
using __queries::get_completion_scheduler_t;
-using __queries::get_delegatee_scheduler_t;
+using __queries::get_delegation_scheduler_t;
using __queries::get_forward_progress_guarantee_t;
using __queries::get_scheduler_t;
using __queries::get_stop_token_t;
@@ -113,7 +113,7 @@
extern const __has_algorithm_customizations_t __has_algorithm_customizations;
extern const get_forward_progress_guarantee_t get_forward_progress_guarantee;
extern const get_scheduler_t get_scheduler;
-extern const get_delegatee_scheduler_t get_delegatee_scheduler;
+extern const get_delegation_scheduler_t get_delegation_scheduler;
extern const get_allocator_t get_allocator;
extern const get_stop_token_t get_stop_token;
template <__completion_tag _CPO>
@@ -207,28 +207,43 @@
extern const as_awaitable_t as_awaitable;
//////////////////////////////////////////////////////////////////////////////////////////////////
-namespace __start_on
+namespace __starts_on_ns
{
-struct start_on_t;
-} // namespace __start_on
+struct starts_on_t;
+} // namespace __starts_on_ns
-using __start_on::start_on_t;
-extern const start_on_t start_on;
+using __starts_on_ns::starts_on_t;
+extern const starts_on_t starts_on;
-using on_t = start_on_t;
-extern const on_t on;
+using on_t [[deprecated("on_t has been renamed starts_on_t")]] = starts_on_t;
+[[deprecated("on has been renamed starts_on")]] extern const starts_on_t on;
+
+using start_on_t
+ [[deprecated("start_on_t has been renamed starts_on_t")]] = starts_on_t;
+[[deprecated(
+ "start_on has been renamed starts_on")]] extern const starts_on_t start_on;
//////////////////////////////////////////////////////////////////////////////////////////////////
-namespace __continue_on
+namespace __continues_on
{
-struct continue_on_t;
-} // namespace __continue_on
+struct continues_on_t;
+} // namespace __continues_on
-using __continue_on::continue_on_t;
-extern const continue_on_t continue_on;
+using __continues_on::continues_on_t;
+extern const continues_on_t continues_on;
-using transfer_t = continue_on_t;
-extern const transfer_t transfer;
+using transfer_t [[deprecated("transfer_t has been renamed continues_on_t")]] =
+ continues_on_t;
+[[deprecated(
+ "transfer has been renamed continues_on")]] extern const continues_on_t
+ transfer;
+
+using continue_t
+ [[deprecated("continue_on_t has been renamed continues_on_t")]] =
+ continues_on_t;
+[[deprecated(
+ "continue_on has been renamed continues_on")]] extern const continues_on_t
+ continue_on;
//////////////////////////////////////////////////////////////////////////////////////////////////
namespace __transfer_just
diff --git a/include/sdbusplus/async/stdexec/__detail/__let.hpp b/include/sdbusplus/async/stdexec/__detail/__let.hpp
index 1ea3a6d..8930589 100644
--- a/include/sdbusplus/async/stdexec/__detail/__let.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__let.hpp
@@ -155,7 +155,7 @@
struct _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_
{};
-#if STDEXEC_NVHPC()
+#if STDEXEC_EDG()
template <class _Sender, class _Set, class... _Env>
struct __bad_result_sender_
{
@@ -380,6 +380,9 @@
};
}
+//! Metafunction creating the operation state needed to connect the result of
+//! calling the sender factory function, `_Fun`, and passing its result to a
+//! receiver.
template <class _Receiver, class _Fun, class _Set, class _Sched>
struct __op_state_for
{
@@ -390,6 +393,9 @@
_Sched, _Receiver>;
};
+//! The core of the operation state for `let_*`.
+//! This gets bundled up into a larger operation state
+//! (`__detail::__op_state<...>`).
template <class _Receiver, class _Fun, class _Set, class _Sched,
class... _Tuples>
struct __let_state
@@ -443,12 +449,20 @@
}
}
- STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Fun __fun_;
- STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Sched __sched_;
+ STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
+ _Fun __fun_;
+ STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
+ _Sched __sched_;
+ //! Variant to hold the results passed from upstream before passing them to
+ //! the function:
__result_variant __args_{};
+ //! Variant type for holding the operation state from connecting
+ //! the function result to the downstream receiver:
__op_state_variant __op_state3_{};
};
+//! Implementation of the `let_*_t` types, where `_Set` is, e.g., `set_value_t`
+//! for `let_value`.
template <class _Set, class _Domain>
struct __let_t
{
@@ -541,13 +555,21 @@
});
};
+ //! Helper function to actually invoke the function to produce `let_*`'s
+ //! sender, connect it to the downstream receiver, and start it. This is the
+ //! heart of `let_*`.
template <class _State, class _OpState, class... _As>
static void __bind_(_State& __state, _OpState& __op_state, _As&&... __as)
{
+ // Store the passed-in (received) args:
auto& __args = __state.__args_.emplace_from(
__tup::__mktuple, static_cast<_As&&>(__as)...);
+ // Apply the function to the args to get the sender:
auto __sndr2 = __args.apply(std::move(__state.__fun_), __args);
+ // Create a receiver based on the state, the computed sender, and the
+ // operation state:
auto __rcvr2 = __state.__get_result_receiver(__sndr2, __op_state);
+ // Connect the sender to the receiver and start it:
auto& __op2 = __state.__op_state3_.emplace_from(
stdexec::connect, std::move(__sndr2), std::move(__rcvr2));
stdexec::start(__op2);
@@ -595,10 +617,13 @@
_As&&... __as) noexcept -> void {
if constexpr (__same_as<_Tag, _Set>)
{
+ // Intercept the channel of interest to compute the sender and
+ // connect it:
__bind(__op_state, static_cast<_As&&>(__as)...);
}
else
{
+ // Forward the other channels downstream:
using _Receiver = decltype(__op_state.__rcvr_);
_Tag()(static_cast<_Receiver&&>(__op_state.__rcvr_),
static_cast<_As&&>(__as)...);
diff --git a/include/sdbusplus/async/stdexec/__detail/__manual_lifetime.hpp b/include/sdbusplus/async/stdexec/__detail/__manual_lifetime.hpp
new file mode 100644
index 0000000..82fb917
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__manual_lifetime.hpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2023 Maikel Nadolski
+ * Copyright (c) 2023 NVIDIA Corporation
+ *
+ * Licensed under the Apache License Version 2.0 with LLVM Exceptions
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://llvm.org/LICENSE.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "../concepts.hpp"
+
+#include <cstddef>
+#include <memory>
+#include <new>
+#include <type_traits>
+
+namespace stdexec
+{
+
+//! Holds storage for a `_Ty`, but allows clients to `__construct(...)`,
+//! `__destry()`, and `__get()` the `_Ty` without regard for usual lifetime
+//! rules.
+template <class _Ty>
+class __manual_lifetime
+{
+ public:
+ //! Constructor does nothing: It's on you to call `__construct(...)` or
+ //! `__construct_from(...)` if you want the `_Ty`'s lifetime to begin.
+ constexpr __manual_lifetime() noexcept {}
+ //! Destructor does nothing: It's on you to call `__destroy()` if you mean
+ //! to.
+ constexpr ~__manual_lifetime() {}
+
+ __manual_lifetime(const __manual_lifetime&) = delete;
+ auto operator=(const __manual_lifetime&) -> __manual_lifetime& = delete;
+
+ __manual_lifetime(__manual_lifetime&&) = delete;
+ auto operator=(__manual_lifetime&&) -> __manual_lifetime& = delete;
+
+ //! Construct the `_Ty` in place.
+ //! There are no safeties guarding against the case that there's already one
+ //! there.
+ template <class... _Args>
+ auto __construct(_Args&&... __args) noexcept(
+ stdexec::__nothrow_constructible_from<_Ty, _Args...>) -> _Ty&
+ {
+ // Use placement new instead of std::construct_at to support aggregate
+ // initialization with brace elision.
+ return *std::launder(::new (static_cast<void*>(__buffer_))
+ _Ty{static_cast<_Args&&>(__args)...});
+ }
+
+ //! Construct the `_Ty` in place from the result of calling `func`.
+ //! There are no safeties guarding against the case that there's already one
+ //! there.
+ template <class _Func, class... _Args>
+ auto __construct_from(_Func&& func, _Args&&... __args) -> _Ty&
+ {
+ // Use placement new instead of std::construct_at in case the function
+ // returns an immovable type.
+ return *std::launder(::new (static_cast<void*>(__buffer_)) _Ty{
+ (static_cast<_Func&&>(func))(static_cast<_Args&&>(__args)...)});
+ }
+ //! End the lifetime of the contained `_Ty`.
+ //! Precondition: The lifetime has started.
+ void __destroy() noexcept
+ {
+ std::destroy_at(&__get());
+ }
+ //! Get access to the `_Ty`.
+ //! Precondition: The lifetime has started.
+ auto __get() & noexcept -> _Ty&
+ {
+ return *reinterpret_cast<_Ty*>(__buffer_);
+ }
+
+ //! Get access to the `_Ty`.
+ //! Precondition: The lifetime has started.
+ auto __get() && noexcept -> _Ty&&
+ {
+ return static_cast<_Ty&&>(*reinterpret_cast<_Ty*>(__buffer_));
+ }
+
+ //! Get access to the `_Ty`.
+ //! Precondition: The lifetime has started.
+ auto __get() const& noexcept -> const _Ty&
+ {
+ return *reinterpret_cast<const _Ty*>(__buffer_);
+ }
+
+ //! Move semantics aren't supported.
+ //! If you want to move the `_Ty`, use `std::move(ml.__get())`.
+ auto __get() const&& noexcept -> const _Ty&& = delete;
+
+ private:
+ alignas(_Ty) unsigned char __buffer_[sizeof(_Ty)]{};
+};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__meta.hpp b/include/sdbusplus/async/stdexec/__detail/__meta.hpp
index dda9a28..1e9931e 100644
--- a/include/sdbusplus/async/stdexec/__detail/__meta.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__meta.hpp
@@ -29,6 +29,9 @@
namespace stdexec
{
+//! Convenience metafunction getting the dependant type `__t` out of `_Tp`.
+//! That is, `typename _Tp::__t`.
+//! See MAINTAINERS.md#class-template-parameters for details.
template <class _Tp>
using __t = typename _Tp::__t;
@@ -86,7 +89,10 @@
{
};
-#if STDEXEC_MSVC()
+#if STDEXEC_NVCC() || STDEXEC_EDG()
+template <std::size_t _Np>
+using __msize_t = std::integral_constant<std::size_t, _Np>;
+#elif STDEXEC_MSVC()
template <std::size_t _Np>
using __msize_t = __mconstant<_Np>;
#else
@@ -94,9 +100,11 @@
using __msize_t = __muchar (*)[_Np + 1]; // +1 to avoid zero-size array
#endif
+//! Metafunction selects the first of two type arguments.
template <class _Tp, class _Up>
using __mfirst = _Tp;
+//! Metafunction selects the second of two type arguments.
template <class _Tp, class _Up>
using __msecond = _Up;
@@ -212,7 +220,7 @@
template <std::size_t _Len>
struct __mstring
{
-#if STDEXEC_NVHPC()
+#if STDEXEC_EDG()
template <std::size_t _Ny, std::size_t... _Is>
constexpr __mstring(const char (&__str)[_Ny], __indices<_Is...>) noexcept :
__what_{(_Is < _Ny ? __str[_Is] : '\0')...}
@@ -248,7 +256,7 @@
return false;
}
-#if !STDEXEC_NVHPC()
+#if !STDEXEC_EDG()
constexpr auto operator<=>(const __mstring&) const noexcept
-> std::strong_ordering = default;
#endif
@@ -353,10 +361,20 @@
template <class... _Args>
concept _Ok = (STDEXEC_IS_SAME(__ok_t<_Args>, __msuccess) && ...);
+//! The struct `__i` is the implementation of P2300's
+//! [_`META-APPLY`_](https://eel.is/c++draft/exec#util.cmplsig-5).
+//! > [Note [1](https://eel.is/c++draft/exec#util.cmplsig-note-1):
+//! > The purpose of META-APPLY is to make it valid to use non-variadic
+//! > templates as Variant and Tuple arguments to gather-signatures. — end note]
+//! In addition to avoiding the dreaded "pack expanded into non-pack argument"
+//! error, it is part of the meta-error propagation mechanism. if any of the
+//! argument types are a specialization of `_ERROR_`, `__i` will short-circuit
+//! and return the error.
+//! `__minvoke` and `__meval` are implemented in terms of `__i`.
template <bool _ArgsOK, bool _FnOK = true>
struct __i;
-#if STDEXEC_NVHPC()
+#if STDEXEC_EDG()
// Most compilers memoize alias template specializations, but
// nvc++ does not. So we memoize the type computations by
// indirecting through a class template specialization.
@@ -406,6 +424,10 @@
typename __i<_Ok<_Args...>> //
::template __g<_Fn, _Args...>; //
+//! Metafunction invocation
+//! Given a metafunction, `_Fn`, and args.
+//! We expect `_Fn::__f` to be type alias template "implementing" the
+//! metafunction `_Fn`.
template <class _Fn, class... _Args>
using __minvoke = //
typename __i<_Ok<_Args...>, _Ok<_Fn>> //
@@ -450,6 +472,16 @@
using __f = _Fn;
};
+//! This struct template is like
+//! [mpl::quote](https://www.boost.org/doc/libs/1_86_0/libs/mpl/doc/refmanual/quote.html).
+//! It turns an alias/class template into a metafunction that also propagates
+//! "meta-exceptions". All of the meta utilities recognize specializations of
+//! stdexec::_ERROR_ as an error type. Error types short-circuit the evaluation
+//! of the metafunction and are automatically propagated like an exception.
+//! Note: `__minvoke` and `__meval` also participate in this error propagation.
+//!
+//! This design lets us report type errors briefly at the library boundary, even
+//! if the actual error happens deep inside a meta-program.
template <template <class...> class _Fn>
struct __q
{
@@ -549,6 +581,9 @@
struct __if_
{
+ //! Metafunction selects `_True` if the bool template is `true`, otherwise
+ //! the second. That is, `__<true>::__f<A, B>` is `A` and `__<false>::__f<A,
+ //! B>` is B. This is similar to `std::conditional_t<Cond, A, B>`.
template <bool>
struct __
{
@@ -560,6 +595,7 @@
using __f = __minvoke<__<static_cast<bool>(__v<_Pred>)>, _True, _False...>;
};
+// Specialization; see above.
template <>
struct __if_::__<false>
{
@@ -736,6 +772,20 @@
using __f = __minvoke<_Fn, _As...>;
};
+template <std::size_t... _Ns>
+struct __muncurry_<__pack::__t<_Ns...>*>
+{
+ template <class _Fn>
+ using __f = __minvoke<_Fn, __msize_t<_Ns>...>;
+};
+
+template <template <class _Np, _Np...> class _Cp, class _Np, _Np... _Ns>
+struct __muncurry_<_Cp<_Np, _Ns...>>
+{
+ template <class _Fn>
+ using __f = __minvoke<_Fn, std::integral_constant<_Np, _Ns>...>;
+};
+
template <class _What, class... _With>
struct __muncurry_<_ERROR_<_What, _With...>>
{
@@ -897,9 +947,13 @@
template <class _Default>
using __msingle_or = __mbind_front_q<__msingle_or_, _Default>;
+//! A concept checking if `_Ty` has a dependent type `_Ty::__id`.
+//! See MAINTAINERS.md#class-template-parameters.
template <class _Ty>
concept __has_id = requires { typename _Ty::__id; };
+//! Identity mapping `_Ty` to itself.
+//! That is, `std::is_same_v<T, typename _Id<T>::__t>`.
template <class _Ty>
struct _Id
{
@@ -913,6 +967,7 @@
// static_assert(!__has_id<std::remove_cvref_t<_Ty>>);
};
+//! Helper metafunction detail of `__id`, below.
template <bool = true>
struct __id_
{
@@ -926,6 +981,11 @@
template <class _Ty>
using __f = _Id<_Ty>;
};
+
+//! Metafunction mapping `_Ty` to either
+//! * `typename _Ty::__id` if that exists, or to
+//! * `_Ty` (itself) otherwise.
+//! See MAINTAINERS.md#class-template-parameters.
template <class _Ty>
using __id = __minvoke<__id_<__has_id<_Ty>>, _Ty>;
@@ -935,7 +995,7 @@
template <class _From, class _To = __decay_t<_From>>
using __cvref_id = __copy_cvref_t<_From, __id<_To>>;
-#if STDEXEC_NVHPC()
+#if STDEXEC_EDG()
// nvc++ doesn't cache the results of alias template specializations.
// To avoid repeated computation of the same function return type,
// cache the result ourselves in a class template specialization.
@@ -949,7 +1009,7 @@
#endif
// BUGBUG TODO file this bug with nvc++
-#if STDEXEC_NVHPC()
+#if STDEXEC_EDG()
template <const auto& _Fun, class... _As>
using __result_of = __call_result_t<decltype(_Fun), _As...>;
#else
@@ -982,8 +1042,11 @@
template <class _Fn>
__emplace_from(_Fn) -> __emplace_from<_Fn>;
-template <class, class, class, class>
-struct __mzip_with2_;
+template <class _Fn, class _Continuation, class _List1, class _List2>
+struct __mzip_with2_ :
+ __mzip_with2_<_Fn, _Continuation, __mapply<__qq<__types>, _List1>,
+ __mapply<__qq<__types>, _List2>>
+{};
template < //
class _Fn, //
@@ -1062,7 +1125,7 @@
template <class _Boolean>
using __mnot = __meval<__mnot_t, _Boolean>;
-#if STDEXEC_NVHPC()
+#if STDEXEC_EDG()
template <class... _Ints>
struct __mplus_t : __mconstant<(__v<_Ints> + ...)>
{};
@@ -1094,7 +1157,9 @@
using __f = __mor<__minvoke<_Fn, _Args>...>;
};
-#if defined(__cpp_pack_indexing)
+// C++23 pack indexing is disabled for clang because of
+// https://github.com/llvm/llvm-project/issues/116105
+#if defined(__cpp_pack_indexing) && !STDEXEC_CLANG()
template <class _Np, class... _Ts>
using __m_at = _Ts...[__v<_Np>];
@@ -1188,7 +1253,7 @@
constexpr decltype(auto) operator()(_Ts&&... __ts) const noexcept
{
static_assert(_Np < sizeof...(_Ts));
- return (static_cast<_Ts&&>(__ts)...[_Np]);
+ return static_cast<_Ts...[_Np] &&>(__ts...[_Np]);
}
};
#else
diff --git a/include/sdbusplus/async/stdexec/__detail/__on.hpp b/include/sdbusplus/async/stdexec/__detail/__on.hpp
index 0d5d522..5f257bf 100644
--- a/include/sdbusplus/async/stdexec/__detail/__on.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__on.hpp
@@ -20,7 +20,7 @@
// include these after __execution_fwd.hpp
#include "__basic_sender.hpp"
#include "__concepts.hpp"
-#include "__continue_on.hpp"
+#include "__continues_on.hpp"
#include "__cpo.hpp"
#include "__diagnostics.hpp"
#include "__domain.hpp"
@@ -71,14 +71,13 @@
};
template <class _Scheduler, class _Closure>
-struct __continue_on_data
+struct __on_data
{
_Scheduler __sched_;
_Closure __clsur_;
};
template <class _Scheduler, class _Closure>
-__continue_on_data(_Scheduler,
- _Closure) -> __continue_on_data<_Scheduler, _Closure>;
+__on_data(_Scheduler, _Closure) -> __on_data<_Scheduler, _Closure>;
template <class _Scheduler>
struct __with_sched
@@ -122,10 +121,10 @@
{
auto __domain = __get_early_domain(__sndr);
return stdexec::transform_sender(
- __domain, __make_sexpr<on_t>(
- __continue_on_data{static_cast<_Scheduler&&>(__sched),
- static_cast<_Closure&&>(__clsur)},
- static_cast<_Sender&&>(__sndr)));
+ __domain,
+ __make_sexpr<on_t>(__on_data{static_cast<_Scheduler&&>(__sched),
+ static_cast<_Closure&&>(__clsur)},
+ static_cast<_Sender&&>(__sndr)));
}
template <scheduler _Scheduler, __sender_adaptor_closure _Closure>
@@ -172,9 +171,9 @@
{
if constexpr (__is_root_env<_Env>)
{
- return continue_on(
- start_on(static_cast<_Data&&>(__data),
- static_cast<_Child&&>(__child)),
+ return continues_on(
+ starts_on(static_cast<_Data&&>(__data),
+ static_cast<_Child&&>(__child)),
__inln::__scheduler{});
}
else
@@ -184,9 +183,10 @@
}
else
{
- return continue_on(start_on(static_cast<_Data&&>(__data),
- static_cast<_Child&&>(__child)),
- static_cast<decltype(__old)&&>(__old));
+ return continues_on(
+ starts_on(static_cast<_Data&&>(__data),
+ static_cast<_Child&&>(__child)),
+ static_cast<decltype(__old)&&>(__old));
}
}
else
@@ -204,9 +204,9 @@
{
auto&& [__sched, __clsur] = static_cast<_Data&&>(__data);
return __write_env( //
- continue_on( //
+ continues_on( //
__forward_like<_Data>(__clsur)( //
- continue_on( //
+ continues_on( //
__write_env(static_cast<_Child&&>(__child),
__with_sched{__old}), //
__sched)), //
diff --git a/include/sdbusplus/async/stdexec/__detail/__read_env.hpp b/include/sdbusplus/async/stdexec/__detail/__read_env.hpp
index e617901..23ab05a 100644
--- a/include/sdbusplus/async/stdexec/__detail/__read_env.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__read_env.hpp
@@ -147,9 +147,9 @@
}
template <class _Tag>
-inline auto get_delegatee_scheduler_t::operator()() const noexcept
+inline auto get_delegation_scheduler_t::operator()() const noexcept
{
- return read_env(get_delegatee_scheduler);
+ return read_env(get_delegation_scheduler);
}
template <class _Tag>
diff --git a/include/sdbusplus/async/stdexec/__detail/__receivers.hpp b/include/sdbusplus/async/stdexec/__detail/__receivers.hpp
index 11c1708..a3e365c 100644
--- a/include/sdbusplus/async/stdexec/__detail/__receivers.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__receivers.hpp
@@ -147,8 +147,8 @@
namespace __detail
{
template <class _Receiver>
-concept __enable_receiver = //
- (STDEXEC_NVHPC(requires { typename _Receiver::receiver_concept; }&&) //
+concept __enable_receiver = //
+ (STDEXEC_EDG(requires { typename _Receiver::receiver_concept; }&&) //
derived_from<typename _Receiver::receiver_concept, receiver_t>) ||
requires { typename _Receiver::is_receiver; } // back-compat, NOT TO SPEC
|| STDEXEC_IS_BASE_OF(receiver_t,
diff --git a/include/sdbusplus/async/stdexec/__detail/__schedule_from.hpp b/include/sdbusplus/async/stdexec/__detail/__schedule_from.hpp
index 3f60705..0f83452 100644
--- a/include/sdbusplus/async/stdexec/__detail/__schedule_from.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__schedule_from.hpp
@@ -175,18 +175,19 @@
};
template <class _Scheduler, class _Sexpr, class _Receiver>
-struct __state : __enable_receiver_from_this<_Sexpr, _Receiver>, __immovable
+struct __state :
+ __enable_receiver_from_this<_Sexpr, _Receiver,
+ __state<_Scheduler, _Sexpr, _Receiver>>,
+ __immovable
{
using __variant_t = __variant_for<__child_of<_Sexpr>, env_of_t<_Receiver>>;
using __receiver2_t = __receiver2<_Scheduler, _Sexpr, _Receiver>;
__variant_t __data_;
connect_result_t<schedule_result_t<_Scheduler>, __receiver2_t> __state2_;
- STDEXEC_APPLE_CLANG(__state* __self_;)
explicit __state(_Scheduler __sched) :
__data_(), __state2_(connect(schedule(__sched), __receiver2_t{this}))
- STDEXEC_APPLE_CLANG(, __self_(this))
{}
};
@@ -245,8 +246,6 @@
[]<class _State, class _Receiver, class _Tag, class... _Args>(
__ignore, _State& __state, _Receiver& __rcvr, _Tag __tag,
_Args&&... __args) noexcept -> void {
- STDEXEC_APPLE_CLANG(
- __state.__self_ == &__state ? void() : std::terminate());
// Write the tag and the args into the operation state so that we can
// forward the completion from within the scheduler's execution context.
if constexpr (__nothrow_callable<__tup::__mktuple_t, _Tag, _Args...>)
diff --git a/include/sdbusplus/async/stdexec/__detail/__schedulers.hpp b/include/sdbusplus/async/stdexec/__detail/__schedulers.hpp
index c60a8ff..3933cf4 100644
--- a/include/sdbusplus/async/stdexec/__detail/__schedulers.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__schedulers.hpp
@@ -108,15 +108,16 @@
}
template <class _Env>
- requires tag_invocable<get_delegatee_scheduler_t, const _Env&>
-inline auto get_delegatee_scheduler_t::operator()(const _Env& __env) const
- noexcept -> tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>
+ requires tag_invocable<get_delegation_scheduler_t, const _Env&>
+inline auto get_delegation_scheduler_t::operator()(const _Env& __env) const
+ noexcept -> tag_invoke_result_t<get_delegation_scheduler_t, const _Env&>
{
static_assert(
- nothrow_tag_invocable<get_delegatee_scheduler_t, const _Env&>);
+ nothrow_tag_invocable<get_delegation_scheduler_t, const _Env&>);
static_assert(
- scheduler<tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>>);
- return tag_invoke(get_delegatee_scheduler_t{}, __env);
+ scheduler<
+ tag_invoke_result_t<get_delegation_scheduler_t, const _Env&>>);
+ return tag_invoke(get_delegation_scheduler_t{}, __env);
}
template <__completion_tag _Tag>
diff --git a/include/sdbusplus/async/stdexec/__detail/__sender_adaptor_closure.hpp b/include/sdbusplus/async/stdexec/__detail/__sender_adaptor_closure.hpp
index 3328e30..1124e60 100644
--- a/include/sdbusplus/async/stdexec/__detail/__sender_adaptor_closure.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__sender_adaptor_closure.hpp
@@ -104,21 +104,62 @@
STDEXEC_ATTRIBUTE((no_unique_address))
_Fun __fun_{};
+#if STDEXEC_INTELLISENSE()
+ // MSVCBUG
+ // https://developercommunity.visualstudio.com/t/rejects-valid-EDG-invocation-of-lambda/10786020
+
+ template <class _Sender>
+ struct __lambda_rvalue
+ {
+ __binder_back& __self_;
+ _Sender& __sndr_;
+
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ auto operator()(_As&... __as) const //
+ noexcept(__nothrow_callable<_Fun, _Sender, _As...>)
+ -> __call_result_t<_Fun, _Sender, _As...>
+ {
+ return static_cast<_Fun&&>(__self_.__fun_)(
+ static_cast<_Sender&&>(__sndr_), static_cast<_As&&>(__as)...);
+ }
+ };
+
+ template <class _Sender>
+ struct __lambda_lvalue
+ {
+ const __binder_back& __self_;
+ _Sender& __sndr_;
+
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ auto operator()(const _As&... __as) const //
+ noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>)
+ -> __call_result_t<const _Fun&, _Sender, const _As&...>
+ {
+ return __self_.__fun_(static_cast<_Sender&&>(__sndr_), __as...);
+ }
+ };
+#endif
+
template <sender _Sender>
requires __callable<_Fun, _Sender, _As...>
STDEXEC_ATTRIBUTE((host, device, always_inline))
- __call_result_t<_Fun, _Sender, _As...> operator()(
- _Sender&& __sndr) && noexcept(__nothrow_callable<_Fun, _Sender, _As...>)
+ auto operator()(_Sender&& __sndr) && //
+ noexcept(__nothrow_callable<_Fun, _Sender, _As...>)
+ -> __call_result_t<_Fun, _Sender, _As...>
{
+#if STDEXEC_INTELLISENSE()
+ return this->apply(__lambda_rvalue<_Sender>{*this, __sndr}, *this);
+#else
return this->apply(
- [&__sndr, this](_As&... __as) noexcept(
- __nothrow_callable<_Fun, _Sender, _As...>)
+ [&__sndr, this](_As&... __as) //
+ noexcept(__nothrow_callable<_Fun, _Sender, _As...>)
-> __call_result_t<_Fun, _Sender, _As...> {
return static_cast<_Fun&&>(
__fun_)(static_cast<_Sender&&>(__sndr),
static_cast<_As&&>(__as)...);
},
*this);
+#endif
}
template <sender _Sender>
@@ -128,13 +169,17 @@
noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>)
-> __call_result_t<const _Fun&, _Sender, const _As&...>
{
+#if STDEXEC_INTELLISENSE()
+ return this->apply(__lambda_lvalue<_Sender>{*this, __sndr}, *this);
+#else
return this->apply(
- [&__sndr, this](const _As&... __as) noexcept(
- __nothrow_callable<_Fun, _Sender, const _As&...>)
+ [&__sndr, this](const _As&... __as) //
+ noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>)
-> __call_result_t<const _Fun&, _Sender, const _As&...> {
return __fun_(static_cast<_Sender&&>(__sndr), __as...);
},
*this);
+#endif
}
};
} // namespace __closure
diff --git a/include/sdbusplus/async/stdexec/__detail/__senders.hpp b/include/sdbusplus/async/stdexec/__detail/__senders.hpp
index 32c0a77..bfb1c01 100644
--- a/include/sdbusplus/async/stdexec/__detail/__senders.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__senders.hpp
@@ -91,6 +91,7 @@
struct get_completion_signatures_t
{
template <class _Sender, class... _Env>
+ requires(sizeof...(_Env) <= 1)
static auto __impl()
{
// Compute the type of the transformed sender:
diff --git a/include/sdbusplus/async/stdexec/__detail/__shared.hpp b/include/sdbusplus/async/stdexec/__detail/__shared.hpp
index 8e008d6..0922594 100644
--- a/include/sdbusplus/async/stdexec/__detail/__shared.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__shared.hpp
@@ -95,7 +95,8 @@
template <class _CvrefSender, class _Receiver>
struct __local_state :
__local_state_base,
- __enable_receiver_from_this<_CvrefSender, _Receiver>
+ __enable_receiver_from_this<_CvrefSender, _Receiver,
+ __local_state<_CvrefSender, _Receiver>>
{
using __tag_t = tag_of_t<_CvrefSender>;
using __stok_t = stop_token_of_t<env_of_t<_Receiver>>;
@@ -228,6 +229,7 @@
return &__tombstone_;
}
+//! Heap-allocatable shared state for things like `stdexec::split`.
template <class _CvrefSender, class _Env>
struct __shared_state :
private __enable_intrusive_from_this<__shared_state<_CvrefSender, _Env>, 2>
diff --git a/include/sdbusplus/async/stdexec/__detail/__spin_loop_pause.hpp b/include/sdbusplus/async/stdexec/__detail/__spin_loop_pause.hpp
index 7cae36f..bef1fae 100644
--- a/include/sdbusplus/async/stdexec/__detail/__spin_loop_pause.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__spin_loop_pause.hpp
@@ -24,7 +24,7 @@
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \
defined(_M_IX86)
-#if STDEXEC_MSVC()
+#if STDEXEC_MSVC_HEADERS()
#include <intrin.h>
#endif
namespace stdexec
@@ -32,7 +32,7 @@
STDEXEC_ATTRIBUTE((always_inline))
static void __spin_loop_pause() noexcept
{
-#if STDEXEC_MSVC()
+#if STDEXEC_MSVC_HEADERS()
_mm_pause();
#else
__builtin_ia32_pause();
diff --git a/include/sdbusplus/async/stdexec/__detail/__starts_on.hpp b/include/sdbusplus/async/stdexec/__detail/__starts_on.hpp
new file mode 100644
index 0000000..9478d10
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__starts_on.hpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2021-2024 NVIDIA Corporation
+ *
+ * Licensed under the Apache License Version 2.0 with LLVM Exceptions
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://llvm.org/LICENSE.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "__execution_fwd.hpp"
+
+// include these after __execution_fwd.hpp
+#include "__concepts.hpp"
+#include "__diagnostics.hpp"
+#include "__domain.hpp"
+#include "__env.hpp"
+#include "__let.hpp"
+#include "__meta.hpp"
+#include "__schedulers.hpp"
+#include "__senders_core.hpp"
+#include "__tag_invoke.hpp"
+#include "__transform_sender.hpp"
+#include "__utility.hpp"
+
+namespace stdexec
+{
+namespace __detail
+{
+//! Constant function object always returning `__val_`.
+template <class _Ty, class = __name_of<__decay_t<_Ty>>>
+struct __always
+{
+ _Ty __val_;
+
+ auto operator()() noexcept -> _Ty
+ {
+ return static_cast<_Ty&&>(__val_);
+ }
+};
+
+template <class _Ty>
+__always(_Ty) -> __always<_Ty>;
+} // namespace __detail
+
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.starts_on]
+namespace __starts_on_ns
+{
+struct starts_on_t
+{
+ using _Sender = __1;
+ using _Scheduler = __0;
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(starts_on_t, _Scheduler, _Sender)>;
+
+ template <scheduler _Scheduler, sender _Sender>
+ auto operator()(_Scheduler&& __sched,
+ _Sender&& __sndr) const -> __well_formed_sender auto
+ {
+ auto __domain = query_or(get_domain, __sched, default_domain());
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<starts_on_t>(static_cast<_Scheduler&&>(__sched),
+ static_cast<_Sender&&>(__sndr)));
+ }
+
+ template <class _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ static auto __transform_env_fn(_Env&& __env) noexcept
+ {
+ return [&](__ignore, auto __sched, __ignore) noexcept {
+ return __detail::__mkenv_sched(static_cast<_Env&&>(__env), __sched);
+ };
+ }
+
+ template <class _Sender, class _Env>
+ static auto transform_env(const _Sender& __sndr, _Env&& __env) noexcept
+ {
+ return __sexpr_apply(__sndr,
+ __transform_env_fn(static_cast<_Env&&>(__env)));
+ }
+
+ template <class _Sender, class _Env>
+ static auto transform_sender(_Sender&& __sndr, const _Env&)
+ {
+ return __sexpr_apply(
+ static_cast<_Sender&&>(__sndr),
+ []<class _Data, class _Child>(__ignore, _Data&& __data,
+ _Child&& __child) {
+ // This is the heart of starts_on: It uses `let_value` to
+ // schedule `__child` on the given scheduler:
+ return let_value(
+ schedule(__data),
+ __detail::__always{static_cast<_Child&&>(__child)});
+ });
+ }
+};
+} // namespace __starts_on_ns
+
+using __starts_on_ns::starts_on_t;
+inline constexpr starts_on_t starts_on{};
+
+using on_t = starts_on_t;
+inline constexpr starts_on_t on{};
+
+using start_on_t = starts_on_t;
+inline constexpr starts_on_t start_on{};
+
+template <>
+struct __sexpr_impl<starts_on_t> : __sexpr_defaults
+{
+ static constexpr auto get_completion_signatures = //
+ []<class _Sender>(_Sender&&) noexcept //
+ -> __completion_signatures_of_t< //
+ transform_sender_result_t<default_domain, _Sender, empty_env>> {
+ return {};
+ };
+};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__sync_wait.hpp b/include/sdbusplus/async/stdexec/__detail/__sync_wait.hpp
index 9032a3e..122dd5a 100644
--- a/include/sdbusplus/async/stdexec/__detail/__sync_wait.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__sync_wait.hpp
@@ -55,8 +55,8 @@
return __loop_->get_scheduler();
}
- auto
- query(get_delegatee_scheduler_t) const noexcept -> run_loop::__scheduler
+ auto query(get_delegation_scheduler_t) const noexcept
+ -> run_loop::__scheduler
{
return __loop_->get_scheduler();
}
@@ -203,7 +203,7 @@
__q<__too_many_successful_completions_error>>,
_Sender, __env>>;
-#if STDEXEC_NVHPC()
+#if STDEXEC_EDG()
// It requires some hoop-jumping to get the NVHPC compiler to report a
// meaningful diagnostic for SFINAE failures.
template <class _Sender>
@@ -263,7 +263,7 @@
static_cast<_Sender&&>(__sndr));
}
-#if STDEXEC_NVHPC()
+#if STDEXEC_EDG()
// This is needed to get sensible diagnostics from nvc++
template <class _Sender, class _Error = __error_description_t<_Sender>>
auto operator()(_Sender&&, [[maybe_unused]] _Error __diagnostic = {}) const
@@ -286,7 +286,7 @@
/// `sync_wait` connects and starts the given sender, and then drives a
/// `run_loop` instance until the sender completes. Additional work
/// can be delegated to the `run_loop` by scheduling work on the
- /// scheduler returned by calling `get_delegatee_scheduler` on the
+ /// scheduler returned by calling `get_delegation_scheduler` on the
/// receiver's environment.
///
/// @pre The sender must have a exactly one value completion signature. That
@@ -308,22 +308,23 @@
auto apply_sender(_Sender&& __sndr) const
-> std::optional<__sync_wait_result_t<_Sender>>
{
- __state __local{};
+ __state __local_state{};
std::optional<__sync_wait_result_t<_Sender>> __result{};
// Launch the sender with a continuation that will fill in the __result
- // optional or set the exception_ptr in __local.
- auto __op_state = connect(static_cast<_Sender&&>(__sndr),
- __receiver_t<_Sender>{&__local, &__result});
+ // optional or set the exception_ptr in __local_state.
+ auto __op_state =
+ connect(static_cast<_Sender&&>(__sndr),
+ __receiver_t<_Sender>{&__local_state, &__result});
stdexec::start(__op_state);
// Wait for the variant to be filled in.
- __local.__loop_.run();
+ __local_state.__loop_.run();
- if (__local.__eptr_)
+ if (__local_state.__eptr_)
{
std::rethrow_exception(
- static_cast<std::exception_ptr&&>(__local.__eptr_));
+ static_cast<std::exception_ptr&&>(__local_state.__eptr_));
}
return __result;
@@ -353,7 +354,7 @@
static_cast<_Sender&&>(__sndr));
}
-#if STDEXEC_NVHPC()
+#if STDEXEC_EDG()
template <class _Sender, class _Error = __error_description_t<
__result_of<into_variant, _Sender>>>
auto operator()(_Sender&&, [[maybe_unused]] _Error __diagnostic = {}) const
diff --git a/include/sdbusplus/async/stdexec/__detail/__transfer_just.hpp b/include/sdbusplus/async/stdexec/__detail/__transfer_just.hpp
index fe189b1..682ee61 100644
--- a/include/sdbusplus/async/stdexec/__detail/__transfer_just.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__transfer_just.hpp
@@ -20,7 +20,7 @@
// include these after __execution_fwd.hpp
#include "__basic_sender.hpp"
#include "__concepts.hpp"
-#include "__continue_on.hpp"
+#include "__continues_on.hpp"
#include "__domain.hpp"
#include "__env.hpp"
#include "__just.hpp"
@@ -55,8 +55,8 @@
{
return [&]<class _Scheduler, class... _Values>(_Scheduler&& __sched,
_Values&&... __vals) {
- return continue_on(just(static_cast<_Values&&>(__vals)...),
- static_cast<_Scheduler&&>(__sched));
+ return continues_on(just(static_cast<_Values&&>(__vals)...),
+ static_cast<_Scheduler&&>(__sched));
};
}
diff --git a/include/sdbusplus/async/stdexec/__detail/__tuple.hpp b/include/sdbusplus/async/stdexec/__detail/__tuple.hpp
index 63c79b2..3c5f2d2 100644
--- a/include/sdbusplus/async/stdexec/__detail/__tuple.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__tuple.hpp
@@ -58,6 +58,18 @@
template <std::size_t... _Is, __indices<_Is...> _Idx, class... _Ts>
struct __tuple<_Idx, _Ts...> : __box<_Ts, _Is>...
{
+ template <class... _Us>
+ static __tuple __convert_from(__tuple<_Idx, _Us...>&& __tup)
+ {
+ return __tuple{{static_cast<_Us&&>(__tup.__box<_Us, _Is>::__value)}...};
+ }
+
+ template <class... _Us>
+ static __tuple __convert_from(const __tuple<_Idx, _Us...>& __tup)
+ {
+ return __tuple{{__tup.__box<_Us, _Is>::__value}...};
+ }
+
template <class _Fn, class _Self, class... _Us>
STDEXEC_ATTRIBUTE((host, device, always_inline))
static auto apply(_Fn&& __fn, _Self&& __self, _Us&&... __us) //
diff --git a/include/sdbusplus/async/stdexec/__detail/__type_traits.hpp b/include/sdbusplus/async/stdexec/__detail/__type_traits.hpp
index 41a17c7..8c6cc67 100644
--- a/include/sdbusplus/async/stdexec/__detail/__type_traits.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__type_traits.hpp
@@ -31,10 +31,23 @@
// __decay_t: An efficient implementation for std::decay
#if STDEXEC_HAS_BUILTIN(__decay)
-template <class _Ty>
-using __decay_t = __decay(_Ty);
+namespace __tt
+{
+template <class>
+struct __wrap;
-#elif STDEXEC_NVHPC()
+template <bool>
+struct __decay_
+{
+ template <class _Ty>
+ using __f = __decay(_Ty);
+};
+} // namespace __tt
+template <class _Ty>
+using __decay_t = typename __tt::__decay_<sizeof(__tt::__wrap<_Ty>*) ==
+ ~0ul>::template __f<_Ty>;
+
+#elif STDEXEC_EDG()
template <class _Ty>
using __decay_t = std::decay_t<_Ty>;
diff --git a/include/sdbusplus/async/stdexec/__detail/__variant.hpp b/include/sdbusplus/async/stdexec/__detail/__variant.hpp
index a3ea4bd..b49e4cf 100644
--- a/include/sdbusplus/async/stdexec/__detail/__variant.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__variant.hpp
@@ -138,7 +138,7 @@
__destroy();
::new (__storage_) _Ty{static_cast<_As&&>(__as)...};
__index_ = __new_index;
- return *reinterpret_cast<_Ty*>(__storage_);
+ return *std::launder(reinterpret_cast<_Ty*>(__storage_));
}
template <std::size_t _Ny, class... _As>
@@ -151,11 +151,26 @@
__destroy();
::new (__storage_) __at<_Ny>{static_cast<_As&&>(__as)...};
__index_ = _Ny;
- return *reinterpret_cast<__at<_Ny>*>(__storage_);
+ return *std::launder(reinterpret_cast<__at<_Ny>*>(__storage_));
+ }
+
+ template <std::size_t _Ny, class _Fn, class... _As>
+ STDEXEC_ATTRIBUTE((host, device))
+ __at<_Ny>& emplace_from_at(_Fn&& __fn, _As&&... __as) //
+ noexcept(__nothrow_callable<_Fn, _As...>)
+ {
+ static_assert(__same_as<__call_result_t<_Fn, _As...>, __at<_Ny>>,
+ "callable does not return the correct type");
+
+ __destroy();
+ ::new (__storage_)
+ __at<_Ny>(static_cast<_Fn&&>(__fn)(static_cast<_As&&>(__as)...));
+ __index_ = _Ny;
+ return *std::launder(reinterpret_cast<__at<_Ny>*>(__storage_));
}
template <class _Fn, class... _As>
- STDEXEC_ATTRIBUTE((host, device))
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
auto emplace_from(_Fn&& __fn, _As&&... __as) //
noexcept(__nothrow_callable<_Fn, _As...>)
-> __call_result_t<_Fn, _As...>&
@@ -164,12 +179,8 @@
constexpr std::size_t __new_index =
stdexec::__index_of<__result_t, _Ts...>();
static_assert(__new_index != __variant_npos, "Type not in variant");
-
- __destroy();
- ::new (__storage_)
- __result_t(static_cast<_Fn&&>(__fn)(static_cast<_As&&>(__as)...));
- __index_ = __new_index;
- return *reinterpret_cast<__result_t*>(__storage_);
+ return emplace_from_at<__new_index>(static_cast<_Fn&&>(__fn),
+ static_cast<_As&&>(__as)...);
}
template <class _Fn, class _Self, class... _As>
diff --git a/include/sdbusplus/async/stdexec/__detail/__when_all.hpp b/include/sdbusplus/async/stdexec/__detail/__when_all.hpp
index 83bbe59..29e373b 100644
--- a/include/sdbusplus/async/stdexec/__detail/__when_all.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__when_all.hpp
@@ -21,7 +21,7 @@
#include "../stop_token.hpp"
#include "__basic_sender.hpp"
#include "__concepts.hpp"
-#include "__continue_on.hpp"
+#include "__continues_on.hpp"
#include "__diagnostics.hpp"
#include "__domain.hpp"
#include "__env.hpp"
@@ -206,7 +206,7 @@
template <class _Receiver>
void __arrive(_Receiver& __rcvr) noexcept
{
- if (0 == --__count_)
+ if (1 == __count_.fetch_sub(1))
{
__complete(__rcvr);
}
@@ -410,7 +410,7 @@
{
// We only need to bother recording the completion values
// if we're not already in the "error" or "stopped" state.
- if (__state.__state_ == __started)
+ if (__state.__state_.load() == __started)
{
auto& __opt_values = __tup::get<__v<_Index>>(__state.__values_);
using _Tuple = __decayed_tuple<_Args...>;
@@ -527,7 +527,7 @@
static_cast<_Sender&&>(__sndr),
[&]<class _Data, class... _Child>(__ignore, _Data&& __data,
_Child&&... __child) {
- return continue_on(
+ return continues_on(
when_all_t()(static_cast<_Child&&>(__child)...),
get_completion_scheduler<set_value_t>(__data));
});
diff --git a/include/sdbusplus/async/stdexec/async_scope.hpp b/include/sdbusplus/async/stdexec/async_scope.hpp
index 6482366..67b223d 100644
--- a/include/sdbusplus/async/stdexec/async_scope.hpp
+++ b/include/sdbusplus/async/stdexec/async_scope.hpp
@@ -16,6 +16,7 @@
#pragma once
#include "../stdexec/__detail/__intrusive_queue.hpp"
+#include "../stdexec/__detail/__optional.hpp"
#include "../stdexec/execution.hpp"
#include "../stdexec/stop_token.hpp"
#include "env.hpp"
@@ -181,13 +182,13 @@
auto& __active = __scope->__active_;
if (--__active == 0)
{
- auto __local = std::move(__scope->__waiters_);
+ auto __local_waiters = std::move(__scope->__waiters_);
__guard.unlock();
__scope = nullptr;
// do not access __scope
- while (!__local.empty())
+ while (!__local_waiters.empty())
{
- auto* __next = __local.pop_front();
+ auto* __next = __local_waiters.pop_front();
__next->__notify_waiter(__next);
// __scope must be considered deleted
}
@@ -371,6 +372,7 @@
{
try
{
+ __forward_consumer_.reset();
auto __state = std::move(__state_);
STDEXEC_ASSERT(__state != nullptr);
std::unique_lock __guard{__state->__mutex_};
@@ -423,7 +425,7 @@
_Receiver __rcvr_;
std::unique_ptr<__future_state<_Sender, _Env>> __state_;
STDEXEC_ATTRIBUTE((no_unique_address))
- __forward_consumer __forward_consumer_;
+ stdexec::__optional<__forward_consumer> __forward_consumer_;
public:
using __id = __future_op;
@@ -455,7 +457,7 @@
}},
__rcvr_(static_cast<_Receiver2&&>(__rcvr)),
__state_(std::move(__state)),
- __forward_consumer_(get_stop_token(get_env(__rcvr_)),
+ __forward_consumer_(std::in_place, get_stop_token(get_env(__rcvr_)),
__forward_stopped{&__state_->__stop_source_})
{}
@@ -486,7 +488,7 @@
};
};
-#if STDEXEC_NVHPC()
+#if STDEXEC_EDG()
template <class _Fn>
struct __completion_as_tuple2_;
@@ -592,7 +594,8 @@
}
inplace_stop_source __stop_source_;
- std::optional<inplace_stop_callback<__forward_stopped>> __forward_scope_;
+ stdexec::__optional<inplace_stop_callback<__forward_stopped>>
+ __forward_scope_;
std::mutex __mutex_;
__future_step __step_ = __future_step::__created;
std::unique_ptr<__future_state_base, __dynamic_delete<__future_state_base>>
@@ -614,12 +617,11 @@
__future_state_base<_Completions, _Env>* __state_;
const __impl* __scope_;
- void __dispatch_result_() noexcept
+ void __dispatch_result_(std::unique_lock<std::mutex>& __guard) noexcept
{
auto& __state = *__state_;
- std::unique_lock __guard{__state.__mutex_};
- auto __local = std::move(__state.__subscribers_);
- __state.__forward_scope_ = std::nullopt;
+ auto __local_subscribers = std::move(__state.__subscribers_);
+ __state.__forward_scope_.reset();
if (__state.__no_future_.get() != nullptr)
{
// nobody is waiting for the results
@@ -631,24 +633,22 @@
return;
}
__guard.unlock();
- while (!__local.empty())
+ while (!__local_subscribers.empty())
{
- auto* __sub = __local.pop_front();
+ auto* __sub = __local_subscribers.pop_front();
__sub->__complete();
}
}
template <class _Tag, class... _As>
- bool __save_completion(_Tag, _As&&... __as) noexcept
+ void __save_completion(_Tag, _As&&... __as) noexcept
{
auto& __state = *__state_;
try
{
- std::unique_lock __guard{__state.__mutex_};
using _Tuple = __decayed_std_tuple<_Tag, _As...>;
__state.__data_.template emplace<_Tuple>(
_Tag(), static_cast<_As&&>(__as)...);
- return true;
}
catch (...)
{
@@ -656,33 +656,32 @@
__state.__data_.template emplace<_Tuple>(
set_error_t(), std::current_exception());
}
- return false;
}
template <__movable_value... _As>
void set_value(_As&&... __as) noexcept
{
- if (__save_completion(set_value_t(), static_cast<_As&&>(__as)...))
- {
- __dispatch_result_();
- }
+ auto& __state = *__state_;
+ std::unique_lock __guard{__state.__mutex_};
+ __save_completion(set_value_t(), static_cast<_As&&>(__as)...);
+ __dispatch_result_(__guard);
}
template <__movable_value _Error>
void set_error(_Error&& __err) noexcept
{
- if (__save_completion(set_error_t(), static_cast<_Error&&>(__err)))
- {
- __dispatch_result_();
- }
+ auto& __state = *__state_;
+ std::unique_lock __guard{__state.__mutex_};
+ __save_completion(set_error_t(), static_cast<_Error&&>(__err));
+ __dispatch_result_(__guard);
}
void set_stopped() noexcept
{
- if (__save_completion(set_stopped_t()))
- {
- __dispatch_result_();
- }
+ auto& __state = *__state_;
+ std::unique_lock __guard{__state.__mutex_};
+ __save_completion(set_stopped_t());
+ __dispatch_result_(__guard);
}
auto get_env() const noexcept -> const __env_t<_Env>&
@@ -927,8 +926,8 @@
// start is noexcept so we can assume that the operation will complete
// after this, which means we can rely on its self-ownership to ensure
// that it is eventually deleted
- stdexec::start(*new __op_t{nest(static_cast<_Sender&&>(__sndr)),
- static_cast<_Env&&>(__env), &__impl_});
+ stdexec::start(*(new __op_t{nest(static_cast<_Sender&&>(__sndr)),
+ static_cast<_Env&&>(__env), &__impl_}));
}
template <__movable_value _Env = empty_env,
diff --git a/include/sdbusplus/async/stdexec/at_coroutine_exit.hpp b/include/sdbusplus/async/stdexec/at_coroutine_exit.hpp
index dac8f8b..28b5a51 100644
--- a/include/sdbusplus/async/stdexec/at_coroutine_exit.hpp
+++ b/include/sdbusplus/async/stdexec/at_coroutine_exit.hpp
@@ -151,9 +151,15 @@
public:
using promise_type = __promise;
+#if STDEXEC_EDG()
+ __task(__coro::coroutine_handle<__promise> __coro) noexcept :
+ __coro_(__coro)
+ {}
+#else
explicit __task(__coro::coroutine_handle<__promise> __coro) noexcept :
__coro_(__coro)
{}
+#endif
__task(__task&& __that) noexcept :
__coro_(std::exchange(__that.__coro_, {}))
@@ -214,9 +220,15 @@
struct __promise : with_awaitable_senders<__promise>
{
+#if STDEXEC_EDG()
+ template <class _Action>
+ __promise(_Action&&, _Ts&&... __ts) noexcept : __args_{__ts...}
+ {}
+#else
template <class _Action>
explicit __promise(_Action&&, _Ts&... __ts) noexcept : __args_{__ts...}
{}
+#endif
auto initial_suspend() noexcept -> __coro::suspend_always
{
diff --git a/include/sdbusplus/async/stdexec/commit.info b/include/sdbusplus/async/stdexec/commit.info
index e3aca22..349afcb 100644
--- a/include/sdbusplus/async/stdexec/commit.info
+++ b/include/sdbusplus/async/stdexec/commit.info
@@ -1 +1 @@
-439a29175f45aecdec6b124e75dc3c7bc5d8b778
+c3f90d40a3d4a596e44cc115e2249a2eeb6fcde0
diff --git a/include/sdbusplus/async/stdexec/coroutine.hpp b/include/sdbusplus/async/stdexec/coroutine.hpp
index 2426ed3..e9d3439 100644
--- a/include/sdbusplus/async/stdexec/coroutine.hpp
+++ b/include/sdbusplus/async/stdexec/coroutine.hpp
@@ -18,7 +18,7 @@
#include "__detail/__awaitable.hpp"
#include "__detail/__config.hpp"
-#if STDEXEC_MSVC() && _MSC_VER >= 1939
+#if STDEXEC_MSVC() && _MSC_VER <= 1939
namespace stdexec
{
// MSVCBUG
diff --git a/include/sdbusplus/async/stdexec/execution.hpp b/include/sdbusplus/async/stdexec/execution.hpp
index 0dc3800..65956d4 100644
--- a/include/sdbusplus/async/stdexec/execution.hpp
+++ b/include/sdbusplus/async/stdexec/execution.hpp
@@ -23,7 +23,7 @@
#include "__detail/__bulk.hpp"
#include "__detail/__completion_signatures.hpp"
#include "__detail/__connect_awaitable.hpp"
-#include "__detail/__continue_on.hpp"
+#include "__detail/__continues_on.hpp"
#include "__detail/__cpo.hpp"
#include "__detail/__debug.hpp"
#include "__detail/__domain.hpp"
@@ -49,7 +49,7 @@
#include "__detail/__senders.hpp"
#include "__detail/__split.hpp"
#include "__detail/__start_detached.hpp"
-#include "__detail/__start_on.hpp"
+#include "__detail/__starts_on.hpp"
#include "__detail/__stopped_as_error.hpp"
#include "__detail/__stopped_as_optional.hpp"
#include "__detail/__submit.hpp"
diff --git a/include/sdbusplus/async/stdexec/task.hpp b/include/sdbusplus/async/stdexec/task.hpp
index 1f8a038..a6ce255 100644
--- a/include/sdbusplus/async/stdexec/task.hpp
+++ b/include/sdbusplus/async/stdexec/task.hpp
@@ -429,9 +429,10 @@
{
// TODO: If we have a complete-where-it-starts query then we can
// optimize this to avoid the reschedule
- return as_awaitable(transfer(static_cast<_Awaitable&&>(__awaitable),
- get_scheduler(*__context_)),
- *this);
+ return as_awaitable(
+ continues_on(static_cast<_Awaitable&&>(__awaitable),
+ get_scheduler(*__context_)),
+ *this);
}
template <class _Scheduler>
diff --git a/include/sdbusplus/async/timer.hpp b/include/sdbusplus/async/timer.hpp
index 3a88cdf..47032e5 100644
--- a/include/sdbusplus/async/timer.hpp
+++ b/include/sdbusplus/async/timer.hpp
@@ -104,7 +104,8 @@
// Run the delay sender and then switch back to the worker thread.
// The delay completion happens from the sd-event handler, which is
// ran on the 'caller' thread.
- return execution::transfer(sleep_sender(ctx, time), get_scheduler(ctx));
+ return execution::continues_on(sleep_sender(ctx, time),
+ get_scheduler(ctx));
}
private:
diff --git a/src/async/context.cpp b/src/async/context.cpp
index 96535fa..17ec7ce 100644
--- a/src/async/context.cpp
+++ b/src/async/context.cpp
@@ -103,8 +103,8 @@
{
// Handle the next sdbus event. Completion likely happened on a
// different thread so we need to transfer back to the worker thread.
- co_await execution::transfer(wait_process_sender(ctx),
- ctx.loop.get_scheduler());
+ co_await execution::continues_on(wait_process_sender(ctx),
+ ctx.loop.get_scheduler());
}
{