stdexec: update to latest commit
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I52e2ff4ea33617fb055981000ce3c2f40faab43e
diff --git a/include/sdbusplus/async/stdexec/__detail/__as_awaitable.hpp b/include/sdbusplus/async/stdexec/__detail/__as_awaitable.hpp
new file mode 100644
index 0000000..e68ef30
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__as_awaitable.hpp
@@ -0,0 +1,295 @@
+/*
+ * 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 "__awaitable.hpp"
+#include "__concepts.hpp"
+#include "__config.hpp"
+#include "__cpo.hpp"
+#include "__execution_fwd.hpp"
+#include "__meta.hpp"
+#include "__receivers.hpp"
+#include "__senders.hpp"
+#include "__tag_invoke.hpp"
+#include "__transform_completion_signatures.hpp"
+#include "__type_traits.hpp"
+
+#include <exception>
+#include <system_error>
+#include <variant>
+
+namespace stdexec
+{
+#if !STDEXEC_STD_NO_COROUTINES()
+/////////////////////////////////////////////////////////////////////////////
+// stdexec::as_awaitable [execution.coro_utils.as_awaitable]
+namespace __as_awaitable
+{
+struct __void
+{};
+
+template <class _Value>
+using __value_or_void_t = __if_c<__same_as<_Value, void>, __void, _Value>;
+
+template <class _Value>
+using __expected_t =
+ std::variant<std::monostate, __value_or_void_t<_Value>, std::exception_ptr>;
+
+template <class _Value>
+struct __receiver_base
+{
+ using receiver_concept = receiver_t;
+
+ template <class... _Us>
+ requires constructible_from<__value_or_void_t<_Value>, _Us...>
+ void set_value(_Us&&... __us) noexcept
+ {
+ try
+ {
+ __result_->template emplace<1>(static_cast<_Us&&>(__us)...);
+ __continuation_.resume();
+ }
+ catch (...)
+ {
+ stdexec::set_error(static_cast<__receiver_base&&>(*this),
+ std::current_exception());
+ }
+ }
+
+ template <class _Error>
+ void set_error(_Error&& __err) noexcept
+ {
+ if constexpr (__decays_to<_Error, std::exception_ptr>)
+ __result_->template emplace<2>(static_cast<_Error&&>(__err));
+ else if constexpr (__decays_to<_Error, std::error_code>)
+ __result_->template emplace<2>(
+ std::make_exception_ptr(std::system_error(__err)));
+ else
+ __result_->template emplace<2>(
+ std::make_exception_ptr(static_cast<_Error&&>(__err)));
+ __continuation_.resume();
+ }
+
+ __expected_t<_Value>* __result_;
+ __coro::coroutine_handle<> __continuation_;
+};
+
+template <class _PromiseId, class _Value>
+struct __receiver
+{
+ using _Promise = stdexec::__t<_PromiseId>;
+
+ struct __t : __receiver_base<_Value>
+ {
+ using __id = __receiver;
+
+ void set_stopped() noexcept
+ {
+ auto __continuation =
+ __coro::coroutine_handle<_Promise>::from_address(
+ this->__continuation_.address());
+ __coro::coroutine_handle<> __stopped_continuation =
+ __continuation.promise().unhandled_stopped();
+ __stopped_continuation.resume();
+ }
+
+ // Forward get_env query to the coroutine promise
+ auto get_env() const noexcept -> env_of_t<_Promise&>
+ {
+ auto __continuation =
+ __coro::coroutine_handle<_Promise>::from_address(
+ this->__continuation_.address());
+ return stdexec::get_env(__continuation.promise());
+ }
+ };
+};
+
+// BUGBUG NOT TO SPEC: make senders of more-than-one-value awaitable
+// by packaging the values into a tuple.
+// See: https://github.com/cplusplus/sender-receiver/issues/182
+template <std::size_t _Count>
+extern const __q<__decayed_std_tuple> __as_single;
+
+template <>
+inline const __q<__midentity> __as_single<1>;
+
+template <>
+inline const __mconst<void> __as_single<0>;
+
+template <class... _Values>
+using __single_value =
+ __minvoke<decltype(__as_single<sizeof...(_Values)>), _Values...>;
+
+template <class _Sender, class _Promise>
+using __value_t =
+ __decay_t<__value_types_of_t<_Sender, env_of_t<_Promise&>,
+ __q<__single_value>, __msingle_or<void>>>;
+
+template <class _Sender, class _Promise>
+using __receiver_t =
+ __t<__receiver<__id<_Promise>, __value_t<_Sender, _Promise>>>;
+
+template <class _Value>
+struct __sender_awaitable_base
+{
+ [[nodiscard]] auto await_ready() const noexcept -> bool
+ {
+ return false;
+ }
+
+ auto await_resume() -> _Value
+ {
+ switch (__result_.index())
+ {
+ case 0: // receiver contract not satisfied
+ STDEXEC_ASSERT(false && +"_Should never get here" == nullptr);
+ break;
+ case 1: // set_value
+ if constexpr (!__same_as<_Value, void>)
+ return static_cast<_Value&&>(std::get<1>(__result_));
+ else
+ return;
+ case 2: // set_error
+ std::rethrow_exception(std::get<2>(__result_));
+ }
+ std::terminate();
+ }
+
+ protected:
+ __expected_t<_Value> __result_;
+};
+
+template <class _PromiseId, class _SenderId>
+struct __sender_awaitable
+{
+ using _Promise = stdexec::__t<_PromiseId>;
+ using _Sender = stdexec::__t<_SenderId>;
+ using __value = __value_t<_Sender, _Promise>;
+
+ struct __t : __sender_awaitable_base<__value>
+ {
+ __t(_Sender&& sndr, __coro::coroutine_handle<_Promise> __hcoro) //
+ noexcept(__nothrow_connectable<_Sender, __receiver>) :
+ __op_state_(connect(static_cast<_Sender&&>(sndr),
+ __receiver{{&this->__result_, __hcoro}}))
+ {}
+
+ void await_suspend(__coro::coroutine_handle<_Promise>) noexcept
+ {
+ stdexec::start(__op_state_);
+ }
+
+ private:
+ using __receiver = __receiver_t<_Sender, _Promise>;
+ connect_result_t<_Sender, __receiver> __op_state_;
+ };
+};
+
+template <class _Promise, class _Sender>
+using __sender_awaitable_t =
+ __t<__sender_awaitable<__id<_Promise>, __id<_Sender>>>;
+
+template <class _Sender, class _Promise>
+concept __awaitable_sender =
+ sender_in<_Sender, env_of_t<_Promise&>> && //
+ __mvalid<__value_t, _Sender, _Promise> && //
+ sender_to<_Sender, __receiver_t<_Sender, _Promise>> && //
+ requires(_Promise& __promise) {
+ {
+ __promise.unhandled_stopped()
+ } -> convertible_to<__coro::coroutine_handle<>>;
+ };
+
+struct __unspecified
+{
+ auto get_return_object() noexcept -> __unspecified;
+ auto initial_suspend() noexcept -> __unspecified;
+ auto final_suspend() noexcept -> __unspecified;
+ void unhandled_exception() noexcept;
+ void return_void() noexcept;
+ auto unhandled_stopped() noexcept -> __coro::coroutine_handle<>;
+};
+
+struct as_awaitable_t
+{
+ template <class _Tp, class _Promise>
+ static constexpr auto __select_impl_() noexcept
+ {
+ if constexpr (tag_invocable<as_awaitable_t, _Tp, _Promise&>)
+ {
+ using _Result = tag_invoke_result_t<as_awaitable_t, _Tp, _Promise&>;
+ constexpr bool _Nothrow =
+ nothrow_tag_invocable<as_awaitable_t, _Tp, _Promise&>;
+ return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
+ // NOLINTNEXTLINE(bugprone-branch-clone)
+ }
+ else if constexpr (__awaitable<_Tp, __unspecified>)
+ { // NOT __awaitable<_Tp, _Promise> !!
+ using _Result = _Tp&&;
+ return static_cast<_Result (*)() noexcept>(nullptr);
+ }
+ else if constexpr (__awaitable_sender<_Tp, _Promise>)
+ {
+ using _Result = __sender_awaitable_t<_Promise, _Tp>;
+ constexpr bool _Nothrow = __nothrow_constructible_from<
+ _Result, _Tp, __coro::coroutine_handle<_Promise>>;
+ return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
+ }
+ else
+ {
+ using _Result = _Tp&&;
+ return static_cast<_Result (*)() noexcept>(nullptr);
+ }
+ }
+
+ template <class _Tp, class _Promise>
+ using __select_impl_t = decltype(__select_impl_<_Tp, _Promise>());
+
+ template <class _Tp, class _Promise>
+ auto operator()(_Tp&& __t, _Promise& __promise) const
+ noexcept(__nothrow_callable<__select_impl_t<_Tp, _Promise>>)
+ -> __call_result_t<__select_impl_t<_Tp, _Promise>>
+ {
+ if constexpr (tag_invocable<as_awaitable_t, _Tp, _Promise&>)
+ {
+ using _Result = tag_invoke_result_t<as_awaitable_t, _Tp, _Promise&>;
+ static_assert(__awaitable<_Result, _Promise>);
+ return tag_invoke(*this, static_cast<_Tp&&>(__t), __promise);
+ // NOLINTNEXTLINE(bugprone-branch-clone)
+ }
+ else if constexpr (__awaitable<_Tp, __unspecified>)
+ { // NOT __awaitable<_Tp, _Promise> !!
+ return static_cast<_Tp&&>(__t);
+ }
+ else if constexpr (__awaitable_sender<_Tp, _Promise>)
+ {
+ auto __hcoro =
+ __coro::coroutine_handle<_Promise>::from_promise(__promise);
+ return __sender_awaitable_t<_Promise, _Tp>{static_cast<_Tp&&>(__t),
+ __hcoro};
+ }
+ else
+ {
+ return static_cast<_Tp&&>(__t);
+ }
+ }
+};
+} // namespace __as_awaitable
+
+using __as_awaitable::as_awaitable_t;
+inline constexpr as_awaitable_t as_awaitable{};
+#endif
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__awaitable.hpp b/include/sdbusplus/async/stdexec/__detail/__awaitable.hpp
new file mode 100644
index 0000000..7b6c2a1
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__awaitable.hpp
@@ -0,0 +1,156 @@
+/*
+ * 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 "__concepts.hpp"
+#include "__config.hpp"
+#include "__utility.hpp"
+
+namespace stdexec
+{
+#if !STDEXEC_STD_NO_COROUTINES()
+// Define some concepts and utilities for working with awaitables
+template <class _Tp>
+concept __await_suspend_result =
+ __one_of<_Tp, void, bool> ||
+ __is_instance_of<_Tp, __coro::coroutine_handle>;
+
+template <class _Awaiter, class _Promise>
+concept __with_await_suspend =
+ requires(_Awaiter& __awaiter, __coro::coroutine_handle<_Promise> __h) {
+ {
+ __awaiter.await_suspend(__h)
+ } -> __await_suspend_result;
+ };
+
+template <class _Awaiter, class... _Promise>
+concept __awaiter = //
+ requires(_Awaiter& __awaiter) {
+ __awaiter.await_ready() ? 1 : 0;
+ __awaiter.await_resume();
+ } && //
+ (__with_await_suspend<_Awaiter, _Promise> && ...);
+
+#if STDEXEC_MSVC()
+// MSVCBUG
+// https://developercommunity.visualstudio.com/t/operator-co_await-not-found-in-requires/10452721
+
+template <class _Awaitable>
+void __co_await_constraint(_Awaitable&& __awaitable)
+ requires requires {
+ operator co_await(static_cast<_Awaitable&&>(__awaitable));
+ };
+#endif
+
+template <class _Awaitable>
+auto __get_awaiter(_Awaitable&& __awaitable, __ignore = {}) -> decltype(auto)
+{
+ if constexpr (requires {
+ static_cast<_Awaitable&&>(__awaitable)
+ .
+ operator co_await();
+ })
+ {
+ return static_cast<_Awaitable&&>(__awaitable).operator co_await();
+ }
+ else if constexpr (requires {
+#if STDEXEC_MSVC()
+ __co_await_constraint(
+ static_cast<_Awaitable&&>(__awaitable));
+#else
+ operator co_await(static_cast<_Awaitable&&>(__awaitable));
+#endif
+ })
+ {
+ return operator co_await(static_cast<_Awaitable&&>(__awaitable));
+ }
+ else
+ {
+ return static_cast<_Awaitable&&>(__awaitable);
+ }
+}
+
+template <class _Awaitable, class _Promise>
+auto __get_awaiter(_Awaitable&& __awaitable, _Promise* __promise)
+ -> decltype(auto)
+ requires requires {
+ __promise->await_transform(
+ static_cast<_Awaitable&&>(__awaitable));
+ }
+{
+ if constexpr (requires {
+ __promise
+ ->await_transform(
+ static_cast<_Awaitable&&>(__awaitable))
+ .
+ operator co_await();
+ })
+ {
+ return __promise
+ ->await_transform(static_cast<_Awaitable&&>(__awaitable))
+ .
+ operator co_await();
+ }
+ else if constexpr (requires {
+#if STDEXEC_MSVC()
+ __co_await_constraint(__promise->await_transform(
+ static_cast<_Awaitable&&>(__awaitable)));
+#else
+ operator co_await(__promise->await_transform(static_cast<_Awaitable&&>(__awaitable)));
+#endif
+ })
+ {
+ return operator co_await(
+ __promise->await_transform(static_cast<_Awaitable&&>(__awaitable)));
+ }
+ else
+ {
+ return __promise->await_transform(
+ static_cast<_Awaitable&&>(__awaitable));
+ }
+}
+
+template <class _Awaitable, class... _Promise>
+concept __awaitable = //
+ requires(_Awaitable&& __awaitable, _Promise*... __promise) {
+ {
+ stdexec::__get_awaiter(static_cast<_Awaitable&&>(__awaitable),
+ __promise...)
+ } -> __awaiter<_Promise...>;
+ };
+
+template <class _Tp>
+auto __as_lvalue(_Tp&&) -> _Tp&;
+
+template <class _Awaitable, class... _Promise>
+ requires __awaitable<_Awaitable, _Promise...>
+using __await_result_t =
+ decltype(stdexec::__as_lvalue(
+ stdexec::__get_awaiter(std::declval<_Awaitable>(),
+ static_cast<_Promise*>(nullptr)...))
+ .await_resume());
+
+#else
+
+template <class _Awaitable, class... _Promise>
+concept __awaitable = false;
+
+template <class _Awaitable, class... _Promise>
+ requires __awaitable<_Awaitable, _Promise...>
+using __await_result_t = void;
+
+#endif
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__basic_sender.hpp b/include/sdbusplus/async/stdexec/__detail/__basic_sender.hpp
index 843c5e4..3228fa2 100644
--- a/include/sdbusplus/async/stdexec/__detail/__basic_sender.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__basic_sender.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
@@ -15,12 +15,13 @@
*/
#pragma once
-#include "../concepts.hpp"
#include "__concepts.hpp"
-#include "__config.hpp"
+#include "__diagnostics.hpp"
#include "__env.hpp"
#include "__execution_fwd.hpp"
#include "__meta.hpp"
+#include "__sender_introspection.hpp"
+#include "__senders_core.hpp"
#include "__tuple.hpp"
#include "__type_traits.hpp"
@@ -37,116 +38,40 @@
template <class _Sender>
using __impl_of = decltype((__declval<_Sender>().__impl_));
-struct __get_tag
-{
- template <class _Tag, class... _Rest>
- STDEXEC_ATTRIBUTE((always_inline))
- _Tag operator()(_Tag, _Rest&&...) const noexcept
- {
- return {};
- }
-};
-
struct __get_data
{
- template <class _Data, class... _Rest>
+ template <class _Data>
STDEXEC_ATTRIBUTE((always_inline))
- _Data&& operator()(__ignore, _Data&& __data, _Rest&&...) const noexcept
+ _Data&& operator()(__ignore, _Data&& __data, auto&&...) const noexcept
{
return static_cast<_Data&&>(__data);
}
};
-
-template <class _Continuation>
-struct __get_children
-{
- template <class... _Child>
- STDEXEC_ATTRIBUTE((always_inline))
- auto operator()(__ignore, __ignore, _Child&&...) const noexcept
- -> __mtype<__minvoke<_Continuation, _Child...>> (*)()
- {
- return nullptr;
- }
-};
-
-template <class _Tag, class _Data, class... _Child>
-struct __desc
-{
- using __tag = _Tag;
- using __data = _Data;
- using __children = __types<_Child...>;
-
- template <class _Fn>
- using __f = __minvoke<_Fn, _Tag, _Data, _Child...>;
-};
-
-template <class _Fn>
-struct __sexpr_uncurry_fn
-{
- template <class _Tag, class _Data, class... _Child>
- requires __minvocable<_Fn, _Tag, _Data, _Child...>
- constexpr auto operator()(_Tag, _Data&&, _Child&&...) const noexcept
- -> __minvoke<_Fn, _Tag, _Data, _Child...>;
-};
-
-template <class _Sender, class _Fn>
-using __sexpr_uncurry =
- __call_result_t<__impl_of<_Sender>, __copy_cvref_fn<_Sender>,
- __sexpr_uncurry_fn<_Fn>>;
-
-template <class _Sender>
-using __desc_of = __sexpr_uncurry<_Sender, __q<__desc>>;
-
-using __get_desc = __sexpr_uncurry_fn<__q<__desc>>;
-
-template <class _Sender>
-extern __q<__midentity> __name_of_v;
-
-template <class _Sender>
-using __name_of_fn = decltype(__name_of_v<_Sender>);
-
-template <class _Sender>
-using __name_of = __minvoke<__name_of_fn<_Sender>, _Sender>;
} // namespace __detail
-template <class _Sender>
-using tag_of_t = typename __detail::__desc_of<_Sender>::__tag;
-
-template <class _Sender>
-using __data_of = typename __detail::__desc_of<_Sender>::__data;
-
-template <class _Sender, class _Continuation = __q<__types>>
-using __children_of = //
- __mapply<_Continuation, typename __detail::__desc_of<_Sender>::__children>;
-
-template <class _Ny, class _Sender>
-using __nth_child_of = __children_of<_Sender, __mbind_front_q<__m_at, _Ny>>;
-
-template <std::size_t _Ny, class _Sender>
-using __nth_child_of_c =
- __children_of<_Sender, __mbind_front_q<__m_at, __msize_t<_Ny>>>;
-
-template <class _Sender>
-using __child_of = __children_of<_Sender, __q<__mfront>>;
-
-template <class _Sender>
-inline constexpr std::size_t __nbr_children_of =
- __v<__children_of<_Sender, __msize>>;
-
-template <class _Fn, class _Tp>
- requires __mvalid<tag_of_t, _Tp> &&
- __mvalid<__detail::__sexpr_uncurry, _Tp, _Fn>
-struct __uncurry_<_Fn, _Tp>
+namespace
{
- using __t = __detail::__sexpr_uncurry<_Tp, _Fn>;
-};
+template <class _Descriptor, auto _DescriptorFn = [] { return _Descriptor(); }>
+inline constexpr auto __descriptor_fn_v = _DescriptorFn;
+
+template <class _Tag, class _Data, class... _Child>
+inline constexpr auto __descriptor_fn()
+{
+ return __descriptor_fn_v<__detail::__desc<_Tag, _Data, _Child...>>;
+}
+} // namespace
+
+#if STDEXEC_NVHPC()
+#define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child) \
+ stdexec::__descriptor_fn<_Tag, _Data, _Child>()
+#else
+#define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child) \
+ stdexec::__descriptor_fn_v<stdexec::__detail::__desc<_Tag, _Data, _Child>>
+#endif
template <class _Tag>
struct __sexpr_impl;
-template <class _Sender>
-using __name_of = __detail::__name_of<_Sender>;
-
namespace __detail
{
template <class _Sexpr, class _Receiver>
@@ -159,10 +84,11 @@
using __state_type_t =
__decay_t<__result_of<__sexpr_impl<_Tag>::get_state, _Sexpr, _Receiver&>>;
-template <class _Tag, class _Index, class _Sexpr, class _Receiver>
-using __env_type_t =
- __result_of<__sexpr_impl<_Tag>::get_env, _Index,
- __state_type_t<_Tag, _Sexpr, _Receiver>&, _Receiver&>;
+template <class _Self, class _Tag, class _Index, class _Sexpr, class _Receiver>
+using __env_type_t = __result_of<
+ __sexpr_impl<__meval<__msecond, _Self, _Tag>>::get_env, _Index,
+ __state_type_t<__meval<__msecond, _Self, _Tag>, _Sexpr, _Receiver>&,
+ _Receiver&>;
template <class _Sexpr, class _Receiver>
concept __connectable =
@@ -199,13 +125,14 @@
inline constexpr auto __get_state = //
[]<class _Sender>(_Sender&& __sndr, __ignore) noexcept -> decltype(auto) {
- return STDEXEC_CALL_EXPLICIT_THIS_MEMFN(static_cast<_Sender&&>(__sndr),
- apply)(__get_data());
+ return __sndr.apply(static_cast<_Sender&&>(__sndr), __get_data());
};
-inline constexpr auto __connect = //
- []<class _Sender, class _Receiver>(
- _Sender&& __sndr, _Receiver __rcvr) -> __op_state<_Sender, _Receiver>
+inline constexpr auto __connect = //
+ []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver __rcvr) //
+ noexcept(__nothrow_constructible_from<__op_state<_Sender, _Receiver>,
+ _Sender, _Receiver>)
+ -> __op_state<_Sender, _Receiver>
requires __connectable<_Sender, _Receiver>
{
return __op_state<_Sender, _Receiver>{static_cast<_Sender&&>(__sndr),
@@ -228,8 +155,12 @@
_SetTag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
};
-inline constexpr auto __get_completion_signatures = //
- [](__ignore, __ignore) noexcept { return void(); };
+inline constexpr auto __sigs = //
+ []<class _Sender>(_Sender&& __sndr, __ignore = {}) noexcept {
+ static_assert(
+ __mnever<tag_of_t<_Sender>>,
+ "No customization of get_completion_signatures for this sender tag type.");
+};
template <class _ReceiverId, class _Sexpr, class _Idx>
struct __receiver
@@ -258,29 +189,43 @@
// (static_cast<char*>(__child) - __offset); return __t{__parent};
// }
- template <__completion_tag _Tag, class... _Args>
+ template <class... _Args>
STDEXEC_ATTRIBUTE((always_inline))
- friend void tag_invoke(_Tag, __t&& __self, _Args&&... __args) noexcept
+ void set_value(_Args&&... __args) noexcept
{
- __self.__op_->__complete(_Idx(), _Tag(),
- static_cast<_Args&&>(__args)...);
+ __op_->__complete(_Idx(), stdexec::set_value,
+ static_cast<_Args&&>(__args)...);
}
- template <same_as<get_env_t> _Tag, class _SexprTag = __tag_t>
+ template <class _Error>
STDEXEC_ATTRIBUTE((always_inline))
- friend auto tag_invoke(_Tag, const __t& __self) noexcept
- -> __env_type_t<_SexprTag, _Idx, _Sexpr, _Receiver>
+ void set_error(_Error&& __err) noexcept
{
- return __self.__op_->__get_env(_Idx());
+ __op_->__complete(_Idx(), stdexec::set_error,
+ static_cast<_Error&&>(__err));
+ }
+
+ STDEXEC_ATTRIBUTE((always_inline))
+ void set_stopped() noexcept
+ {
+ __op_->__complete(_Idx(), stdexec::set_stopped);
+ }
+
+ template <__same_as<__t> _Self = __t>
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto get_env() const noexcept
+ -> __env_type_t<_Self, __tag_t, _Idx, _Sexpr, _Receiver>
+ {
+ return __op_->__get_env(_Idx());
}
};
};
-template <class _Receiver>
-using __sexpr_connected_with =
- __mapply<__mbind_front_q<__m_at, typename _Receiver::__index>,
- typename __call_result_t<__impl_of<typename _Receiver::__sexpr>,
- __cp, __get_desc>::__children>;
+// template <class _Receiver>
+// using __sexpr_connected_with = __mapply<
+// __mbind_front_q<__m_at, typename _Receiver::__index>,
+// typename __call_result_t<__impl_of<typename _Receiver::__sexpr>, __cp,
+// __get_desc>::__children>;
template <class _Sexpr, class _Receiver>
struct __op_base : __immovable
@@ -291,7 +236,9 @@
STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Receiver __rcvr_;
STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS __state_t __state_;
- __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) :
+ __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) //
+ noexcept(__nothrow_decay_copyable<_Receiver> &&
+ __nothrow_move_constructible<__state_t>) :
__rcvr_(static_cast<_Receiver&&>(__rcvr)),
__state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr),
__rcvr_))
@@ -301,6 +248,11 @@
{
return __rcvr_;
}
+
+ auto __rcvr() const& noexcept -> const _Receiver&
+ {
+ return __rcvr_;
+ }
};
// template <class _Sexpr, class _Receiver>
@@ -355,7 +307,7 @@
{
template <std::size_t _Idx>
using __receiver_t =
- __t<__receiver<__id<_Receiver>, _Sexpr, __mconstant<_Idx>>>;
+ __t<__receiver<__id<_Receiver>, _Sexpr, __msize_t<_Idx>>>;
__op_state<_Sexpr, _Receiver>* __op_;
@@ -363,26 +315,29 @@
{
__op_state<_Sexpr, _Receiver>* __op_;
- template <std::size_t... _Is, class _Tag, class _Data, class... _Child>
- auto operator()(__indices<_Is...>, _Tag, _Data&&,
- _Child&&... __child) const
- -> __tup::__tuple<__indices<_Is...>,
- connect_result_t<_Child, __receiver_t<_Is>>...>
+ template <std::size_t... _Is, class... _Child>
+ auto operator()(__indices<_Is...>, _Child&&... __child) const
+ noexcept((__nothrow_connectable<_Child, __receiver_t<_Is>> && ...))
+ -> __tuple_for<connect_result_t<_Child, __receiver_t<_Is>>...>
{
return __tuple{connect(static_cast<_Child&&>(__child),
__receiver_t<_Is>{__op_})...};
}
};
- template <class _Tag, class _Data, class... _Child>
- auto operator()(_Tag, _Data&& __data, _Child&&... __child) const
- -> __call_result_t<__impl, __indices_for<_Child...>, _Tag, _Data,
- _Child...>
+ template <class... _Child>
+ auto operator()(__ignore, __ignore, _Child&&... __child) const noexcept(
+ __nothrow_callable<__impl, __indices_for<_Child...>, _Child...>)
+ -> __call_result_t<__impl, __indices_for<_Child...>, _Child...>
{
- return __impl{__op_}(__indices_for<_Child...>(), _Tag(),
- static_cast<_Data&&>(__data),
+ return __impl{__op_}(__indices_for<_Child...>(),
static_cast<_Child&&>(__child)...);
}
+
+ auto operator()(__ignore, __ignore) const noexcept -> __tuple_for<>
+ {
+ return {};
+ }
};
STDEXEC_PRAGMA_POP()
@@ -392,7 +347,7 @@
using __desc_t = typename __decay_t<_Sexpr>::__desc_t;
using __tag_t = typename __desc_t::__tag;
using __data_t = typename __desc_t::__data;
- using __children_t = typename __desc_t::__children;
+ // using __children_t = typename __desc_t::__children;
using __state_t = typename __op_state::__state_t;
using __inner_ops_t =
__result_of<__sexpr_apply, _Sexpr, __connect_fn<_Sexpr, _Receiver>>;
@@ -402,29 +357,31 @@
// template <std::size_t _Idx>
// static std::ptrdiff_t __get_child_op_offset() noexcept {
// __op_state* __self = (__op_state*) &__self;
- // return (std::ptrdiff_t)((char*)
- // &__tup::__get<_Idx>(__self->__inner_ops_) -
- // static_cast<char*>(__self));
+ // return (std::ptrdiff_t)((char*) &__tup::get<_Idx>(__self->__inner_ops_)
+ // - static_cast<char*>(__self));
// }
- __op_state(_Sexpr&& __sexpr, _Receiver __rcvr) :
+ __op_state(_Sexpr&& __sexpr, _Receiver __rcvr) //
+ noexcept(__nothrow_constructible_from<__op_base<_Sexpr, _Receiver>,
+ _Sexpr&&, _Receiver&&> &&
+ __nothrow_callable<__sexpr_apply_t, _Sexpr&&,
+ __connect_fn<_Sexpr, _Receiver>>) :
__op_state::__op_base{static_cast<_Sexpr&&>(__sexpr),
static_cast<_Receiver&&>(__rcvr)},
__inner_ops_(__sexpr_apply(static_cast<_Sexpr&&>(__sexpr),
__connect_fn<_Sexpr, _Receiver>{this}))
{}
- template <same_as<start_t> _Tag2>
STDEXEC_ATTRIBUTE((always_inline))
- friend void tag_invoke(_Tag2, __op_state& __self) noexcept
+ void start() & noexcept
{
using __tag_t = typename __op_state::__tag_t;
- auto&& __rcvr = __self.__rcvr();
- __tup::__apply(
+ auto&& __rcvr = this->__rcvr();
+ __inner_ops_.apply(
[&](auto&... __ops) noexcept {
- __sexpr_impl<__tag_t>::start(__self.__state_, __rcvr, __ops...);
+ __sexpr_impl<__tag_t>::start(this->__state_, __rcvr, __ops...);
},
- __self.__inner_ops_);
+ __inner_ops_);
}
template <class _Index, class _Tag2, class... _Args>
@@ -433,15 +390,25 @@
{
using __tag_t = typename __op_state::__tag_t;
auto&& __rcvr = this->__rcvr();
- __sexpr_impl<__tag_t>::complete(_Index(), this->__state_, __rcvr,
- _Tag2(),
- static_cast<_Args&&>(__args)...);
+ using _CompleteFn = __mtypeof<__sexpr_impl<__tag_t>::complete>;
+ if constexpr (__callable<_CompleteFn, _Index, __op_state&, _Tag2,
+ _Args...>)
+ {
+ __sexpr_impl<__tag_t>::complete(_Index(), *this, _Tag2(),
+ static_cast<_Args&&>(__args)...);
+ }
+ else
+ {
+ __sexpr_impl<__tag_t>::complete(_Index(), this->__state_, __rcvr,
+ _Tag2(),
+ static_cast<_Args&&>(__args)...);
+ }
}
template <class _Index>
STDEXEC_ATTRIBUTE((always_inline))
- auto __get_env(_Index) noexcept
- -> __env_type_t<__tag_t, _Index, _Sexpr, _Receiver>
+ auto __get_env(_Index) const noexcept
+ -> __env_type_t<_Index, __tag_t, _Index, _Sexpr, _Receiver>
{
const auto& __rcvr = this->__rcvr();
return __sexpr_impl<__tag_t>::get_env(_Index(), this->__state_, __rcvr);
@@ -451,9 +418,8 @@
inline constexpr auto __drop_front = //
[]<class _Fn>(_Fn __fn) noexcept {
return
- [__fn = std::move(__fn)]<class... _Rest>(
- auto&&, _Rest&&... __rest) noexcept(__nothrow_callable<const _Fn&,
- _Rest...>)
+ [__fn = std::move(__fn)]<class... _Rest>(auto&&, _Rest&&... __rest) //
+ noexcept(__nothrow_callable<const _Fn&, _Rest...>)
-> __call_result_t<const _Fn&, _Rest...> {
return __fn(static_cast<_Rest&&>(__rest)...);
};
@@ -480,19 +446,6 @@
};
}
-template <class _Sender>
-concept __non_dependent_sender = //
- requires { typename _Sender::completion_signatures; } ||
- requires { requires _Sender::__is_non_dependent(); };
-
-template <class _Tag, class... _Child>
-concept __is_non_dependent_sexpr = //
- !requires { typename __sexpr_impl<_Tag>::is_dependent; } &&
- (__non_dependent_sender<_Child> && ...);
-
-template <class _Tag, class _Data, class... _Child>
-using __is_non_dependent_t = __mbool<__is_non_dependent_sexpr<_Tag, _Child...>>;
-
template <class _Tag, class _Data, class... _Child>
using __captures_t = decltype(__detail::__captures(_Tag(), __declval<_Data>(),
__declval<_Child>()...));
@@ -514,13 +467,14 @@
static constexpr auto connect = __detail::__connect;
static constexpr auto start = __detail::__start;
static constexpr auto complete = __detail::__complete;
- static constexpr auto get_completion_signatures =
- __detail::__get_completion_signatures;
+ static constexpr auto get_completion_signatures = __detail::__sigs;
};
template <class _Tag>
struct __sexpr_impl : __sexpr_defaults
-{};
+{
+ using not_specialized = void;
+};
using __detail::__enable_receiver_from_this;
@@ -548,11 +502,6 @@
using __tag_t = typename __desc_t::__tag;
using __captures_t = __minvoke<__desc_t, __q<__detail::__captures_t>>;
- static constexpr auto __is_non_dependent() noexcept -> bool
- {
- return __v<__minvoke<__desc_t, __q<__detail::__is_non_dependent_t>>>;
- }
-
mutable __captures_t __impl_;
template <class _Tag, class _Data, class... _Child>
@@ -562,55 +511,45 @@
static_cast<_Child&&>(__child)...))
{}
- template <class _Tag>
- using __impl = __sexpr_impl<__meval<__msecond, _Tag, __tag_t>>;
+ template <class _Self>
+ using __impl = __sexpr_impl<__meval<__msecond, _Self, __tag_t>>;
- template <same_as<get_env_t> _Tag, same_as<__sexpr> _Self>
+ template <class _Self = __sexpr>
STDEXEC_ATTRIBUTE((always_inline))
- friend auto tag_invoke(_Tag, const _Self& __self) noexcept //
- -> __msecond<
- __if_c<same_as<_Tag, get_env_t> && same_as<_Self, __sexpr>>, //
- __result_of<__sexpr_apply, const _Self&, __get_attrs_fn<__tag_t>>>
+ auto get_env() const noexcept
+ -> __result_of<__sexpr_apply, const _Self&, __get_attrs_fn<__tag_t>>
{
- return __sexpr_apply(__self,
- __detail::__drop_front(__impl<_Tag>::get_attrs));
+ return __sexpr_apply(*this,
+ __detail::__drop_front(__impl<_Self>::get_attrs));
}
- template <same_as<get_completion_signatures_t> _Tag,
- __decays_to<__sexpr> _Self, class _Env>
+ template <__decays_to<__sexpr> _Self, class... _Env>
STDEXEC_ATTRIBUTE((always_inline))
- friend auto tag_invoke(_Tag, _Self&& __self, _Env&& __env) noexcept //
- -> __msecond<
- __if_c<same_as<_Tag, get_completion_signatures_t> &&
- __decays_to<_Self, __sexpr>>,
- __result_of<__impl<_Tag>::get_completion_signatures, _Self, _Env>>
+ static auto get_completion_signatures(_Self&&, _Env&&...) noexcept //
+ -> __msecond<__if_c<__decays_to<_Self, __sexpr>>,
+ __result_of<__impl<_Self>::get_completion_signatures,
+ _Self, _Env...>>
{
return {};
}
// BUGBUG fix receiver constraint here:
- template <same_as<connect_t> _Tag, __decays_to<__sexpr> _Self,
- /*receiver*/ class _Receiver>
+ template <__decays_to<__sexpr> _Self, /*receiver*/ class _Receiver>
STDEXEC_ATTRIBUTE((always_inline))
- friend auto tag_invoke(_Tag, _Self&& __self, _Receiver&& __rcvr) //
- noexcept(noexcept(__impl<_Tag>::connect(
- static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr)))) //
- -> __msecond<
- __if_c<same_as<_Tag, connect_t> && __decays_to<_Self, __sexpr>>,
- __result_of<__impl<_Tag>::connect, _Self, _Receiver>>
+ static auto connect(_Self&& __self, _Receiver&& __rcvr) //
+ noexcept(__noexcept_of<__impl<_Self>::connect, _Self, _Receiver>) //
+ -> __msecond<__if_c<__decays_to<_Self, __sexpr>>,
+ __result_of<__impl<_Self>::connect, _Self, _Receiver>>
{
- return __impl<_Tag>::connect(static_cast<_Self&&>(__self),
- static_cast<_Receiver&&>(__rcvr));
+ return __impl<_Self>::connect(static_cast<_Self&&>(__self),
+ static_cast<_Receiver&&>(__rcvr));
}
template <class _Sender, class _ApplyFn>
STDEXEC_ATTRIBUTE((always_inline))
- STDEXEC_DEFINE_EXPLICIT_THIS_MEMFN(auto apply)(
- this _Sender&& __sndr,
- _ApplyFn&&
- __fun) noexcept(__nothrow_callable<__detail::__impl_of<_Sender>,
- __copy_cvref_fn<_Sender>,
- _ApplyFn>) //
+ static auto apply(_Sender&& __sndr, _ApplyFn&& __fun) //
+ noexcept(__nothrow_callable<__detail::__impl_of<_Sender>,
+ __copy_cvref_fn<_Sender>, _ApplyFn>) //
-> __call_result_t<__detail::__impl_of<_Sender>,
__copy_cvref_fn<_Sender>, _ApplyFn>
{ //
@@ -620,7 +559,7 @@
template <std::size_t _Idx, __decays_to_derived_from<__sexpr> _Self>
STDEXEC_ATTRIBUTE((always_inline))
- friend decltype(auto) get(_Self&& __self) noexcept
+ friend auto get(_Self&& __self) noexcept -> decltype(auto)
requires __detail::__in_range<_Idx, __desc_t>
{
if constexpr (_Idx == 0)
@@ -635,26 +574,6 @@
}
};
-namespace
-{
-template <class _Descriptor, auto _DescriptorFn = [] { return _Descriptor(); }>
-inline constexpr auto __descriptor_fn_v = _DescriptorFn;
-
-template <class _Tag, class _Data, class... _Child>
-inline constexpr auto __descriptor_fn()
-{
- return __descriptor_fn_v<__detail::__desc<_Tag, _Data, _Child...>>;
-}
-} // namespace
-
-#if STDEXEC_NVHPC()
-#define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child) \
- stdexec::__descriptor_fn<_Tag, _Data, _Child>()
-#else
-#define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child) \
- stdexec::__descriptor_fn_v<stdexec::__detail::__desc<_Tag, _Data, _Child>>
-#endif
-
template <class _Tag, class _Data, class... _Child>
STDEXEC_ATTRIBUTE((host, device))
__sexpr(_Tag, _Data, _Child...)
@@ -683,42 +602,6 @@
template <class _Tag>
inline constexpr __detail::__make_sexpr_t<_Tag> __make_sexpr{};
-namespace __detail
-{
-struct __sexpr_apply_t
-{
- template <class _Sender, class _ApplyFn>
- STDEXEC_ATTRIBUTE((always_inline))
- auto operator()(_Sender&& __sndr, _ApplyFn&& __fun) const
- noexcept(noexcept(STDEXEC_CALL_EXPLICIT_THIS_MEMFN(
- (static_cast<_Sender&&>(__sndr)),
- apply)(static_cast<_ApplyFn&&>(__fun)))) //
- -> decltype(STDEXEC_CALL_EXPLICIT_THIS_MEMFN(
- (static_cast<_Sender&&>(__sndr)),
- apply)(static_cast<_ApplyFn&&>(__fun)))
- {
- return STDEXEC_CALL_EXPLICIT_THIS_MEMFN(
- (static_cast<_Sender&&>(__sndr)),
- apply)(static_cast<_ApplyFn&&>(__fun)); //
- }
-};
-} // namespace __detail
-
-using __detail::__sexpr_apply_t;
-inline constexpr __sexpr_apply_t __sexpr_apply{};
-
-template <class _Sender, class _ApplyFn>
-using __sexpr_apply_result_t =
- __call_result_t<__sexpr_apply_t, _Sender, _ApplyFn>;
-
-template <class _Sender>
-concept sender_expr = //
- __mvalid<tag_of_t, _Sender>;
-
-template <class _Sender, class _Tag>
-concept sender_expr_for = //
- sender_expr<_Sender> && same_as<tag_of_t<_Sender>, _Tag>;
-
// The __name_of utility defined below is used to pretty-print the type names of
// senders in compiler diagnostics.
namespace __detail
diff --git a/include/sdbusplus/async/stdexec/__detail/__bulk.hpp b/include/sdbusplus/async/stdexec/__detail/__bulk.hpp
new file mode 100644
index 0000000..1878534
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__bulk.hpp
@@ -0,0 +1,182 @@
+/*
+ * 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 "__diagnostics.hpp"
+#include "__domain.hpp"
+#include "__meta.hpp"
+#include "__sender_adaptor_closure.hpp"
+#include "__senders.hpp"
+#include "__senders_core.hpp"
+#include "__transform_completion_signatures.hpp"
+#include "__transform_sender.hpp"
+
+STDEXEC_PRAGMA_PUSH()
+STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.bulk]
+namespace __bulk
+{
+inline constexpr __mstring __bulk_context =
+ "In stdexec::bulk(Sender, Shape, Function)..."_mstr;
+using __on_not_callable = __callable_error<__bulk_context>;
+
+template <class _Shape, class _Fun>
+struct __data
+{
+ _Shape __shape_;
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _Fun __fun_;
+ static constexpr auto __mbrs_ =
+ __mliterals<&__data::__shape_, &__data::__fun_>();
+};
+template <class _Shape, class _Fun>
+__data(_Shape, _Fun) -> __data<_Shape, _Fun>;
+
+template <class _Ty>
+using __decay_ref = __decay_t<_Ty>&;
+
+template <class _Catch, class _Fun, class _Shape, class _CvrefSender,
+ class... _Env>
+using __with_error_invoke_t = //
+ __if<__value_types_t<
+ __completion_signatures_of_t<_CvrefSender, _Env...>,
+ __mtransform<
+ __q<__decay_ref>,
+ __mbind_front<__mtry_catch_q<__nothrow_invocable_t, _Catch>,
+ _Fun, _Shape>>,
+ __q<__mand>>,
+ completion_signatures<>, __eptr_completion>;
+
+template <class _Fun, class _Shape, class _CvrefSender, class... _Env>
+using __completion_signatures = //
+ transform_completion_signatures<
+ __completion_signatures_of_t<_CvrefSender, _Env...>,
+ __with_error_invoke_t<__on_not_callable, _Fun, _Shape, _CvrefSender,
+ _Env...>>;
+
+struct bulk_t
+{
+ template <sender _Sender, integral _Shape, __movable_value _Fun>
+ STDEXEC_ATTRIBUTE((host, device))
+ auto operator()(_Sender&& __sndr, _Shape __shape, _Fun __fun) const
+ -> __well_formed_sender auto
+ {
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<bulk_t>(__data{__shape, static_cast<_Fun&&>(__fun)},
+ static_cast<_Sender&&>(__sndr)));
+ }
+
+ template <integral _Shape, class _Fun>
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto operator()(_Shape __shape, _Fun __fun) const
+ -> __binder_back<bulk_t, _Shape, _Fun>
+ {
+ return {{static_cast<_Shape&&>(__shape), static_cast<_Fun&&>(__fun)},
+ {},
+ {}};
+ }
+
+ // This describes how to use the pieces of a bulk sender to find
+ // legacy customizations of the bulk algorithm.
+ using _Sender = __1;
+ using _Shape = __nth_member<0>(__0);
+ using _Fun = __nth_member<1>(__0);
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(bulk_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(_Sender&)),
+ _Sender, _Shape, _Fun),
+ tag_invoke_t(bulk_t, _Sender, _Shape, _Fun)>;
+};
+
+struct __bulk_impl : __sexpr_defaults
+{
+ template <class _Sender>
+ using __fun_t = decltype(__decay_t<__data_of<_Sender>>::__fun_);
+
+ template <class _Sender>
+ using __shape_t = decltype(__decay_t<__data_of<_Sender>>::__shape_);
+
+ static constexpr auto get_completion_signatures = //
+ []<class _Sender, class... _Env>(_Sender&&, _Env&&...) noexcept
+ -> __completion_signatures<__fun_t<_Sender>, __shape_t<_Sender>,
+ __child_of<_Sender>, _Env...> {
+ static_assert(sender_expr_for<_Sender, bulk_t>);
+ return {};
+ };
+
+ 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>)
+ {
+ using __shape_t = decltype(__state.__shape_);
+ if constexpr (noexcept(__state.__fun_(__shape_t{}, __args...)))
+ {
+ for (__shape_t __i{}; __i != __state.__shape_; ++__i)
+ {
+ __state.__fun_(__i, __args...);
+ }
+ _Tag()(static_cast<_Receiver&&>(__rcvr),
+ static_cast<_Args&&>(__args)...);
+ }
+ else
+ {
+ try
+ {
+ for (__shape_t __i{}; __i != __state.__shape_; ++__i)
+ {
+ __state.__fun_(__i, __args...);
+ }
+ _Tag()(static_cast<_Receiver&&>(__rcvr),
+ static_cast<_Args&&>(__args)...);
+ }
+ catch (...)
+ {
+ stdexec::set_error(static_cast<_Receiver&&>(__rcvr),
+ std::current_exception());
+ }
+ }
+ }
+ else
+ {
+ _Tag()(static_cast<_Receiver&&>(__rcvr),
+ static_cast<_Args&&>(__args)...);
+ }
+ };
+};
+} // namespace __bulk
+
+using __bulk::bulk_t;
+inline constexpr bulk_t bulk{};
+
+template <>
+struct __sexpr_impl<bulk_t> : __bulk::__bulk_impl
+{};
+} // namespace stdexec
+
+STDEXEC_PRAGMA_POP()
diff --git a/include/sdbusplus/async/stdexec/__detail/__completion_signatures.hpp b/include/sdbusplus/async/stdexec/__detail/__completion_signatures.hpp
new file mode 100644
index 0000000..dd16858
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__completion_signatures.hpp
@@ -0,0 +1,58 @@
+/*
+ * 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 "__meta.hpp"
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// completion_signatures
+namespace __sigs
+{
+template <class... _Args>
+inline constexpr bool __is_compl_sig<set_value_t(_Args...)> = true;
+template <class _Error>
+inline constexpr bool __is_compl_sig<set_error_t(_Error)> = true;
+template <>
+inline constexpr bool __is_compl_sig<set_stopped_t()> = true;
+
+template <class>
+inline constexpr bool __is_completion_signatures = false;
+template <class... _Sigs>
+inline constexpr bool
+ __is_completion_signatures<completion_signatures<_Sigs...>> = true;
+} // namespace __sigs
+
+template <class... _Sigs>
+struct completion_signatures
+{};
+
+template <class _Completions>
+concept __valid_completion_signatures = //
+ __same_as<__ok_t<_Completions>, __msuccess> &&
+ __sigs::__is_completion_signatures<_Completions>;
+
+template <class _Sender, class _Env>
+using __unrecognized_sender_error = //
+ __mexception<_UNRECOGNIZED_SENDER_TYPE_<>, _WITH_SENDER_<_Sender>,
+ _WITH_ENVIRONMENT_<_Env>>;
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__concepts.hpp b/include/sdbusplus/async/stdexec/__detail/__concepts.hpp
index a3187a7..667dcb4 100644
--- a/include/sdbusplus/async/stdexec/__detail/__concepts.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__concepts.hpp
@@ -15,12 +15,30 @@
*/
#pragma once
+#if __cpp_concepts < 201907L
+#error This library requires support for C++20 concepts
+#endif
+
#include "__config.hpp"
#include "__type_traits.hpp"
+#include <version>
+
+// Perhaps the stdlib lacks support for concepts though:
+#if __has_include(<concepts>) && __cpp_lib_concepts >= 202002
+#define STDEXEC_HAS_STD_CONCEPTS_HEADER() 1
+#else
+#define STDEXEC_HAS_STD_CONCEPTS_HEADER() 0
+#endif
+
+#if STDEXEC_HAS_STD_CONCEPTS_HEADER()
+#include <concepts>
+#else
+#include <type_traits>
+#endif
+
namespace stdexec
{
-
//////////////////////////////////////////////////////////////////////////////////////////////////
template <class _Fun, class... _As>
concept __callable = //
@@ -44,28 +62,8 @@
concept __typename = requires { typename __types<_Ts...>; };
//////////////////////////////////////////////////////////////////////////////////////////////////
-#if STDEXEC_CLANG()
-
template <class _Ap, class _Bp>
-concept __same_as = __is_same(_Ap, _Bp);
-
-#elif STDEXEC_GCC()
-
-template <class _Ap, class _Bp>
-concept __same_as = __is_same_as(_Ap, _Bp);
-
-#else
-
-template <class _Ap, class _Bp>
-inline constexpr bool __same_as_v = false;
-
-template <class _Ap>
-inline constexpr bool __same_as_v<_Ap, _Ap> = true;
-
-template <class _Ap, class _Bp>
-concept __same_as = __same_as_v<_Ap, _Bp>;
-
-#endif
+concept __same_as = STDEXEC_IS_SAME(_Ap, _Bp);
// Handy concepts
template <class _Ty, class _Up>
@@ -102,5 +100,229 @@
template <class _Ay, template <class...> class _Ty>
concept __is_not_instance_of = !__is_instance_of<_Ay, _Ty>;
+} // namespace stdexec
+namespace stdexec::__std_concepts
+{
+// Make sure we're using a same_as concept that doesn't instantiate std::is_same
+template <class _Ap, class _Bp>
+concept same_as = __same_as<_Ap, _Bp> && __same_as<_Bp, _Ap>;
+
+#if STDEXEC_HAS_STD_CONCEPTS_HEADER()
+
+using std::convertible_to;
+using std::derived_from;
+using std::equality_comparable;
+using std::integral;
+
+#else
+
+template <class T>
+concept integral = std::is_integral_v<T>;
+
+template <class _Ap, class _Bp>
+concept derived_from = //
+ STDEXEC_IS_BASE_OF(_Bp, _Ap) && //
+ STDEXEC_IS_CONVERTIBLE_TO(const volatile _Ap*, const volatile _Bp*);
+
+template <class _From, class _To>
+concept convertible_to = //
+ STDEXEC_IS_CONVERTIBLE_TO(_From, _To) && //
+ requires(_From (&__fun)()) { static_cast<_To>(__fun()); };
+
+template <class _Ty>
+concept equality_comparable = //
+ requires(__cref_t<_Ty> __t) {
+ {
+ __t == __t
+ } -> convertible_to<bool>;
+ {
+ __t != __t
+ } -> convertible_to<bool>;
+ };
+#endif
+} // namespace stdexec::__std_concepts
+
+namespace stdexec
+{
+using namespace __std_concepts;
+
+// Avoid using libstdc++'s object concepts because they instantiate a
+// lot of templates.
+#if STDEXEC_HAS_BUILTIN(__is_nothrow_destructible) || STDEXEC_MSVC()
+template <class _Ty>
+concept destructible = __is_nothrow_destructible(_Ty);
+#else
+template <class _Ty>
+inline constexpr bool __destructible_ = //
+ requires(_Ty && (&__fn)() noexcept) {
+ {
+ __fn().~_Ty()
+ } noexcept;
+ };
+template <class _Ty>
+inline constexpr bool __destructible_<_Ty&> = true;
+template <class _Ty>
+inline constexpr bool __destructible_<_Ty&&> = true;
+template <class _Ty, std::size_t _Np>
+inline constexpr bool __destructible_<_Ty[_Np]> = __destructible_<_Ty>;
+
+template <class T>
+concept destructible = __destructible_<T>;
+#endif
+
+template <class _Ty, class... _As>
+concept constructible_from = //
+ destructible<_Ty> && //
+ STDEXEC_IS_CONSTRUCTIBLE(_Ty, _As...);
+
+template <class _Ty>
+concept default_initializable = //
+ constructible_from<_Ty> && //
+ requires { _Ty{}; } && //
+ requires { ::new _Ty; };
+
+template <class _Ty>
+concept move_constructible = //
+ constructible_from<_Ty, _Ty>;
+
+template <class _Ty>
+concept copy_constructible = //
+ move_constructible<_Ty> //
+ && constructible_from<_Ty, const _Ty&>;
+
+template <class _LHS, class _RHS>
+concept assignable_from = //
+ same_as<_LHS, _LHS&> && //
+ // std::common_reference_with<
+ // const std::remove_reference_t<_LHS>&,
+ // const std::remove_reference_t<_RHS>&> &&
+ requires(_LHS __lhs, _RHS&& __rhs) {
+ {
+ __lhs = static_cast<_RHS&&>(__rhs)
+ } -> same_as<_LHS>;
+ };
+
+namespace __swap
+{
+using std::swap;
+
+template <class _Ty, class _Uy>
+concept swappable_with = //
+ requires(_Ty&& __t, _Uy&& __u) { //
+ swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u));
+ };
+
+inline constexpr const auto __fn = //
+ []<class _Ty, swappable_with<_Ty> _Uy>(_Ty&& __t, _Uy&& __u) //
+ noexcept(noexcept(swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u)))) {
+ swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u));
+};
+} // namespace __swap
+
+using __swap::swappable_with;
+inline constexpr const auto& swap = __swap::__fn;
+
+template <class _Ty>
+concept swappable = //
+ requires(_Ty& a, _Ty& b) { swap(a, b); };
+
+template <class _Ty>
+concept movable = //
+ std::is_object_v<_Ty> && //
+ move_constructible<_Ty> && //
+ assignable_from<_Ty&, _Ty> && //
+ swappable<_Ty>;
+
+template <class _Ty>
+concept copyable = //
+ copy_constructible<_Ty> && //
+ movable<_Ty> && //
+ assignable_from<_Ty&, _Ty&> && //
+ assignable_from<_Ty&, const _Ty&> && //
+ assignable_from<_Ty&, const _Ty>;
+
+template <class _Ty>
+concept semiregular = //
+ copyable<_Ty> && //
+ default_initializable<_Ty>;
+
+template <class _Ty>
+concept regular = //
+ semiregular<_Ty> && //
+ equality_comparable<_Ty>;
+
+// Not exactly right, but close.
+template <class _Ty>
+concept __boolean_testable_ = convertible_to<_Ty, bool>;
+
+template <class T, class U>
+concept __partially_ordered_with = //
+ requires(__cref_t<T> t, __cref_t<U> u) {
+ {
+ t < u
+ } -> __boolean_testable_;
+ {
+ t > u
+ } -> __boolean_testable_;
+ {
+ t <= u
+ } -> __boolean_testable_;
+ {
+ t >= u
+ } -> __boolean_testable_;
+ {
+ u < t
+ } -> __boolean_testable_;
+ {
+ u > t
+ } -> __boolean_testable_;
+ {
+ u <= t
+ } -> __boolean_testable_;
+ {
+ u >= t
+ } -> __boolean_testable_;
+ };
+
+template <class _Ty>
+concept totally_ordered = //
+ equality_comparable<_Ty> && //
+ __partially_ordered_with<_Ty, _Ty>;
+
+template <class _Ty>
+concept __movable_value = //
+ move_constructible<__decay_t<_Ty>> && //
+ constructible_from<__decay_t<_Ty>, _Ty>;
+
+template <class _Ty>
+concept __nothrow_movable_value = //
+ __movable_value<_Ty> && //
+ requires(_Ty&& __t) {
+ {
+ __decay_t<_Ty>{__decay_t<_Ty>{static_cast<_Ty&&>(__t)}}
+ } noexcept;
+ };
+
+template <class _Ty, class... _As>
+concept __nothrow_constructible_from =
+ constructible_from<_Ty, _As...> &&
+ STDEXEC_IS_NOTHROW_CONSTRUCTIBLE(_Ty, _As...);
+
+template <class _Ty>
+concept __nothrow_move_constructible = __nothrow_constructible_from<_Ty, _Ty>;
+
+template <class _Ty>
+concept __nothrow_copy_constructible =
+ __nothrow_constructible_from<_Ty, const _Ty&>;
+
+template <class... _Ts>
+concept __decay_copyable = (constructible_from<__decay_t<_Ts>, _Ts> && ...);
+
+template <class... _Ts>
+concept __nothrow_decay_copyable =
+ (__nothrow_constructible_from<__decay_t<_Ts>, _Ts> && ...);
+
+template <class _Ty, class _Up>
+concept __decays_to_derived_from = derived_from<__decay_t<_Ty>, _Up>;
} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__config.hpp b/include/sdbusplus/async/stdexec/__detail/__config.hpp
index 68dfa93..acf6498 100644
--- a/include/sdbusplus/async/stdexec/__detail/__config.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__config.hpp
@@ -16,8 +16,17 @@
#pragma once
#if __cplusplus < 202002L
+#if defined(_MSC_VER) && !defined(__clang__)
+# error This library requires the use of C++20. Use /Zc:__cplusplus to enable __cplusplus conformance.
+#else
#error This library requires the use of C++20.
#endif
+#endif
+
+#if defined(_MSC_VER) && !defined(__clang__) && \
+ (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL)
+# error This library requires the use of the new conforming preprocessor enabled by /Zc:preprocessor.
+#endif
#if __has_include(<version>)
#include <version>
@@ -26,7 +35,7 @@
#endif
#include <cassert>
-#include <version>
+#include <type_traits>
#define STDEXEC_STRINGIZE(_ARG) #_ARG
@@ -37,18 +46,16 @@
#define STDEXEC_EVAL(_MACRO, ...) _MACRO(__VA_ARGS__)
#define STDEXEC_EAT(...)
-#define STDEXEC_NOT(_XP) STDEXEC_CAT(STDEXEC_NOT_, _XP)
-
-enum
-{
- STDEXEC_NOT_0 = 1,
- STDEXEC_NOT_1 = 0
-};
-
+#define STDEXEC_IIF(_XP, _YP, ...) \
+ STDEXEC_IIF_EVAL(STDEXEC_CAT(STDEXEC_IIF_, _XP), _YP, __VA_ARGS__)
#define STDEXEC_IIF_0(_YP, ...) __VA_ARGS__
#define STDEXEC_IIF_1(_YP, ...) _YP
-#define STDEXEC_IIF(_XP, _YP, ...) \
- STDEXEC_EVAL(STDEXEC_CAT(STDEXEC_IIF_, _XP), _YP, __VA_ARGS__)
+#define STDEXEC_IIF_EVAL(_MACRO, ...) _MACRO(__VA_ARGS__)
+
+#define STDEXEC_COMPL(_B) STDEXEC_COMPL_CAT(STDEXEC_COMPL_, _B)
+#define STDEXEC_COMPL_0 1
+#define STDEXEC_COMPL_1 0
+#define STDEXEC_COMPL_CAT(_XP, ...) _XP##__VA_ARGS__
#define STDEXEC_COUNT(...) \
STDEXEC_EXPAND(STDEXEC_COUNT_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1))
@@ -59,6 +66,16 @@
#define STDEXEC_PROBE(...) STDEXEC_PROBE_(__VA_ARGS__, 1)
#define STDEXEC_PROBE_(_XP, _NP, ...) _XP, _NP,
+#define STDEXEC_NOT(_XP) STDEXEC_CHECK(STDEXEC_CAT(STDEXEC_NOT_, _XP))
+#define STDEXEC_NOT_0 STDEXEC_PROBE(~, 1)
+
+#define STDEXEC_BOOL(_XP) STDEXEC_COMPL(STDEXEC_NOT(_XP))
+#define STDEXEC_IF(_XP, _YP, ...) \
+ STDEXEC_IIF(STDEXEC_BOOL(_XP), _YP, __VA_ARGS__)
+
+#define STDEXEC_WHEN(_XP, ...) \
+ STDEXEC_IF(_XP, STDEXEC_EXPAND, STDEXEC_EAT)(__VA_ARGS__)
+
////////////////////////////////////////////////////////////////////////////////
// STDEXEC_FOR_EACH
// Inspired by "Recursive macros with C++20 __VA_OPT__", by David Mazières
@@ -90,6 +107,15 @@
#define STDEXEC_FOR_EACH_AGAIN() STDEXEC_FOR_EACH_HELPER
////////////////////////////////////////////////////////////////////////////////////////////////////
+#define STDEXEC_FRONT(...) __VA_OPT__(STDEXEC_FRONT_HELPER(__VA_ARGS__))
+#define STDEXEC_FRONT_HELPER(_A1, ...) _A1
+#define STDEXEC_BACK(...) \
+ __VA_OPT__(STDEXEC_EXPAND_R(STDEXEC_BACK_HELPER(__VA_ARGS__)))
+#define STDEXEC_BACK_HELPER(_A1, ...) \
+ STDEXEC_FRONT(__VA_OPT__(, ) _A1, ) \
+ __VA_OPT__(STDEXEC_BACK_AGAIN STDEXEC_PARENS(__VA_ARGS__))
+#define STDEXEC_BACK_AGAIN() STDEXEC_BACK_HELPER
+
// If tail is non-empty, expand to the tail. Otherwise, expand to the head
#define STDEXEC_HEAD_OR_TAIL(_XP, ...) \
STDEXEC_EXPAND __VA_OPT__((__VA_ARGS__)STDEXEC_EAT)(_XP)
@@ -113,6 +139,9 @@
#if defined(_MSC_VER)
#define STDEXEC_CLANG_CL(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#endif
+#if defined(__apple_build_version__)
+#define STDEXEC_APPLE_CLANG(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
+#endif
#elif defined(__GNUC__)
#define STDEXEC_GCC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#elif defined(_MSC_VER)
@@ -134,6 +163,9 @@
#ifndef STDEXEC_CLANG_CL
#define STDEXEC_CLANG_CL(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
+#ifndef STDEXEC_APPLE_CLANG
+#define STDEXEC_APPLE_CLANG(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
+#endif
#ifndef STDEXEC_GCC
#define STDEXEC_GCC(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
@@ -141,6 +173,11 @@
#define STDEXEC_MSVC(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
+#if STDEXEC_NVHPC()
+#define STDEXEC_NVHPC_VERSION() \
+ (__NVCOMPILER_MAJOR__ * 100 + __NVCOMPILER_MINOR__)
+#endif
+
////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef __CUDACC__
#define STDEXEC_CUDA(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
@@ -149,6 +186,19 @@
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
+#if __cpp_impl_coroutine >= 201902 && __cpp_lib_coroutine >= 201902
+#include <coroutine>
+#define STDEXEC_STD_NO_COROUTINES() 0
+namespace __coro = std;
+#elif defined(__cpp_coroutines) && __has_include(<experimental/coroutine>)
+#include <experimental/coroutine>
+#define STDEXEC_STD_NO_COROUTINES() 0
+namespace __coro = std::experimental;
+#else
+#define STDEXEC_STD_NO_COROUTINES() 1
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
// For portably declaring attributes on functions and types
// Usage:
//
@@ -209,6 +259,14 @@
#endif
#define STDEXEC_ATTR_always_inline STDEXEC_PROBE(~, 4)
+#if STDEXEC_CLANG() || STDEXEC_GCC()
+#define STDEXEC_ATTR_WHICH_5(_ATTR) __attribute__((__weak__))
+#else
+#define STDEXEC_ATTR_WHICH_5(_ATTR) /*nothing*/
+#endif
+#define STDEXEC_ATTR_weak STDEXEC_PROBE(~, 5)
+#define STDEXEC_ATTR___weak__ STDEXEC_PROBE(~, 5)
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// warning push/pop portability macros
#if STDEXEC_NVCC()
@@ -232,6 +290,10 @@
#define STDEXEC_PRAGMA_POP() _Pragma("GCC diagnostic pop")
#define STDEXEC_PRAGMA_IGNORE_GNU(...) \
_Pragma(STDEXEC_STRINGIZE(GCC diagnostic ignored __VA_ARGS__))
+#elif STDEXEC_MSVC()
+#define STDEXEC_PRAGMA_PUSH() __pragma(warning(push))
+#define STDEXEC_PRAGMA_POP() __pragma(warning(pop))
+#define STDEXEC_PRAGMA_IGNORE_MSVC(...) __pragma(warning(disable : __VA_ARGS__))
#else
#define STDEXEC_PRAGMA_PUSH()
#define STDEXEC_PRAGMA_POP()
@@ -243,6 +305,9 @@
#ifndef STDEXEC_PRAGMA_IGNORE_EDG
#define STDEXEC_PRAGMA_IGNORE_EDG(...)
#endif
+#ifndef STDEXEC_PRAGMA_IGNORE_MSVC
+#define STDEXEC_PRAGMA_IGNORE_MSVC(...)
+#endif
#if !STDEXEC_MSVC() && defined(__has_builtin)
#define STDEXEC_HAS_BUILTIN __has_builtin
@@ -280,9 +345,77 @@
#if STDEXEC_HAS_BUILTIN(__is_const)
#define STDEXEC_IS_CONST(...) __is_const(__VA_ARGS__)
#else
-#define STDEXEC_IS_CONST(...) stdexec::__is_const<__VA_ARGS__>
+#define STDEXEC_IS_CONST(...) stdexec::__is_const_<__VA_ARGS__>
#endif
+#if STDEXEC_HAS_BUILTIN(__is_same)
+#define STDEXEC_IS_SAME(...) __is_same(__VA_ARGS__)
+#elif STDEXEC_HAS_BUILTIN(__is_same_as)
+#define STDEXEC_IS_SAME(...) __is_same_as(__VA_ARGS__)
+#elif STDEXEC_MSVC()
+// msvc replaces std::is_same_v with a compile-time constant
+#define STDEXEC_IS_SAME(...) std::is_same_v<__VA_ARGS__>
+#else
+#define STDEXEC_IS_SAME(...) stdexec::__same_as_v<__VA_ARGS__>
+#endif
+
+#if STDEXEC_HAS_BUILTIN(__is_constructible) || STDEXEC_MSVC()
+#define STDEXEC_IS_CONSTRUCTIBLE(...) __is_constructible(__VA_ARGS__)
+#else
+#define STDEXEC_IS_CONSTRUCTIBLE(...) std::is_constructible_v<__VA_ARGS__>
+#endif
+
+#if STDEXEC_HAS_BUILTIN(__is_nothrow_constructible) || STDEXEC_MSVC()
+#define STDEXEC_IS_NOTHROW_CONSTRUCTIBLE(...) \
+ __is_nothrow_constructible(__VA_ARGS__)
+#else
+#define STDEXEC_IS_NOTHROW_CONSTRUCTIBLE(...) \
+ std::is_nothrow_constructible_v<__VA_ARGS__>
+#endif
+
+#if STDEXEC_HAS_BUILTIN(__is_trivially_constructible) || STDEXEC_MSVC()
+#define STDEXEC_IS_TRIVIALLY_CONSTRUCTIBLE(...) \
+ __is_trivially_constructible(__VA_ARGS__)
+#else
+#define STDEXEC_IS_TRIVIALLY_CONSTRUCTIBLE(...) \
+ std::is_trivially_constructible_v<__VA_ARGS__>
+#endif
+
+#if STDEXEC_HAS_BUILTIN(__is_empty) || STDEXEC_MSVC()
+#define STDEXEC_IS_EMPTY(...) __is_empty(__VA_ARGS__)
+#else
+#define STDEXEC_IS_EMPTY(...) std::is_empty_v<__VA_ARGS__>
+#endif
+
+#if STDEXEC_HAS_BUILTIN(__remove_reference)
+namespace stdexec
+{
+template <class Ty>
+using _remove_reference_t = __remove_reference(Ty);
+} // namespace stdexec
+
+#define STDEXEC_REMOVE_REFERENCE(...) stdexec::_remove_reference_t<__VA_ARGS__>
+#elif STDEXEC_HAS_BUILTIN(__remove_reference_t)
+namespace stdexec
+{
+template <class Ty>
+using _remove_reference_t = __remove_reference_t(Ty);
+} // namespace stdexec
+
+#define STDEXEC_REMOVE_REFERENCE(...) stdexec::_remove_reference_t<__VA_ARGS__>
+#else
+#define STDEXEC_REMOVE_REFERENCE(...) ::std::remove_reference_t<__VA_ARGS__>
+#endif
+
+namespace stdexec
+{
+template <class _Ap, class _Bp>
+inline constexpr bool __same_as_v = false;
+
+template <class _Ap>
+inline constexpr bool __same_as_v<_Ap, _Ap> = true;
+} // namespace stdexec
+
#if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L
#define STDEXEC_UNREACHABLE() std::unreachable()
#elif STDEXEC_HAS_BUILTIN(__builtin_unreachable)
@@ -381,18 +514,6 @@
#define STDEXEC_EXPLICIT_THIS(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
-#if STDEXEC_EXPLICIT_THIS()
-#define STDEXEC_DEFINE_EXPLICIT_THIS_MEMFN(...) __VA_ARGS__
-#define STDEXEC_CALL_EXPLICIT_THIS_MEMFN(_OBJ, _NAME) (_OBJ)._NAME( STDEXEC_CALL_EXPLICIT_THIS_MEMFN_DETAIL
-#define STDEXEC_CALL_EXPLICIT_THIS_MEMFN_DETAIL(...) __VA_ARGS__ )
-#else
-#define STDEXEC_DEFINE_EXPLICIT_THIS_MEMFN(...) static __VA_ARGS__(STDEXEC_FUN_ARGS
-#define STDEXEC_CALL_EXPLICIT_THIS_MEMFN(_OBJ, _NAME) (_OBJ)._NAME((_OBJ) STDEXEC_CALL_EXPLICIT_THIS_MEMFN_DETAIL
-#define STDEXEC_CALL_EXPLICIT_THIS_MEMFN_DETAIL(...) __VA_OPT__(, ) __VA_ARGS__)
-#define STDEXEC_EAT_THIS_DETAIL_this
-#define STDEXEC_FUN_ARGS(...) STDEXEC_CAT(STDEXEC_EAT_THIS_DETAIL_, __VA_ARGS__))
-#endif
-
// Configure extra type checking
#define STDEXEC_TYPE_CHECKING_ZERO() 0
#define STDEXEC_TYPE_CHECKING_ONE() 1
diff --git a/include/sdbusplus/async/stdexec/__detail/__connect_awaitable.hpp b/include/sdbusplus/async/stdexec/__detail/__connect_awaitable.hpp
new file mode 100644
index 0000000..5f11184
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__connect_awaitable.hpp
@@ -0,0 +1,258 @@
+/*
+ * 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 "__awaitable.hpp"
+#include "__completion_signatures.hpp"
+#include "__concepts.hpp"
+#include "__config.hpp"
+#include "__execution_fwd.hpp"
+#include "__meta.hpp"
+#include "__receivers.hpp"
+#include "__tag_invoke.hpp"
+
+#include <exception>
+#include <utility>
+
+namespace stdexec
+{
+#if !STDEXEC_STD_NO_COROUTINES()
+/////////////////////////////////////////////////////////////////////////////
+// __connect_awaitable_
+namespace __connect_awaitable_
+{
+struct __promise_base
+{
+ auto initial_suspend() noexcept -> __coro::suspend_always
+ {
+ return {};
+ }
+
+ [[noreturn]] auto final_suspend() noexcept -> __coro::suspend_always
+ {
+ std::terminate();
+ }
+
+ [[noreturn]] void unhandled_exception() noexcept
+ {
+ std::terminate();
+ }
+
+ [[noreturn]] void return_void() noexcept
+ {
+ std::terminate();
+ }
+};
+
+struct __operation_base
+{
+ __coro::coroutine_handle<> __coro_;
+
+ explicit __operation_base(__coro::coroutine_handle<> __hcoro) noexcept :
+ __coro_(__hcoro)
+ {}
+
+ __operation_base(__operation_base&& __other) noexcept :
+ __coro_(std::exchange(__other.__coro_, {}))
+ {}
+
+ ~__operation_base()
+ {
+ if (__coro_)
+ {
+#if STDEXEC_MSVC()
+ // MSVCBUG
+ // https://developercommunity.visualstudio.com/t/Double-destroy-of-a-local-in-coroutine-d/10456428
+
+ // Reassign __coro_ before calling destroy to make the mutation
+ // observable and to hopefully ensure that the compiler does not
+ // eliminate it.
+ auto __coro = __coro_;
+ __coro_ = {};
+ __coro.destroy();
+#else
+ __coro_.destroy();
+#endif
+ }
+ }
+
+ void start() & noexcept
+ {
+ __coro_.resume();
+ }
+};
+
+template <class _ReceiverId>
+struct __promise;
+
+template <class _ReceiverId>
+struct __operation
+{
+ struct __t : __operation_base
+ {
+ using promise_type = stdexec::__t<__promise<_ReceiverId>>;
+ using __operation_base::__operation_base;
+ };
+};
+
+template <class _ReceiverId>
+struct __promise
+{
+ using _Receiver = stdexec::__t<_ReceiverId>;
+
+ struct __t : __promise_base
+ {
+ using __id = __promise;
+
+ explicit __t(auto&, _Receiver& __rcvr) noexcept : __rcvr_(__rcvr) {}
+
+ auto unhandled_stopped() noexcept -> __coro::coroutine_handle<>
+ {
+ stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr_));
+ // Returning noop_coroutine here causes the __connect_awaitable
+ // coroutine to never resume past the point where it co_await's
+ // the awaitable.
+ return __coro::noop_coroutine();
+ }
+
+ auto get_return_object() noexcept
+ -> stdexec::__t<__operation<_ReceiverId>>
+ {
+ return stdexec::__t<__operation<_ReceiverId>>{
+ __coro::coroutine_handle<__t>::from_promise(*this)};
+ }
+
+ template <class _Awaitable>
+ auto await_transform(_Awaitable&& __awaitable) noexcept -> _Awaitable&&
+ {
+ return static_cast<_Awaitable&&>(__awaitable);
+ }
+
+ template <class _Awaitable>
+ requires tag_invocable<as_awaitable_t, _Awaitable, __t&>
+ auto await_transform(_Awaitable&& __awaitable) //
+ noexcept(nothrow_tag_invocable<as_awaitable_t, _Awaitable, __t&>)
+ -> tag_invoke_result_t<as_awaitable_t, _Awaitable, __t&>
+ {
+ return tag_invoke(as_awaitable,
+ static_cast<_Awaitable&&>(__awaitable), *this);
+ }
+
+ // Pass through the get_env receiver query
+ auto get_env() const noexcept -> env_of_t<_Receiver>
+ {
+ return stdexec::get_env(__rcvr_);
+ }
+
+ _Receiver& __rcvr_;
+ };
+};
+
+template <receiver _Receiver>
+using __promise_t = __t<__promise<__id<_Receiver>>>;
+
+template <receiver _Receiver>
+using __operation_t = __t<__operation<__id<_Receiver>>>;
+
+struct __connect_awaitable_t
+{
+ private:
+ template <class _Fun, class... _Ts>
+ static auto __co_call(_Fun __fun, _Ts&&... __as) noexcept
+ {
+ auto __fn = [&, __fun]() noexcept {
+ __fun(static_cast<_Ts&&>(__as)...);
+ };
+
+ struct __awaiter
+ {
+ decltype(__fn) __fn_;
+
+ static constexpr auto await_ready() noexcept -> bool
+ {
+ return false;
+ }
+
+ void await_suspend(__coro::coroutine_handle<>) noexcept
+ {
+ __fn_();
+ }
+
+ [[noreturn]] void await_resume() noexcept
+ {
+ std::terminate();
+ }
+ };
+
+ return __awaiter{__fn};
+ }
+
+ template <class _Awaitable, class _Receiver>
+#if STDEXEC_GCC() && (__GNUC__ > 11)
+ __attribute__((__used__))
+#endif
+ static auto
+ __co_impl(_Awaitable __awaitable, _Receiver __rcvr)
+ -> __operation_t<_Receiver>
+ {
+ using __result_t = __await_result_t<_Awaitable, __promise_t<_Receiver>>;
+ std::exception_ptr __eptr;
+ try
+ {
+ if constexpr (same_as<__result_t, void>)
+ co_await (
+ co_await static_cast<_Awaitable&&>(__awaitable),
+ __co_call(set_value, static_cast<_Receiver&&>(__rcvr)));
+ else
+ co_await __co_call(
+ set_value, static_cast<_Receiver&&>(__rcvr),
+ co_await static_cast<_Awaitable&&>(__awaitable));
+ }
+ catch (...)
+ {
+ __eptr = std::current_exception();
+ }
+ co_await __co_call(set_error, static_cast<_Receiver&&>(__rcvr),
+ static_cast<std::exception_ptr&&>(__eptr));
+ }
+
+ template <receiver _Receiver, class _Awaitable>
+ using __completions_t = //
+ completion_signatures<
+ __minvoke< // set_value_t() or set_value_t(T)
+ __mremove<void, __qf<set_value_t>>,
+ __await_result_t<_Awaitable, __promise_t<_Receiver>>>,
+ set_error_t(std::exception_ptr), set_stopped_t()>;
+
+ public:
+ template <class _Receiver, __awaitable<__promise_t<_Receiver>> _Awaitable>
+ requires receiver_of<_Receiver, __completions_t<_Receiver, _Awaitable>>
+ auto operator()(_Awaitable&& __awaitable, _Receiver __rcvr) const
+ -> __operation_t<_Receiver>
+ {
+ return __co_impl(static_cast<_Awaitable&&>(__awaitable),
+ static_cast<_Receiver&&>(__rcvr));
+ }
+};
+} // namespace __connect_awaitable_
+
+using __connect_awaitable_::__connect_awaitable_t;
+#else
+struct __connect_awaitable_t
+{};
+#endif
+inline constexpr __connect_awaitable_t __connect_awaitable{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__continue_on.hpp b/include/sdbusplus/async/stdexec/__detail/__continue_on.hpp
new file mode 100644
index 0000000..a5566f6
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__continue_on.hpp
@@ -0,0 +1,130 @@
+/*
+ * 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.continue_on]
+namespace __continue_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 continue_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<continue_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<continue_on_t, __decay_t<_Scheduler>>
+ {
+ return {{static_cast<_Scheduler&&>(__sched)}, {}, {}};
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ using _Env = __0;
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(continue_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(continue_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 __continue_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 __continue_on
+
+using __continue_on::continue_on_t;
+inline constexpr continue_on_t continue_on{};
+
+using transfer_t = continue_on_t;
+inline constexpr transfer_t transfer{};
+
+template <>
+struct __sexpr_impl<continue_on_t> : __continue_on::__continue_on_impl
+{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__cpo.hpp b/include/sdbusplus/async/stdexec/__detail/__cpo.hpp
index 7a5d680..b5fb859 100644
--- a/include/sdbusplus/async/stdexec/__detail/__cpo.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__cpo.hpp
@@ -18,10 +18,6 @@
#include "__config.hpp"
#include "__execution_fwd.hpp"
-#define STDEXEC_EAT_THIS_this
-#define STDEXEC_EAT_AUTO_auto
-#define STDEXEC_EAT_VOID_void
-
///////////////////////////////////////////////////////////////////////////////
/// To hook a customization point like stdexec::get_env, first bring the names
/// in stdexec::tags into scope:
@@ -38,39 +34,54 @@
/// }
/// @endcode
#define STDEXEC_MEMFN_DECL(...) \
- friend STDEXEC_TAG_INVOKE(STDEXEC_IS_AUTO(__VA_ARGS__), __VA_ARGS__) \
- STDEXEC_TAG_INVOKE_ARGS
+ friend STDEXEC_EVAL(STDEXEC_MEMFN_DECL_TAG_INVOKE, \
+ STDEXEC_MEMFN_DECL_WHICH(__VA_ARGS__), __VA_ARGS__)
-#define STDEXEC_TAG_INVOKE(_ISAUTO, ...) \
- STDEXEC_IIF(_ISAUTO, STDEXEC_RETURN_AUTO, STDEXEC_RETURN_TYPE)(__VA_ARGS__) \
- tag_invoke( \
- STDEXEC_IIF(_ISAUTO, STDEXEC_TAG_AUTO, STDEXEC_TAG_WHAT)(__VA_ARGS__)
+#define STDEXEC_MEMFN_DECL_WHICH(_A1, ...) \
+ STDEXEC_CAT(STDEXEC_MEMFN_DECL_WHICH_, STDEXEC_FRONT(__VA_OPT__(1, ) 0)) \
+ (_A1, __VA_ARGS__)
+#define STDEXEC_MEMFN_DECL_WHICH_0(_A1, ...) \
+ STDEXEC_CHECK(STDEXEC_MEMFN_DECL_PROBE_##_A1), \
+ STDEXEC_CHECK(_A1##_STDEXEC_MEMFN_DECL_PROBE)
+#define STDEXEC_MEMFN_DECL_WHICH_1(_A1, ...) \
+ 0, STDEXEC_CHECK( \
+ STDEXEC_CAT(STDEXEC_BACK(__VA_ARGS__), _STDEXEC_MEMFN_DECL_PROBE))
-#define STDEXEC_PROBE_AUTO_auto STDEXEC_PROBE(~)
-#define STDEXEC_IS_AUTO(_TY, ...) \
- STDEXEC_CHECK(STDEXEC_CAT(STDEXEC_PROBE_AUTO_, _TY))
+#define STDEXEC_MEMFN_DECL_TAG_INVOKE(_WHICH, _QUERY, ...) \
+ STDEXEC_MEMFN_DECL_RETURN_ ## _WHICH(__VA_ARGS__) \
+ tag_invoke(STDEXEC_MEMFN_DECL_TAG_ ## _WHICH ## _QUERY(__VA_ARGS__)
-#define STDEXEC_PROBE_VOID_void STDEXEC_PROBE(~)
-#define STDEXEC_IS_VOID(_TY, ...) \
- STDEXEC_CHECK(STDEXEC_CAT(STDEXEC_PROBE_VOID_, _TY))
+#define STDEXEC_MEMFN_DECL_ARGS(...) \
+ STDEXEC_CAT(STDEXEC_EAT_THIS_, __VA_ARGS__))
-#define STDEXEC_RETURN_AUTO(...) auto
-#define STDEXEC_RETURN_TYPE(...) ::stdexec::__arg_type_t<void(__VA_ARGS__())>
+#define STDEXEC_MEMFN_DECL_QUERY(_SELF, _TAG, ...) \
+ _TAG, STDEXEC_CAT(STDEXEC_EAT_THIS_, _SELF) __VA_OPT__(, __VA_ARGS__))
-#define STDEXEC_TAG_AUTO(...) \
- STDEXEC_CAT(STDEXEC_CAT(STDEXEC_EAT_AUTO_, __VA_ARGS__), _t)
-#define STDEXEC_TAG_WHAT(...) \
- STDEXEC_IIF(STDEXEC_IS_VOID(__VA_ARGS__), STDEXEC_TAG_VOID, \
- STDEXEC_TAG_TYPE) \
- (__VA_ARGS__)
+#define STDEXEC_EAT_THIS_this
+#define STDEXEC_EAT_AUTO_auto
+#define STDEXEC_EAT_VOID_void
-#define STDEXEC_TAG_VOID(...) \
- STDEXEC_CAT(STDEXEC_CAT(STDEXEC_EAT_VOID_, __VA_ARGS__), _t)
-#define STDEXEC_TAG_TYPE(...) \
- ::stdexec::__tag_type_t<STDEXEC_CAT(__VA_ARGS__, _t::*)>
+#define query_STDEXEC_MEMFN_DECL_PROBE STDEXEC_PROBE(~, 1)
+#define STDEXEC_MEMFN_DECL_PROBE_auto STDEXEC_PROBE(~, 1)
+#define STDEXEC_MEMFN_DECL_PROBE_void STDEXEC_PROBE(~, 2)
-#define STDEXEC_TAG_INVOKE_ARGS(...) \
- __VA_OPT__(,) STDEXEC_CAT(STDEXEC_EAT_THIS_, __VA_ARGS__))
+#define STDEXEC_MEMFN_DECL_RETURN_0(...) \
+ ::stdexec::__arg_type_t<void(__VA_ARGS__())>
+#define STDEXEC_MEMFN_DECL_RETURN_1(...) auto
+#define STDEXEC_MEMFN_DECL_RETURN_2(...) void
+
+#define STDEXEC_MEMFN_DECL_TAG_00(...) \
+ const ::stdexec::__tag_type_t<__VA_ARGS__##_t::*>&, STDEXEC_MEMFN_DECL_ARGS
+#define STDEXEC_MEMFN_DECL_TAG_10(...) \
+ const STDEXEC_EAT_AUTO_##__VA_ARGS__##_t&, STDEXEC_MEMFN_DECL_ARGS
+#define STDEXEC_MEMFN_DECL_TAG_20(...) \
+ const STDEXEC_EAT_VOID_##__VA_ARGS__##_t&, STDEXEC_MEMFN_DECL_ARGS
+#define STDEXEC_MEMFN_DECL_TAG_01(...) STDEXEC_MEMFN_DECL_QUERY
+#define STDEXEC_MEMFN_DECL_TAG_11(...) STDEXEC_MEMFN_DECL_QUERY
+#define STDEXEC_MEMFN_DECL_TAG_21(...) STDEXEC_MEMFN_DECL_QUERY
+
+#define STDEXEC_MEMFN_FRIEND(_TAG) \
+ using STDEXEC_CAT(_TAG, _t) = STDEXEC_CAT(stdexec::_TAG, _t)
#if STDEXEC_MSVC()
#pragma deprecated(STDEXEC_CUSTOM)
diff --git a/include/sdbusplus/async/stdexec/__detail/__debug.hpp b/include/sdbusplus/async/stdexec/__detail/__debug.hpp
new file mode 100644
index 0000000..05036b1
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__debug.hpp
@@ -0,0 +1,282 @@
+/*
+ * 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 "__concepts.hpp"
+#include "__cpo.hpp"
+#include "__diagnostics.hpp"
+#include "__env.hpp"
+#include "__execution_fwd.hpp"
+#include "__meta.hpp"
+#include "__tag_invoke.hpp"
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// Some utilities for debugging senders
+namespace __debug
+{
+struct __is_debug_env_t
+{
+ static constexpr auto query(forwarding_query_t) noexcept -> bool
+ {
+ return true;
+ }
+ template <class _Env>
+ requires tag_invocable<__is_debug_env_t, const _Env&>
+ auto operator()(const _Env&) const noexcept
+ -> tag_invoke_result_t<__is_debug_env_t, const _Env&>;
+};
+
+template <class _Env>
+using __debug_env_t = env<prop<__is_debug_env_t, bool>, _Env>;
+
+template <class _Env>
+concept __is_debug_env = tag_invocable<__is_debug_env_t, _Env>;
+
+struct __completion_signatures
+{};
+
+#if STDEXEC_MSVC()
+// MSVCBUG
+// https://developercommunity.visualstudio.com/t/Explicit-variable-template-specialisatio/10360032
+// MSVCBUG
+// https://developercommunity.visualstudio.com/t/Non-function-type-interpreted-as-functio/10447831
+
+template <class _Sig>
+struct __normalize_sig;
+
+template <class _Tag, class... _Args>
+struct __normalize_sig<_Tag(_Args...)>
+{
+ using __type = _Tag (*)(_Args&&...);
+};
+
+template <class _Sig>
+using __normalize_sig_t = typename __normalize_sig<_Sig>::__type;
+#else
+template <class _Sig>
+extern int __normalize_sig;
+
+template <class _Tag, class... _Args>
+extern _Tag (*__normalize_sig<_Tag(_Args...)>)(_Args&&...);
+
+template <class _Sig>
+using __normalize_sig_t = decltype(__normalize_sig<_Sig>);
+#endif
+
+template <class... _Sigs>
+struct __valid_completions
+{
+ template <class... _Args>
+ requires __one_of<set_value_t (*)(_Args&&...), _Sigs...>
+ STDEXEC_ATTRIBUTE((host, device)) void set_value(_Args&&...) noexcept
+ {
+ STDEXEC_TERMINATE();
+ }
+
+ template <class _Error>
+ requires __one_of<set_error_t (*)(_Error&&), _Sigs...>
+ STDEXEC_ATTRIBUTE((host, device)) void set_error(_Error&&) noexcept
+ {
+ STDEXEC_TERMINATE();
+ }
+
+ STDEXEC_ATTRIBUTE((host, device))
+ void set_stopped() noexcept
+ requires __one_of<set_stopped_t (*)(), _Sigs...>
+ {
+ STDEXEC_TERMINATE();
+ }
+};
+
+template <class _CvrefSenderId, class _Env, class _Completions>
+struct __debug_receiver
+{
+ using __t = __debug_receiver;
+ using __id = __debug_receiver;
+ using receiver_concept = receiver_t;
+};
+
+template <class _CvrefSenderId, class _Env, class... _Sigs>
+struct __debug_receiver<_CvrefSenderId, _Env,
+ completion_signatures<_Sigs...>> //
+ : __valid_completions<__normalize_sig_t<_Sigs>...>
+{
+ using __t = __debug_receiver;
+ using __id = __debug_receiver;
+ using receiver_concept = receiver_t;
+
+ STDEXEC_ATTRIBUTE((host, device))
+ auto get_env() const noexcept -> __debug_env_t<_Env>
+ {
+ STDEXEC_TERMINATE();
+ }
+};
+
+struct _COMPLETION_SIGNATURES_MISMATCH_
+{};
+
+template <class _Sig>
+struct _COMPLETION_SIGNATURE_
+{};
+
+template <class... _Sigs>
+struct _IS_NOT_ONE_OF_
+{};
+
+template <class _Sender>
+struct _SIGNAL_SENT_BY_SENDER_
+{};
+
+template <class _Warning>
+[[deprecated(
+ "The sender claims to send a particular set of completions,"
+ " but in actual fact it completes with a result that is not"
+ " one of the declared completion signatures.")]] STDEXEC_ATTRIBUTE((host,
+ device)) void _ATTENTION_() noexcept
+{}
+
+template <class _Sig>
+struct __invalid_completion
+{
+ struct __t
+ {
+ template <class _CvrefSenderId, class _Env, class... _Sigs>
+ // BUGBUG this works around a recently (aug 2023) introduced regression
+ // in nvc++
+ requires(!__one_of<_Sig, _Sigs...>)
+ __t(__debug_receiver<_CvrefSenderId, _Env,
+ completion_signatures<_Sigs...>>&&) noexcept
+ {
+ using _SenderId = __decay_t<_CvrefSenderId>;
+ using _Sender = stdexec::__t<_SenderId>;
+ using _What = //
+ _WARNING_< //
+ _COMPLETION_SIGNATURES_MISMATCH_,
+ _COMPLETION_SIGNATURE_<_Sig>, _IS_NOT_ONE_OF_<_Sigs...>,
+ _SIGNAL_SENT_BY_SENDER_<__name_of<_Sender>>>;
+ __debug::_ATTENTION_<_What>();
+ }
+ };
+};
+
+template <__completion_tag _Tag, class... _Args>
+STDEXEC_ATTRIBUTE((host, device))
+void tag_invoke(_Tag, __t<__invalid_completion<_Tag(_Args...)>>,
+ _Args&&...) noexcept
+{}
+
+struct __debug_operation
+{
+ void start() & noexcept {}
+};
+
+////////////////////////////////////////////////////////////////////////////
+// `__debug_sender`
+// ===============
+//
+// Understanding why a particular sender doesn't connect to a particular
+// receiver is nigh impossible in the current design due to limitations in
+// how the compiler reports overload resolution failure in the presence of
+// constraints. `__debug_sender` is a utility to assist with the process. It
+// gives you the deep template instantiation backtrace that you need to
+// understand where in a chain of senders the problem is occurring.
+//
+// ```c++
+// template <class _Sigs, class _Env = empty_env, class _Sender>
+// void __debug_sender(_Sender&& __sndr, _Env = {});
+//
+// template <class _Env = empty_env, class _Sender>
+// void __debug_sender(_Sender&& __sndr, _Env = {});
+// ```
+//
+// **Usage:**
+//
+// To find out where in a chain of senders a sender is failing to connect
+// to a receiver, pass it to `__debug_sender`, optionally with an
+// environment argument; e.g. `__debug_sender(sndr [, env])`
+//
+// To find out why a sender will not connect to a receiver of a particular
+// signature, specify the set of completion signatures as an explicit template
+// argument that names an instantiation of `completion_signatures`; e.g.:
+// `__debug_sender<completion_signatures<set_value_t(int)>>(sndr [, env])`.
+//
+// **How it works:**
+//
+// The `__debug_sender` function `connect`'s the sender to a
+// `__debug_receiver`, whose environment is augmented with a special
+// `__is_debug_env_t` query. An additional fall-back overload is added to
+// the `connect` CPO that recognizes receivers whose environments respond to
+// that query and lets them through. Then in a non-immediate context, it
+// looks for a `tag_invoke(connect_t...)` overload for the input sender and
+// receiver. This will recurse until it hits the `tag_invoke` call that is
+// causing the failure.
+//
+// At least with clang, this gives me a nice backtrace, at the bottom of
+// which is the faulty `tag_invoke` overload with a mention of the
+// constraint that failed.
+template <class _Sigs, class _Env = empty_env, class _Sender>
+void __debug_sender(_Sender&& __sndr, const _Env& = {})
+{
+ if constexpr (!__is_debug_env<_Env>)
+ {
+ if (sizeof(_Sender) == ~0u)
+ { // never true
+ using _Receiver =
+ __debug_receiver<__cvref_id<_Sender>, _Env, _Sigs>;
+ using _Operation = connect_result_t<_Sender, _Receiver>;
+ // static_assert(receiver_of<_Receiver, _Sigs>);
+ if constexpr (!same_as<_Operation, __debug_operation>)
+ {
+ auto __op = connect(static_cast<_Sender&&>(__sndr),
+ _Receiver{});
+ stdexec::start(__op);
+ }
+ }
+ }
+}
+
+template <class _Env = empty_env, class _Sender>
+void __debug_sender(_Sender&& __sndr, const _Env& = {})
+{
+ if constexpr (!__is_debug_env<_Env>)
+ {
+ if (sizeof(_Sender) == ~0ul)
+ { // never true
+ using _Sigs =
+ __completion_signatures_of_t<_Sender, __debug_env_t<_Env>>;
+ if constexpr (!same_as<_Sigs, __debug::__completion_signatures>)
+ {
+ using _Receiver =
+ __debug_receiver<__cvref_id<_Sender>, _Env, _Sigs>;
+ using _Operation = connect_result_t<_Sender, _Receiver>;
+ // static_assert(receiver_of<_Receiver, _Sigs>);
+ if constexpr (!same_as<_Operation, __debug_operation>)
+ {
+ auto __op = connect(static_cast<_Sender&&>(__sndr),
+ _Receiver{});
+ stdexec::start(__op);
+ }
+ }
+ }
+ }
+}
+} // namespace __debug
+
+using __debug::__debug_sender;
+using __debug::__is_debug_env;
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__diagnostics.hpp b/include/sdbusplus/async/stdexec/__detail/__diagnostics.hpp
new file mode 100644
index 0000000..9180cf3
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__diagnostics.hpp
@@ -0,0 +1,113 @@
+/*
+ * 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 "__meta.hpp"
+
+namespace stdexec
+{
+namespace __detail
+{
+template <class _Ty>
+extern __q<__midentity> __name_of_v;
+
+template <class _Ty>
+using __name_of_fn = decltype(__name_of_v<_Ty>);
+
+template <class _Ty>
+using __name_of = __minvoke<__name_of_fn<_Ty>, _Ty>;
+} // namespace __detail
+
+// A utility for pretty-printing type names in diagnostics
+template <class _Ty>
+using __name_of = __detail::__name_of<_Ty>;
+
+namespace __errs
+{
+inline constexpr __mstring __unrecognized_sender_type_diagnostic =
+ "The given type cannot be used as a sender with the given environment "
+ "because the attempt to compute the completion signatures failed."_mstr;
+
+template <class _Sender>
+struct _WITH_SENDER_;
+
+template <class... _Senders>
+struct _WITH_SENDERS_;
+} // namespace __errs
+
+struct _WHERE_;
+
+struct _IN_ALGORITHM_;
+
+template <__mstring _Diagnostic = __errs::__unrecognized_sender_type_diagnostic>
+struct _UNRECOGNIZED_SENDER_TYPE_;
+
+template <class _Sender>
+using _WITH_SENDER_ = __errs::_WITH_SENDER_<__name_of<_Sender>>;
+
+template <class... _Senders>
+using _WITH_SENDERS_ = __errs::_WITH_SENDERS_<__name_of<_Senders>...>;
+
+template <class _Env>
+struct _WITH_ENVIRONMENT_;
+
+template <class _Ty>
+struct _WITH_TYPE_;
+
+template <class _Receiver>
+struct _WITH_RECEIVER_;
+
+template <class _Sig>
+struct _MISSING_COMPLETION_SIGNAL_;
+
+template <class _Sig>
+struct _WITH_COMPLETION_SIGNATURE_;
+
+template <class _Fun>
+struct _WITH_FUNCTION_;
+
+template <class... _Args>
+struct _WITH_ARGUMENTS_;
+
+template <class _Tag>
+struct _WITH_QUERY_;
+
+struct _SENDER_TYPE_IS_NOT_COPYABLE_;
+
+inline constexpr __mstring __not_callable_diag =
+ "The specified function is not callable with the arguments provided."_mstr;
+
+template <__mstring _Context, __mstring _Diagnostic = __not_callable_diag>
+struct _NOT_CALLABLE_;
+
+template <auto _Reason = "You cannot pipe one sender into another."_mstr>
+struct _CANNOT_PIPE_INTO_A_SENDER_
+{};
+
+template <class _Sender>
+using __bad_pipe_sink_t =
+ __mexception<_CANNOT_PIPE_INTO_A_SENDER_<>, _WITH_SENDER_<_Sender>>;
+
+template <__mstring _Context>
+struct __callable_error
+{
+ template <class _Fun, class... _Args>
+ using __f = //
+ __mexception< //
+ _NOT_CALLABLE_<_Context>, _WITH_FUNCTION_<_Fun>,
+ _WITH_ARGUMENTS_<_Args...>>;
+};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__domain.hpp b/include/sdbusplus/async/stdexec/__detail/__domain.hpp
index a82a1e6..7d6135d 100644
--- a/include/sdbusplus/async/stdexec/__detail/__domain.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__domain.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
@@ -16,12 +16,12 @@
#pragma once
#include "../functional.hpp"
-#include "__basic_sender.hpp"
#include "__concepts.hpp"
#include "__config.hpp"
#include "__env.hpp"
#include "__execution_fwd.hpp"
#include "__meta.hpp"
+#include "__sender_introspection.hpp"
namespace stdexec
{
@@ -83,6 +83,41 @@
requires(_DomainOrTag __tag, _Args&&... __args) {
__tag.apply_sender(static_cast<_Args&&>(__args)...);
};
+
+template <class _Sender>
+constexpr bool __is_nothrow_transform_sender()
+{
+ if constexpr (__callable<__sexpr_apply_t, _Sender,
+ __domain::__legacy_customization>)
+ {
+ return __nothrow_callable<__sexpr_apply_t, _Sender,
+ __domain::__legacy_customization>;
+ }
+ else if constexpr (__domain::__has_default_transform_sender<_Sender>)
+ {
+ return noexcept(
+ tag_of_t<_Sender>().transform_sender(__declval<_Sender>()));
+ }
+ else
+ {
+ return __nothrow_constructible_from<_Sender, _Sender>;
+ }
+}
+
+template <class _Sender, class _Env>
+constexpr bool __is_nothrow_transform_sender() noexcept
+{
+ if constexpr (__domain::__has_default_transform_sender<_Sender, _Env>)
+ {
+ return //
+ noexcept(tag_of_t<_Sender>().transform_sender(
+ __declval<_Sender>(), __declval<const _Env&>()));
+ }
+ else
+ {
+ return __nothrow_constructible_from<_Sender, _Sender>;
+ }
+}
} // namespace __domain
struct default_domain
@@ -93,6 +128,7 @@
template <class _Sender>
STDEXEC_ATTRIBUTE((always_inline))
decltype(auto) transform_sender(_Sender&& __sndr) const
+ noexcept(__domain::__is_nothrow_transform_sender<_Sender>())
{
// Look for a legacy customization for the given tag, and if found,
// apply it.
@@ -117,6 +153,7 @@
template <class _Sender, class _Env>
STDEXEC_ATTRIBUTE((always_inline))
decltype(auto) transform_sender(_Sender&& __sndr, const _Env& __env) const
+ noexcept(__domain::__is_nothrow_transform_sender<_Sender, _Env>())
{
if constexpr (__domain::__has_default_transform_sender<_Sender, _Env>)
{
@@ -185,9 +222,9 @@
// are no completion schedulers or if they don't specify a domain.
template <class _Env>
struct __completion_domain_or_none_ :
- __mdefer_<__transform<
+ __mdefer_<__mtransform<
__mbind_front_q<__completion_domain_for, _Env>,
- __remove<__none_such, __munique<__msingle_or<__none_such>>>>,
+ __mremove<__none_such, __munique<__msingle_or<__none_such>>>>,
set_value_t, set_error_t, set_stopped_t>
{};
@@ -215,7 +252,7 @@
{
if constexpr (__callable<get_domain_t, env_of_t<_Sender>>)
{
- return __call_result_t<get_domain_t, env_of_t<_Sender>>();
+ return __domain_of_t<env_of_t<_Sender>>();
}
else if constexpr (__detail::__has_completion_domain<_Sender>)
{
diff --git a/include/sdbusplus/async/stdexec/__detail/__ensure_started.hpp b/include/sdbusplus/async/stdexec/__detail/__ensure_started.hpp
new file mode 100644
index 0000000..0657379
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__ensure_started.hpp
@@ -0,0 +1,124 @@
+/*
+ * 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 "__intrusive_ptr.hpp"
+#include "__meta.hpp"
+#include "__sender_adaptor_closure.hpp"
+#include "__senders.hpp"
+#include "__shared.hpp"
+#include "__transform_sender.hpp"
+#include "__type_traits.hpp"
+
+#include <utility>
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.ensure_started]
+namespace __ensure_started
+{
+using namespace __shared;
+
+struct __ensure_started_t
+{};
+
+struct ensure_started_t
+{
+ template <sender _Sender, class _Env = empty_env>
+ requires sender_in<_Sender, _Env> && __decay_copyable<env_of_t<_Sender>>
+ [[nodiscard]] auto operator()(_Sender&& __sndr, _Env&& __env = {}) const
+ -> __well_formed_sender auto
+ {
+ if constexpr (sender_expr_for<_Sender, __ensure_started_t>)
+ {
+ return static_cast<_Sender&&>(__sndr);
+ }
+ else
+ {
+ auto __domain = __get_late_domain(__sndr, __env);
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<ensure_started_t>(static_cast<_Env&&>(__env),
+ static_cast<_Sender&&>(__sndr)));
+ }
+ }
+
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto operator()() const noexcept -> __binder_back<ensure_started_t>
+ {
+ return {{}, {}, {}};
+ }
+
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(ensure_started_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(const _Sender&)),
+ _Sender),
+ tag_invoke_t(ensure_started_t, _Sender)>;
+
+ template <class _CvrefSender, class _Env>
+ using __receiver_t =
+ __t<__meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>>>;
+
+ template <class _Sender>
+ static auto transform_sender(_Sender&& __sndr)
+ {
+ using _Receiver =
+ __receiver_t<__child_of<_Sender>, __decay_t<__data_of<_Sender>>>;
+ static_assert(sender_to<__child_of<_Sender>, _Receiver>);
+
+ return __sexpr_apply(static_cast<_Sender&&>(__sndr),
+ [&]<class _Env, class _Child>(
+ __ignore, _Env&& __env, _Child&& __child) {
+ // The shared state starts life with a ref-count of one.
+ auto __sh_state =
+ __make_intrusive<__shared_state<_Child, __decay_t<_Env>>, 2>(
+ static_cast<_Child&&>(__child), static_cast<_Env&&>(__env));
+
+ // Eagerly start the work:
+ __sh_state->__try_start();
+
+ return __make_sexpr<__ensure_started_t>(
+ __box{__ensure_started_t(), std::move(__sh_state)});
+ });
+ }
+};
+} // namespace __ensure_started
+
+using __ensure_started::ensure_started_t;
+inline constexpr ensure_started_t ensure_started{};
+
+template <>
+struct __sexpr_impl<__ensure_started::__ensure_started_t> :
+ __shared::__shared_impl<__ensure_started::__ensure_started_t>
+{};
+
+template <>
+struct __sexpr_impl<ensure_started_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>> {};
+};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__env.hpp b/include/sdbusplus/async/stdexec/__detail/__env.hpp
index 178b913..325ba79 100644
--- a/include/sdbusplus/async/stdexec/__detail/__env.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__env.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2023 NVIDIA Corporation
+ * 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
@@ -15,15 +15,16 @@
*/
#pragma once
-#include "../functional.hpp"
-#include "../stop_token.hpp"
#include "__concepts.hpp"
-#include "__config.hpp"
+#include "__cpo.hpp"
#include "__execution_fwd.hpp"
#include "__meta.hpp"
+#include "__stop_token.hpp"
+#include "__tag_invoke.hpp"
+#include "__tuple.hpp"
-#include <concepts>
#include <exception>
+#include <functional>
#include <type_traits>
STDEXEC_PRAGMA_PUSH()
@@ -47,16 +48,32 @@
// [exec.queries]
namespace __queries
{
+template <class _Tp>
+concept __is_bool_constant = //
+ requires { //
+ typename __mbool<_Tp::value>;
+ };
+
struct forwarding_query_t
{
template <class _Query>
- constexpr auto operator()(_Query __query) const noexcept -> bool
+ consteval auto operator()(_Query __query) const noexcept -> bool
{
if constexpr (tag_invocable<forwarding_query_t, _Query>)
{
- return tag_invoke(*this, static_cast<_Query&&>(__query));
+ using __result_t = tag_invoke_result_t<forwarding_query_t, _Query>;
+ // If this a integral type wrapper, unpack it and return the value.
+ // Otherwise, return the result of the tag_invoke call expression.
+ if constexpr (__is_bool_constant<__result_t>)
+ {
+ return __result_t::value;
+ }
+ else
+ {
+ return tag_invoke(*this, static_cast<_Query&&>(__query));
+ }
}
- else if constexpr (std::derived_from<_Query, forwarding_query_t>)
+ else if constexpr (derived_from<_Query, forwarding_query_t>)
{
return true;
}
@@ -115,7 +132,8 @@
requires tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>>
constexpr auto operator()(_Tp&& __t) const noexcept(
nothrow_tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>>)
- -> tag_invoke_result_t<get_forward_progress_guarantee_t, __cref_t<_Tp>>
+ -> __decay_t<tag_invoke_result_t<get_forward_progress_guarantee_t,
+ __cref_t<_Tp>>>
{
return tag_invoke(get_forward_progress_guarantee_t{},
std::as_const(__t));
@@ -160,8 +178,7 @@
struct get_scheduler_t : __query<get_scheduler_t>
{
- friend constexpr auto tag_invoke(forwarding_query_t,
- const get_scheduler_t&) noexcept -> bool
+ static constexpr auto query(forwarding_query_t) noexcept -> bool
{
return true;
}
@@ -177,9 +194,7 @@
struct get_delegatee_scheduler_t : __query<get_delegatee_scheduler_t>
{
- friend constexpr auto tag_invoke(forwarding_query_t,
- const get_delegatee_scheduler_t&) noexcept
- -> bool
+ static constexpr auto query(forwarding_query_t) noexcept -> bool
{
return true;
}
@@ -195,8 +210,7 @@
struct get_allocator_t : __query<get_allocator_t>
{
- friend constexpr auto tag_invoke(forwarding_query_t,
- const get_allocator_t&) noexcept -> bool
+ static constexpr auto query(forwarding_query_t) noexcept -> bool
{
return true;
}
@@ -218,26 +232,26 @@
struct get_stop_token_t : __query<get_stop_token_t>
{
- friend constexpr auto tag_invoke(forwarding_query_t,
- const get_stop_token_t&) noexcept -> bool
+ static constexpr auto query(forwarding_query_t) noexcept -> bool
{
return true;
}
- template <class _Env>
- auto operator()(const _Env&) const noexcept -> never_stop_token
+ template <class _Env, class _Token = never_stop_token>
+ auto operator()(const _Env&) const noexcept -> _Token
{
return {};
}
- template <class _Env>
+ template <class _Env, class = void>
requires tag_invocable<get_stop_token_t, const _Env&>
auto operator()(const _Env& __env) const noexcept
-> tag_invoke_result_t<get_stop_token_t, const _Env&>
{
static_assert(nothrow_tag_invocable<get_stop_token_t, const _Env&>);
- static_assert(stoppable_token<
- tag_invoke_result_t<get_stop_token_t, const _Env&>>);
+ static_assert(
+ stoppable_token<
+ __decay_t<tag_invoke_result_t<get_stop_token_t, const _Env&>>>);
return tag_invoke(get_stop_token_t{}, __env);
}
@@ -245,24 +259,22 @@
auto operator()() const noexcept;
};
-template <class _Queryable, class _CPO>
+template <class _Queryable, class _Tag>
concept __has_completion_scheduler_for =
queryable<_Queryable> && //
- tag_invocable<get_completion_scheduler_t<_CPO>, const _Queryable&>;
+ tag_invocable<get_completion_scheduler_t<_Tag>, const _Queryable&>;
-template <__completion_tag _CPO>
-struct get_completion_scheduler_t : __query<get_completion_scheduler_t<_CPO>>
+template <__completion_tag _Tag>
+struct get_completion_scheduler_t : __query<get_completion_scheduler_t<_Tag>>
{
- friend constexpr auto
- tag_invoke(forwarding_query_t,
- const get_completion_scheduler_t<_CPO>&) noexcept -> bool
+ static constexpr auto query(forwarding_query_t) noexcept -> bool
{
return true;
}
- template <__has_completion_scheduler_for<_CPO> _Queryable>
+ template <__has_completion_scheduler_for<_Tag> _Queryable>
auto operator()(const _Queryable& __queryable) const noexcept
- -> tag_invoke_result_t<get_completion_scheduler_t<_CPO>,
+ -> tag_invoke_result_t<get_completion_scheduler_t<_Tag>,
const _Queryable&>;
};
@@ -271,16 +283,68 @@
template <class _Ty>
requires tag_invocable<get_domain_t, const _Ty&>
constexpr auto operator()(const _Ty& __ty) const noexcept
- -> tag_invoke_result_t<get_domain_t, const _Ty&>
+ -> __decay_t<tag_invoke_result_t<get_domain_t, const _Ty&>>
{
static_assert(nothrow_tag_invocable<get_domain_t, const _Ty&>,
"Customizations of get_domain must be noexcept.");
- static_assert(__class<tag_invoke_result_t<get_domain_t, const _Ty&>>,
- "Customizations of get_domain must return a class type.");
- return tag_invoke(get_domain_t{}, __ty);
+ static_assert(
+ __class<__decay_t<tag_invoke_result_t<get_domain_t, const _Ty&>>>,
+ "Customizations of get_domain must return a class type.");
+ return {};
}
- friend constexpr auto tag_invoke(forwarding_query_t, get_domain_t) noexcept
+ static constexpr auto query(forwarding_query_t) noexcept -> bool
+ {
+ return true;
+ }
+};
+
+struct __is_scheduler_affine_t
+{
+ template <class _Env>
+ constexpr auto operator()(const _Env&) const noexcept
+ {
+ if constexpr (tag_invocable<__is_scheduler_affine_t, const _Env&>)
+ {
+ using _Result =
+ tag_invoke_result_t<__is_scheduler_affine_t, const _Env&>;
+ static_assert(__same_as<decltype(__v<_Result>), const bool>);
+ return _Result();
+ }
+ else
+ {
+ return std::false_type();
+ }
+ }
+
+ static constexpr auto query(forwarding_query_t) noexcept -> bool
+ {
+ return false;
+ }
+};
+
+struct __root_t
+{
+ template <class _Env>
+ requires tag_invocable<__root_t, const _Env&>
+ constexpr auto operator()(const _Env& __env) const noexcept -> bool
+ {
+ STDEXEC_ASSERT(tag_invoke(__root_t{}, __env) == true);
+ return true;
+ }
+
+ static constexpr auto query(forwarding_query_t) noexcept -> bool
+ {
+ return false;
+ }
+};
+
+struct __root_env
+{
+ using __t = __root_env;
+ using __id = __root_env;
+
+ constexpr STDEXEC_MEMFN_DECL(auto __root)(this const __root_env&) noexcept
-> bool
{
return true;
@@ -289,6 +353,9 @@
} // 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;
@@ -312,8 +379,8 @@
inline constexpr get_allocator_t get_allocator{};
inline constexpr get_stop_token_t get_stop_token{};
#if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__)
-template <__completion_tag _CPO>
-inline constexpr get_completion_scheduler_t<_CPO> get_completion_scheduler{};
+template <__completion_tag _Tag>
+inline constexpr get_completion_scheduler_t<_Tag> get_completion_scheduler{};
#else
template <>
inline constexpr get_completion_scheduler_t<set_value_t>
@@ -331,36 +398,15 @@
inline constexpr get_domain_t get_domain{};
+template <class _Env>
+using __domain_of_t = __decay_t<__call_result_t<get_domain_t, _Env>>;
+
template <class _Tag, class _Queryable, class _Default>
using __query_result_or_t =
__call_result_t<query_or_t, _Tag, _Queryable, _Default>;
-/////////////////////////////////////////////////////////////////////////////
namespace __env
{
-// For getting an execution environment from a receiver,
-// or the attributes from a sender.
-struct get_env_t
-{
- template <class _EnvProvider>
- requires tag_invocable<get_env_t, const _EnvProvider&>
- STDEXEC_ATTRIBUTE((always_inline)) constexpr auto
- operator()(const _EnvProvider& __env_provider) const noexcept
- -> tag_invoke_result_t<get_env_t, const _EnvProvider&>
- {
- static_assert(
- queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>);
- static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>);
- return tag_invoke(*this, __env_provider);
- }
-
- template <class _EnvProvider>
- constexpr auto operator()(const _EnvProvider&) const noexcept -> empty_env
- {
- return {};
- }
-};
-
// To be kept in sync with the promise type used in __connect_awaitable
template <class _Env>
struct __promise
@@ -380,13 +426,127 @@
return tag_invoke(as_awaitable, static_cast<_Ty&&>(__value), *this);
}
- template <same_as<get_env_t> _Tag>
- friend auto tag_invoke(_Tag, const __promise&) noexcept -> const _Env&
- {
- std::terminate();
- }
+ auto get_env() const noexcept -> const _Env&;
};
+template <class _Env, class _Query, class... _Args>
+concept __queryable = //
+ tag_invocable<_Query, const _Env&, _Args...>;
+
+template <class _Env, class _Query, class... _Args>
+concept __nothrow_queryable = //
+ nothrow_tag_invocable<_Query, const _Env&, _Args...>;
+
+template <class _Env, class _Query, class... _Args>
+using __query_result_t = //
+ tag_invoke_result_t<_Query, const _Env&, _Args...>;
+
+// A singleton environment from a query/value pair
+template <class _Query, class _Value>
+struct prop
+{
+ using __t = prop;
+ using __id = prop;
+
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _Query __query;
+
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _Value __value;
+
+ STDEXEC_ATTRIBUTE((nodiscard))
+ constexpr const _Value& query(_Query) const noexcept
+ {
+ return __value;
+ }
+
+ prop& operator=(const prop&) = delete;
+};
+
+template <class _Query, class _Value>
+prop(_Query, _Value) -> prop<_Query, std::unwrap_reference_t<_Value>>;
+
+// utility for joining multiple environments
+template <class... _Envs>
+struct env
+{
+ using __t = env;
+ using __id = env;
+
+ __tuple_for<_Envs...> __tup_;
+
+ // return a reference to the first child env for which
+ // __queryable<_Envs, _Query, _Args...> is true.
+ template <class _Query, class... _Args>
+ STDEXEC_ATTRIBUTE((always_inline))
+ constexpr decltype(auto) __get_1st() const noexcept
+ {
+ constexpr bool __flags[] = {__queryable<_Envs, _Query, _Args...>...};
+ constexpr std::size_t __idx = __pos_of(__flags,
+ __flags + sizeof...(_Envs));
+ return __tup::get<__idx>(__tup_);
+ }
+
+ template <class _Query, class... _Args>
+ requires(__queryable<_Envs, _Query, _Args...> || ...)
+ STDEXEC_ATTRIBUTE((always_inline)) constexpr decltype(auto)
+ query(_Query __q, _Args&&... __args) const
+ noexcept(__nothrow_queryable<decltype(__get_1st<_Query, _Args...>()),
+ _Query, _Args...>)
+ {
+ return tag_invoke(__q, __get_1st<_Query, _Args...>(),
+ static_cast<_Args&&>(__args)...);
+ }
+
+ env& operator=(const env&) = delete;
+};
+
+// specialization for two envs to avoid warnings about elided braces
+template <class _Env0, class _Env1>
+struct env<_Env0, _Env1>
+{
+ using __t = env;
+ using __id = env;
+
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _Env0 __env0_;
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _Env1 __env1_;
+
+ // return a reference to the first child env for which
+ // __queryable<_Envs, _Query, _Args...> is true.
+ template <class _Query, class... _Args>
+ STDEXEC_ATTRIBUTE((always_inline))
+ constexpr decltype(auto) __get_1st() const noexcept
+ {
+ if constexpr (__queryable<_Env0, _Query, _Args...>)
+ {
+ return (__env0_);
+ }
+ else
+ {
+ return (__env1_);
+ }
+ }
+
+ template <class _Query, class... _Args>
+ requires __queryable<_Env0, _Query, _Args...> ||
+ __queryable<_Env1, _Query, _Args...>
+ STDEXEC_ATTRIBUTE((always_inline)) constexpr decltype(auto)
+ query(_Query __q, _Args&&... __args) const
+ noexcept(__nothrow_queryable<decltype(__get_1st<_Query, _Args...>()),
+ _Query, _Args...>)
+ {
+ return tag_invoke(__q, __get_1st<_Query, _Args...>(),
+ static_cast<_Args&&>(__args)...);
+ }
+
+ env& operator=(const env&) = delete;
+};
+
+template <class... _Envs>
+env(_Envs...) -> env<std::unwrap_reference_t<_Envs>...>;
+
template <class _Value, class _Tag, class... _Tags>
struct __with
{
@@ -408,101 +568,104 @@
{}
template <__one_of<_Tag, _Tags...> _Key>
- friend auto tag_invoke(_Key, const __with& __self) //
- noexcept(__nothrow_decay_copyable<_Value>) -> _Value
+ auto query(_Key) const noexcept -> const _Value&
{
- return __self.__value_;
+ return __value_;
}
+
+ __with& operator=(const __with&) = delete;
};
template <class _Value, class _Tag, class... _Tags>
__with(_Value, _Tag, _Tags...) -> __with<_Value, _Tag, _Tags...>;
-template <class _Env>
+template <class _EnvId>
struct __fwd
{
+ using _Env = __cvref_t<_EnvId>;
static_assert(__nothrow_move_constructible<_Env>);
- using __t = __fwd;
- using __id = __fwd;
- STDEXEC_ATTRIBUTE((no_unique_address))
- _Env __env_;
- template <__forwarding_query _Tag>
- requires tag_invocable<_Tag, const _Env&>
- friend auto tag_invoke(_Tag, const __fwd& __self) //
- noexcept(nothrow_tag_invocable<_Tag, const _Env&>)
- -> tag_invoke_result_t<_Tag, const _Env&>
+ struct __t
{
- return _Tag()(__self.__env_);
- }
+ using __id = __fwd;
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _Env __env_;
+
+#if STDEXEC_GCC() && __GNUC__ < 12
+ using __cvref_env_t = std::add_const_t<_Env>&;
+#else
+ using __cvref_env_t = const _Env&;
+#endif
+
+ template <__forwarding_query _Tag>
+ requires tag_invocable<_Tag, __cvref_env_t>
+ auto query(_Tag) const
+ noexcept(nothrow_tag_invocable<_Tag, __cvref_env_t>)
+ -> tag_invoke_result_t<_Tag, __cvref_env_t>
+ {
+ return tag_invoke(_Tag(), __env_);
+ }
+
+ __t& operator=(const __t&) = delete;
+ };
};
-template <class _Env>
-__fwd(_Env&&) -> __fwd<_Env>;
-
-template <class _Env>
-struct __ref
-{
- using __t = __ref;
- using __id = __ref;
- const _Env& __env_;
-
- template <class _Tag>
- requires tag_invocable<_Tag, const _Env&>
- friend auto tag_invoke(_Tag, const __ref& __self) //
- noexcept(nothrow_tag_invocable<_Tag, const _Env&>)
- -> tag_invoke_result_t<_Tag, const _Env&>
- {
- return _Tag()(__self.__env_);
- }
-};
-
-template <class _Env>
-__ref(_Env&) -> __ref<_Env>;
-
-struct __ref_fn
+struct __fwd_fn
{
template <class _Env>
- constexpr auto operator()(_Env&& __env) const
+ auto operator()(_Env&& __env) const
{
- if constexpr (same_as<_Env, _Env&>)
- {
- return __ref{static_cast<_Env&&>(__env)};
- }
- else
- {
- return static_cast<_Env&&>(__env);
- }
+ return __t<__fwd<__cvref_id<_Env>>>{static_cast<_Env&&>(__env)};
+ }
+
+ auto operator()(empty_env) const -> empty_env
+ {
+ return {};
}
};
-template <class _Env, class _Tag, class... _Tags>
-struct __without_ : _Env
+template <class _EnvId, class _Tag>
+struct __without_
{
+ using _Env = __cvref_t<_EnvId>;
static_assert(__nothrow_move_constructible<_Env>);
- using __t = __without_;
- using __id = __without_;
- constexpr explicit __without_(_Env&& __env, _Tag, _Tags...) noexcept :
- _Env(static_cast<_Env&&>(__env))
- {}
+ struct __t
+ {
+ using __id = __without_;
+ _Env __env_;
- template <__one_of<_Tag, _Tags...> _Key, class _Self>
- requires(std::is_base_of_v<__without_, __decay_t<_Self>>)
- friend auto tag_invoke(_Key, _Self&&) noexcept = delete;
+#if STDEXEC_GCC() && __GNUC__ < 12
+ using __cvref_env_t = std::add_const_t<_Env>&;
+#else
+ using __cvref_env_t = const _Env&;
+#endif
+
+ auto query(_Tag) const noexcept = delete;
+
+ template <tag_invocable<__cvref_env_t> _Key>
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto query(_Key) const
+ noexcept(nothrow_tag_invocable<_Key, __cvref_env_t>)
+ -> decltype(auto)
+ {
+ return tag_invoke(_Key(), __env_);
+ }
+
+ __t& operator=(const __t&) = delete;
+ };
};
struct __without_fn
{
- template <class _Env, class _Tag, class... _Tags>
- constexpr auto operator()(_Env&& __env, _Tag, _Tags...) const noexcept
+ template <class _Env, class _Tag>
+ constexpr auto operator()(_Env&& __env, _Tag) const noexcept
-> decltype(auto)
{
- if constexpr (tag_invocable<_Tag, _Env> ||
- (tag_invocable<_Tags, _Env> || ...))
+ if constexpr (tag_invocable<_Tag, _Env>)
{
- return __without_{__ref_fn()(static_cast<_Env&&>(__env)), _Tag(),
- _Tags()...};
+ using _Without = __t<__without_<__cvref_id<_Env>, _Tag>>;
+ return _Without{static_cast<_Env&&>(__env)};
}
else
{
@@ -516,30 +679,6 @@
template <class _Env, class _Tag, class... _Tags>
using __without_t = __result_of<__without, _Env, _Tag, _Tags...>;
-template <class _Second, class _First>
-struct __joined : _Second
-{
- static_assert(__nothrow_move_constructible<_First>);
- static_assert(__nothrow_move_constructible<_Second>);
- using __t = __joined;
- using __id = __joined;
-
- STDEXEC_ATTRIBUTE((no_unique_address))
- _First __env_;
-
- template <class _Tag>
- requires tag_invocable<_Tag, const _First&>
- friend auto tag_invoke(_Tag, const __joined& __self) //
- noexcept(nothrow_tag_invocable<_Tag, const _First&>)
- -> tag_invoke_result_t<_Tag, const _First&>
- {
- return _Tag()(__self.__env_);
- }
-};
-
-template <class _Second, class _First>
-__joined(_Second&&, _First&&) -> __joined<_Second, _First>;
-
template <__nothrow_move_constructible _Fun>
struct __from
{
@@ -550,82 +689,110 @@
template <class _Tag>
requires __callable<const _Fun&, _Tag>
- friend auto tag_invoke(_Tag, const __from& __self) //
- noexcept(__nothrow_callable<const _Fun&, _Tag>)
- -> __call_result_t<const _Fun&, _Tag>
+ auto query(_Tag) const noexcept(__nothrow_callable<const _Fun&, _Tag>)
+ -> __call_result_t<const _Fun&, _Tag>
{
- return __self.__fun_(_Tag());
+ return __fun_(_Tag());
}
+
+ __from& operator=(const __from&) = delete;
};
template <class _Fun>
__from(_Fun) -> __from<_Fun>;
-struct __fwd_fn
-{
- template <class Env>
- auto operator()(Env&& env) const
- {
- return __fwd{static_cast<Env&&>(env)};
- }
-
- auto operator()(empty_env) const -> empty_env
- {
- return {};
- }
-};
-
struct __join_fn
{
- auto operator()() const -> empty_env
+ auto operator()(empty_env, empty_env) const noexcept -> empty_env
{
return {};
}
template <class _Env>
- auto operator()(_Env&& __env) const -> _Env
+ auto operator()(_Env&& __env, empty_env = {}) const noexcept -> _Env
{
return static_cast<_Env&&>(__env);
}
- auto operator()(empty_env) const -> empty_env
- {
- return {};
- }
-
template <class _Env>
- auto operator()(_Env&& __env, empty_env) const -> _Env
+ auto operator()(empty_env, _Env&& __env) const noexcept -> decltype(auto)
{
- return static_cast<_Env&&>(__env);
+ return __fwd_fn()(static_cast<_Env&&>(__env));
}
- auto operator()(empty_env, empty_env) const -> empty_env
+ template <class _First, class _Second>
+ auto operator()(_First&& __first, _Second&& __second) const noexcept
+ -> env<_First, __call_result_t<__fwd_fn, _Second>>
{
- return {};
- }
-
- template <class... Rest>
- auto operator()(empty_env, Rest&&... rest) const -> decltype(auto)
- {
- return __fwd_fn()(__join_fn()(static_cast<Rest&&>(rest)...));
- }
-
- template <class First, class... Rest>
- auto operator()(First&& first, Rest&&... rest) const -> decltype(auto)
- {
- return __joined{__fwd_fn()(__join_fn()(static_cast<Rest&&>(rest)...)),
- static_cast<First&&>(first)};
+ return {static_cast<_First&&>(__first),
+ __fwd_fn()(static_cast<_Second&&>(__second))};
}
};
inline constexpr __join_fn __join{};
-template <class... _Envs>
-using __join_t = __result_of<__join, _Envs...>;
+template <class _First, class... _Second>
+using __join_t = __result_of<__join, _First, _Second...>;
+
+struct __as_root_env_fn
+{
+ template <class _Env>
+ constexpr auto operator()(_Env __env) const noexcept
+ -> __join_t<__root_env, std::unwrap_reference_t<_Env>>
+ {
+ return __join(__root_env{},
+ static_cast<std::unwrap_reference_t<_Env>&&>(__env));
+ }
+};
+
+inline constexpr __as_root_env_fn __as_root_env{};
+
+template <class _Env>
+using __as_root_env_t = __result_of<__as_root_env, _Env>;
} // namespace __env
-using __env::empty_env;
-using __env::get_env_t;
+using __env::env;
+using __env::prop;
+using empty_env = env<>;
+
+/////////////////////////////////////////////////////////////////////////////
+namespace __get_env
+{
+// For getting an execution environment from a receiver or the attributes from a
+// sender.
+struct get_env_t
+{
+ template <__same_as<get_env_t> _Self, class _EnvProvider>
+ STDEXEC_ATTRIBUTE((always_inline))
+ friend auto tag_invoke(_Self, const _EnvProvider& __env_provider) noexcept
+ -> decltype(__env_provider.get_env())
+ {
+ static_assert(noexcept(__env_provider.get_env()),
+ "get_env() members must be noexcept");
+ return __env_provider.get_env();
+ }
+
+ template <class _EnvProvider>
+ requires tag_invocable<get_env_t, const _EnvProvider&>
+ STDEXEC_ATTRIBUTE((always_inline)) constexpr auto
+ operator()(const _EnvProvider& __env_provider) const noexcept
+ -> tag_invoke_result_t<get_env_t, const _EnvProvider&>
+ {
+ static_assert(
+ queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>);
+ static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>);
+ return tag_invoke(*this, __env_provider);
+ }
+
+ template <class _EnvProvider>
+ constexpr auto operator()(const _EnvProvider&) const noexcept -> empty_env
+ {
+ return {};
+ }
+};
+} // namespace __get_env
+
+using __get_env::get_env_t;
inline constexpr get_env_t get_env{};
template <class _EnvProvider>
@@ -635,6 +802,23 @@
get_env(std::as_const(__ep))
} -> queryable;
};
+
+using __env::__as_root_env;
+using __env::__as_root_env_t;
+
+template <class _Env>
+concept __is_root_env = requires(_Env&& __env) {
+ {
+ __root_t{}(__env)
+ } -> same_as<bool>;
+ };
+
+template <class _Sender>
+concept __is_scheduler_affine = //
+ requires {
+ requires __v<
+ __call_result_t<__is_scheduler_affine_t, env_of_t<_Sender>>>;
+ };
} // namespace stdexec
STDEXEC_PRAGMA_POP()
diff --git a/include/sdbusplus/async/stdexec/__detail/__execute.hpp b/include/sdbusplus/async/stdexec/__detail/__execute.hpp
new file mode 100644
index 0000000..cb62593
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__execute.hpp
@@ -0,0 +1,92 @@
+/*
+ * 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 "__completion_signatures.hpp"
+#include "__concepts.hpp"
+#include "__cpo.hpp"
+#include "__execution_fwd.hpp"
+#include "__meta.hpp"
+#include "__receivers.hpp"
+#include "__schedulers.hpp"
+#include "__senders.hpp"
+#include "__submit.hpp"
+#include "__tag_invoke.hpp"
+#include "__transform_sender.hpp"
+#include "__type_traits.hpp"
+
+#include <exception>
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.execute]
+namespace __execute_
+{
+template <class _Fun>
+struct __as_receiver
+{
+ using receiver_concept = receiver_t;
+ _Fun __fun_;
+
+ void set_value() noexcept
+ {
+ // terminates on exception:
+ __fun_();
+ }
+
+ [[noreturn]] void set_error(std::exception_ptr) noexcept
+ {
+ std::terminate();
+ }
+
+ void set_stopped() noexcept {}
+};
+
+struct execute_t
+{
+ template <scheduler _Scheduler, class _Fun>
+ requires __callable<_Fun&> && move_constructible<_Fun>
+ void operator()(_Scheduler&& __sched, _Fun __fun) const noexcept(false)
+ {
+ // Look for a legacy customization
+ if constexpr (tag_invocable<execute_t, _Scheduler, _Fun>)
+ {
+ tag_invoke(execute_t{}, static_cast<_Scheduler&&>(__sched),
+ static_cast<_Fun&&>(__fun));
+ }
+ else
+ {
+ auto __domain = query_or(get_domain, __sched, default_domain());
+ stdexec::apply_sender(__domain, *this,
+ schedule(static_cast<_Scheduler&&>(__sched)),
+ static_cast<_Fun&&>(__fun));
+ }
+ }
+
+ template <sender_of<set_value_t()> _Sender, class _Fun>
+ requires __callable<_Fun&> && move_constructible<_Fun>
+ void apply_sender(_Sender&& __sndr, _Fun __fun) const noexcept(false)
+ {
+ __submit(static_cast<_Sender&&>(__sndr),
+ __as_receiver<_Fun>{static_cast<_Fun&&>(__fun)});
+ }
+};
+} // namespace __execute_
+
+using __execute_::execute_t;
+inline constexpr execute_t execute{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__execution_fwd.hpp b/include/sdbusplus/async/stdexec/__detail/__execution_fwd.hpp
index 43e123a..7b25130 100644
--- a/include/sdbusplus/async/stdexec/__detail/__execution_fwd.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__execution_fwd.hpp
@@ -28,16 +28,16 @@
struct default_domain;
//////////////////////////////////////////////////////////////////////////////////////////////////
-namespace __receivers
+namespace __rcvrs
{
struct set_value_t;
struct set_error_t;
struct set_stopped_t;
-} // namespace __receivers
+} // namespace __rcvrs
-using __receivers::set_error_t;
-using __receivers::set_stopped_t;
-using __receivers::set_value_t;
+using __rcvrs::set_error_t;
+using __rcvrs::set_stopped_t;
+using __rcvrs::set_value_t;
extern const set_value_t set_value;
extern const set_error_t set_error;
extern const set_stopped_t set_stopped;
@@ -51,20 +51,26 @@
template <class _Sender>
extern const bool enable_receiver;
-//////////////////////////////////////////////////////////////////////////////////////////////////
namespace __env
{
-struct get_env_t;
+template <class _Query, class _Value>
+struct prop;
-struct empty_env
-{
- using __t = empty_env;
- using __id = empty_env;
-};
+template <class... _Envs>
+struct env;
} // namespace __env
-using __env::empty_env;
-using __env::get_env_t;
+using __env::env;
+using __env::prop;
+using empty_env = env<>;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+namespace __get_env
+{
+struct get_env_t;
+} // namespace __get_env
+
+using __get_env::get_env_t;
extern const get_env_t get_env;
template <class _EnvProvider>
@@ -113,6 +119,12 @@
template <__completion_tag _CPO>
extern const get_completion_scheduler_t<_CPO> get_completion_scheduler;
+struct never_stop_token;
+class inplace_stop_source;
+class inplace_stop_token;
+template <class _Fn>
+class inplace_stop_callback;
+
template <class _Tp>
using stop_token_of_t = __decay_t<__call_result_t<get_stop_token_t, _Tp>>;
@@ -125,14 +137,27 @@
__call_result_t<get_completion_scheduler_t<_CPO>, env_of_t<const _Sender&>>;
//////////////////////////////////////////////////////////////////////////////////////////////////
-namespace __get_completion_signatures
+namespace __sigs
{
-struct get_completion_signatures_t;
-} // namespace __get_completion_signatures
+template <class _Sig>
+inline constexpr bool __is_compl_sig = false;
-using __get_completion_signatures::get_completion_signatures_t;
+struct get_completion_signatures_t;
+} // namespace __sigs
+
+template <class _Sig>
+concept __completion_signature = __sigs::__is_compl_sig<_Sig>;
+
+template <class... _Sigs>
+struct completion_signatures;
+
+using __sigs::get_completion_signatures_t;
extern const get_completion_signatures_t get_completion_signatures;
+template <class _Sender, class... _Env>
+using __completion_signatures_of_t = //
+ __call_result_t<get_completion_signatures_t, _Sender, _Env...>;
+
//////////////////////////////////////////////////////////////////////////////////////////////////
namespace __connect
{
@@ -164,12 +189,12 @@
extern const start_t start;
//////////////////////////////////////////////////////////////////////////////////////////////////
-namespace __schedule
+namespace __sched
{
struct schedule_t;
-} // namespace __schedule
+} // namespace __sched
-using __schedule::schedule_t;
+using __sched::schedule_t;
extern const schedule_t schedule;
//////////////////////////////////////////////////////////////////////////////////////////////////
@@ -182,12 +207,27 @@
extern const as_awaitable_t as_awaitable;
//////////////////////////////////////////////////////////////////////////////////////////////////
-namespace __transfer
+namespace __start_on
{
-struct transfer_t;
-} // namespace __transfer
+struct start_on_t;
+} // namespace __start_on
-using __transfer::transfer_t;
+using __start_on::start_on_t;
+extern const start_on_t start_on;
+
+using on_t = start_on_t;
+extern const on_t on;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+namespace __continue_on
+{
+struct continue_on_t;
+} // namespace __continue_on
+
+using __continue_on::continue_on_t;
+extern const continue_on_t continue_on;
+
+using transfer_t = continue_on_t;
extern const transfer_t transfer;
//////////////////////////////////////////////////////////////////////////////////////////////////
@@ -249,8 +289,8 @@
} // namespace stdexec
template <class...>
-[[deprecated]] void print()
+[[deprecated]] void __print()
{}
-template <class>
-struct undef;
+template <class...>
+struct __undef;
diff --git a/include/sdbusplus/async/stdexec/__detail/__inline_scheduler.hpp b/include/sdbusplus/async/stdexec/__detail/__inline_scheduler.hpp
new file mode 100644
index 0000000..e65a48b
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__inline_scheduler.hpp
@@ -0,0 +1,88 @@
+/*
+ * 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 "__basic_sender.hpp"
+#include "__cpo.hpp"
+#include "__env.hpp"
+#include "__execution_fwd.hpp"
+#include "__receivers.hpp"
+#include "__schedulers.hpp"
+#include "__utility.hpp"
+
+namespace stdexec
+{
+namespace __inln
+{
+struct __schedule_t
+{};
+
+struct __scheduler
+{
+ using __t = __scheduler;
+ using __id = __scheduler;
+
+ template <class _Tag = __schedule_t>
+ STDEXEC_ATTRIBUTE((host, device))
+ STDEXEC_MEMFN_DECL(auto schedule)(this __scheduler)
+ {
+ return __make_sexpr<_Tag>();
+ }
+
+ auto query(get_forward_progress_guarantee_t) const noexcept
+ -> forward_progress_guarantee
+ {
+ return forward_progress_guarantee::weakly_parallel;
+ }
+
+ auto operator==(const __scheduler&) const noexcept -> bool = default;
+};
+
+struct __env
+{
+ static constexpr bool query(__is_scheduler_affine_t) noexcept
+ {
+ return true;
+ }
+
+ constexpr auto query(get_completion_scheduler_t<set_value_t>) const noexcept
+ -> __scheduler
+ {
+ return {};
+ }
+};
+} // namespace __inln
+
+template <>
+struct __sexpr_impl<__inln::__schedule_t> : __sexpr_defaults
+{
+ static constexpr auto get_attrs = //
+ [](__ignore) noexcept { return __inln::__env(); };
+
+ static constexpr auto get_completion_signatures = //
+ [](__ignore,
+ __ignore = {}) noexcept -> completion_signatures<set_value_t()> {
+ return {};
+ };
+
+ static constexpr auto start = //
+ []<class _Receiver>(__ignore, _Receiver& __rcvr) noexcept -> void {
+ stdexec::set_value(static_cast<_Receiver&&>(__rcvr));
+ };
+};
+
+static_assert(__is_scheduler_affine<schedule_result_t<__inln::__scheduler>>);
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__into_variant.hpp b/include/sdbusplus/async/stdexec/__detail/__into_variant.hpp
new file mode 100644
index 0000000..5673b49
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__into_variant.hpp
@@ -0,0 +1,130 @@
+/*
+ * 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 "__domain.hpp"
+#include "__env.hpp"
+#include "__meta.hpp"
+#include "__sender_adaptor_closure.hpp"
+#include "__senders.hpp"
+#include "__transform_completion_signatures.hpp"
+#include "__transform_sender.hpp"
+#include "__utility.hpp"
+
+#include <exception>
+#include <tuple>
+#include <variant>
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.into_variant]
+namespace __into_variant
+{
+template <class _Sender, class _Env>
+ requires sender_in<_Sender, _Env>
+using __into_variant_result_t = value_types_of_t<_Sender, _Env>;
+
+template <class _Sender, class... _Env>
+using __variant_t =
+ __value_types_t<__completion_signatures_of_t<_Sender, _Env...>>;
+
+template <class _Variant>
+using __variant_completions =
+ completion_signatures<set_value_t(_Variant),
+ set_error_t(std::exception_ptr)>;
+
+template <class _Sender, class... _Env>
+using __completions = //
+ transform_completion_signatures<
+ __completion_signatures_of_t<_Sender, _Env...>,
+ __meval<__variant_completions, __variant_t<_Sender, _Env...>>,
+ __mconst<completion_signatures<>>::__f>;
+
+struct into_variant_t
+{
+ template <sender _Sender>
+ auto operator()(_Sender&& __sndr) const -> __well_formed_sender auto
+ {
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<into_variant_t>(__(), static_cast<_Sender&&>(__sndr)));
+ }
+
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto operator()() const noexcept -> __binder_back<into_variant_t>
+ {
+ return {{}, {}, {}};
+ }
+};
+
+struct __into_variant_impl : __sexpr_defaults
+{
+ static constexpr auto get_state = //
+ []<class _Self, class _Receiver>(_Self&&, _Receiver&) noexcept {
+ using __variant_t =
+ value_types_of_t<__child_of<_Self>, env_of_t<_Receiver>>;
+ return __mtype<__variant_t>();
+ };
+
+ static constexpr auto complete = //
+ []<class _State, class _Receiver, class _Tag, class... _Args>(
+ __ignore, _State, _Receiver& __rcvr, _Tag,
+ _Args&&... __args) noexcept -> void {
+ if constexpr (__same_as<_Tag, set_value_t>)
+ {
+ using __variant_t = __t<_State>;
+ try
+ {
+ set_value(static_cast<_Receiver&&>(__rcvr),
+ __variant_t{std::tuple<_Args&&...>{
+ static_cast<_Args&&>(__args)...}});
+ }
+ catch (...)
+ {
+ stdexec::set_error(static_cast<_Receiver&&>(__rcvr),
+ std::current_exception());
+ }
+ }
+ else
+ {
+ _Tag()(static_cast<_Receiver&&>(__rcvr),
+ static_cast<_Args&&>(__args)...);
+ }
+ };
+
+ static constexpr auto get_completion_signatures = //
+ []<class _Self, class... _Env>(_Self&&, _Env&&...) noexcept //
+ -> __completions<__child_of<_Self>, _Env...> {
+ static_assert(sender_expr_for<_Self, into_variant_t>);
+ return {};
+ };
+};
+} // namespace __into_variant
+
+using __into_variant::into_variant_t;
+inline constexpr into_variant_t into_variant{};
+
+template <>
+struct __sexpr_impl<into_variant_t> : __into_variant::__into_variant_impl
+{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__intrusive_mpsc_queue.hpp b/include/sdbusplus/async/stdexec/__detail/__intrusive_mpsc_queue.hpp
new file mode 100644
index 0000000..2830784
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__intrusive_mpsc_queue.hpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) Dmitiy V'jukov
+ * Copyright (c) 2024 Maikel Nadolski
+ * Copyright (c) 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.
+ */
+
+// general design of this MPSC queue is taken from
+// https://www.1024cores.net/home/lock-free-algorithms/queues/intrusive-mpsc-node-based-queue
+
+#pragma once
+
+#include <sdbusplus/async/stdexec/__spin_loop_pause.hpp>
+
+#include <atomic>
+
+namespace stdexec
+{
+template <auto _Ptr>
+class __intrusive_mpsc_queue;
+
+template <class _Node, std::atomic<void*> _Node::*_Next>
+class __intrusive_mpsc_queue<_Next>
+{
+ std::atomic<void*> __back_{&__nil_};
+ void* __front_{&__nil_};
+ std::atomic<_Node*> __nil_ = nullptr;
+
+ void push_back_nil()
+ {
+ __nil_.store(nullptr, std::memory_order_relaxed);
+ _Node* __prev = static_cast<_Node*>(
+ __back_.exchange(&__nil_, std::memory_order_acq_rel));
+ (__prev->*_Next).store(&__nil_, std::memory_order_release);
+ }
+
+ public:
+ bool push_back(_Node* __new_node) noexcept
+ {
+ (__new_node->*_Next).store(nullptr, std::memory_order_relaxed);
+ void* __prev_back = __back_.exchange(__new_node,
+ std::memory_order_acq_rel);
+ bool __is_nil = __prev_back == static_cast<void*>(&__nil_);
+ if (__is_nil)
+ {
+ __nil_.store(__new_node, std::memory_order_release);
+ }
+ else
+ {
+ (static_cast<_Node*>(__prev_back)->*_Next)
+ .store(__new_node, std::memory_order_release);
+ }
+ return __is_nil;
+ }
+
+ _Node* pop_front() noexcept
+ {
+ if (__front_ == static_cast<void*>(&__nil_))
+ {
+ _Node* __next = __nil_.load(std::memory_order_acquire);
+ if (!__next)
+ {
+ return nullptr;
+ }
+ __front_ = __next;
+ }
+ _Node* __front = static_cast<_Node*>(__front_);
+ void* __next = (__front->*_Next).load(std::memory_order_acquire);
+ if (__next)
+ {
+ __front_ = __next;
+ return __front;
+ }
+ STDEXEC_ASSERT(!__next);
+ push_back_nil();
+ do
+ {
+ __spin_loop_pause();
+ __next = (__front->*_Next).load(std::memory_order_acquire);
+ } while (!__next);
+ __front_ = __next;
+ return __front;
+ }
+};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__intrusive_ptr.hpp b/include/sdbusplus/async/stdexec/__detail/__intrusive_ptr.hpp
index 9897a0b..9242067 100644
--- a/include/sdbusplus/async/stdexec/__detail/__intrusive_ptr.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__intrusive_ptr.hpp
@@ -15,8 +15,7 @@
*/
#pragma once
-#include "../concepts.hpp"
-#include "__config.hpp"
+#include "__concepts.hpp"
#include "__meta.hpp"
#include <atomic>
@@ -33,41 +32,79 @@
{
namespace __ptr
{
-template <class _Ty>
+template <std::size_t _ReservedBits>
+struct __count_and_bits
+{
+ static constexpr std::size_t __ref_count_increment = 1ul << _ReservedBits;
+
+ enum struct __bits : std::size_t
+ {
+ };
+
+ friend constexpr std::size_t __count(__bits __b) noexcept
+ {
+ return static_cast<std::size_t>(__b) / __ref_count_increment;
+ }
+
+ template <std::size_t _Bit>
+ friend constexpr bool __bit(__bits __b) noexcept
+ {
+ static_assert(_Bit < _ReservedBits, "Bit index out of range");
+ return (static_cast<std::size_t>(__b) & (1ul << _Bit)) != 0;
+ }
+};
+
+template <std::size_t _ReservedBits>
+using __bits_t = typename __count_and_bits<_ReservedBits>::__bits;
+
+template <class _Ty, std::size_t _ReservedBits>
struct __make_intrusive_t;
-template <class _Ty>
+template <class _Ty, std::size_t _ReservedBits = 0ul>
class __intrusive_ptr;
-template <class _Ty>
+template <class _Ty, std::size_t _ReservedBits = 0ul>
struct __enable_intrusive_from_this
{
- auto __intrusive_from_this() noexcept -> __intrusive_ptr<_Ty>;
- auto __intrusive_from_this() const noexcept -> __intrusive_ptr<const _Ty>;
+ auto __intrusive_from_this() noexcept
+ -> __intrusive_ptr<_Ty, _ReservedBits>;
+ auto __intrusive_from_this() const noexcept
+ -> __intrusive_ptr<const _Ty, _ReservedBits>;
private:
+ using __bits_t = typename __count_and_bits<_ReservedBits>::__bits;
friend _Ty;
- void __inc_ref() noexcept;
- void __dec_ref() noexcept;
+ __bits_t __inc_ref() noexcept;
+ __bits_t __dec_ref() noexcept;
+
+ template <std::size_t _Bit>
+ bool __is_set() const noexcept;
+ template <std::size_t _Bit>
+ __bits_t __set_bit() noexcept;
+ template <std::size_t _Bit>
+ __bits_t __clear_bit() noexcept;
};
STDEXEC_PRAGMA_PUSH()
STDEXEC_PRAGMA_IGNORE_GNU("-Wtsan")
-template <class _Ty>
+template <class _Ty, std::size_t _ReservedBits>
struct __control_block
{
+ using __bits_t = typename __count_and_bits<_ReservedBits>::__bits;
+ static constexpr std::size_t __ref_count_increment = 1ul << _ReservedBits;
+
alignas(_Ty) unsigned char __value_[sizeof(_Ty)];
- std::atomic<unsigned long> __refcount_;
+ std::atomic<std::size_t> __ref_count_;
template <class... _Us>
explicit __control_block(_Us&&... __us) noexcept(noexcept(_Ty{
__declval<_Us>()...})) :
- __refcount_(1u)
+ __ref_count_(__ref_count_increment)
{
- // Construct the value *after* the initialization of the
- // atomic in case the constructor of _Ty calls
- // __intrusive_from_this() (which increments the atomic):
+ // Construct the value *after* the initialization of the atomic in case
+ // the constructor of _Ty calls __intrusive_from_this() (which
+ // increments the ref count):
::new (static_cast<void*>(__value_)) _Ty{static_cast<_Us&&>(__us)...};
}
@@ -81,36 +118,68 @@
return *reinterpret_cast<_Ty*>(__value_);
}
- void __inc_ref_() noexcept
+ __bits_t __inc_ref_() noexcept
{
- __refcount_.fetch_add(1, std::memory_order_relaxed);
+ auto __old = __ref_count_.fetch_add(__ref_count_increment,
+ std::memory_order_relaxed);
+ return static_cast<__bits_t>(__old);
}
- void __dec_ref_() noexcept
+ __bits_t __dec_ref_() noexcept
{
- if (1u == __refcount_.fetch_sub(1, std::memory_order_release))
+ auto __old = __ref_count_.fetch_sub(__ref_count_increment,
+ std::memory_order_acq_rel);
+ if (__count(static_cast<__bits_t>(__old)) == 1)
{
- std::atomic_thread_fence(std::memory_order_acquire);
- // TSan does not support std::atomic_thread_fence, so we
- // need to use the TSan-specific __tsan_acquire instead:
- STDEXEC_TSAN(__tsan_acquire(&__refcount_));
delete this;
}
+ return static_cast<__bits_t>(__old);
+ }
+
+ // Returns true if the bit was set, false if it was already set.
+ template <std::size_t _Bit>
+ [[nodiscard]] bool __is_set_() const noexcept
+ {
+ auto __old = __ref_count_.load(std::memory_order_relaxed);
+ return __bit<_Bit>(static_cast<__bits_t>(__old));
+ }
+
+ template <std::size_t _Bit>
+ __bits_t __set_bit_() noexcept
+ {
+ static_assert(_Bit < _ReservedBits, "Bit index out of range");
+ constexpr std::size_t __mask = 1ul << _Bit;
+ auto __old = __ref_count_.fetch_or(__mask, std::memory_order_acq_rel);
+ return static_cast<__bits_t>(__old);
+ }
+
+ // Returns true if the bit was cleared, false if it was already cleared.
+ template <std::size_t _Bit>
+ __bits_t __clear_bit_() noexcept
+ {
+ static_assert(_Bit < _ReservedBits, "Bit index out of range");
+ constexpr std::size_t __mask = 1ul << _Bit;
+ auto __old = __ref_count_.fetch_and(~__mask, std::memory_order_acq_rel);
+ return static_cast<__bits_t>(__old);
}
};
STDEXEC_PRAGMA_POP()
-template <class _Ty>
+template <class _Ty, std::size_t _ReservedBits /* = 0ul */>
class __intrusive_ptr
{
using _UncvTy = std::remove_cv_t<_Ty>;
- friend struct __make_intrusive_t<_Ty>;
- friend struct __enable_intrusive_from_this<_UncvTy>;
+ using __enable_intrusive_t =
+ __enable_intrusive_from_this<_UncvTy, _ReservedBits>;
+ friend _Ty;
+ friend struct __make_intrusive_t<_Ty, _ReservedBits>;
+ friend struct __enable_intrusive_from_this<_UncvTy, _ReservedBits>;
- __control_block<_UncvTy>* __data_{nullptr};
+ __control_block<_UncvTy, _ReservedBits>* __data_{nullptr};
- explicit __intrusive_ptr(__control_block<_UncvTy>* __data) noexcept :
+ explicit __intrusive_ptr(
+ __control_block<_UncvTy, _ReservedBits>* __data) noexcept :
__data_(__data)
{}
@@ -130,6 +199,16 @@
}
}
+ // For use when types want to take over manual control of the reference
+ // count. Very unsafe, but useful for implementing custom reference
+ // counting.
+ [[nodiscard]] __enable_intrusive_t* __release_() noexcept
+ {
+ auto* __data = std::exchange(__data_, nullptr);
+ return __data ? &__c_upcast<__enable_intrusive_t>(__data->__value())
+ : nullptr;
+ }
+
public:
using element_type = _Ty;
@@ -145,7 +224,8 @@
__inc_ref_();
}
- __intrusive_ptr(__enable_intrusive_from_this<_Ty>* __that) noexcept :
+ __intrusive_ptr(
+ __enable_intrusive_from_this<_Ty, _ReservedBits>* __that) noexcept :
__intrusive_ptr(__that ? __that->__intrusive_from_this()
: __intrusive_ptr())
{}
@@ -162,7 +242,8 @@
return operator=(__intrusive_ptr(__that));
}
- auto operator=(__enable_intrusive_from_this<_Ty>* __that) noexcept
+ auto operator=(
+ __enable_intrusive_from_this<_Ty, _ReservedBits>* __that) noexcept
-> __intrusive_ptr&
{
return operator=(__that ? __that->__intrusive_from_this()
@@ -217,59 +298,93 @@
}
};
-template <class _Ty>
-auto __enable_intrusive_from_this<_Ty>::__intrusive_from_this() noexcept
- -> __intrusive_ptr<_Ty>
+template <class _Ty, std::size_t _ReservedBits>
+auto __enable_intrusive_from_this<
+ _Ty, _ReservedBits>::__intrusive_from_this() noexcept
+ -> __intrusive_ptr<_Ty, _ReservedBits>
{
- auto* __data =
- reinterpret_cast<__control_block<_Ty>*>(static_cast<_Ty*>(this));
+ auto* __data = reinterpret_cast<__control_block<_Ty, _ReservedBits>*>(
+ &__c_downcast<_Ty>(*this));
__data->__inc_ref_();
- return __intrusive_ptr<_Ty>{__data};
+ return __intrusive_ptr<_Ty, _ReservedBits>{__data};
}
-template <class _Ty>
-auto __enable_intrusive_from_this<_Ty>::__intrusive_from_this() const noexcept
- -> __intrusive_ptr<const _Ty>
+template <class _Ty, std::size_t _ReservedBits>
+auto __enable_intrusive_from_this<_Ty, _ReservedBits>::__intrusive_from_this()
+ const noexcept -> __intrusive_ptr<const _Ty, _ReservedBits>
{
- auto* __data =
- reinterpret_cast<__control_block<_Ty>*>(static_cast<const _Ty*>(this));
+ auto* __data = reinterpret_cast<__control_block<_Ty, _ReservedBits>*>(
+ &__c_downcast<_Ty>(*this));
__data->__inc_ref_();
- return __intrusive_ptr<const _Ty>{__data};
+ return __intrusive_ptr<const _Ty, _ReservedBits>{__data};
}
-template <class _Ty>
-void __enable_intrusive_from_this<_Ty>::__inc_ref() noexcept
+template <class _Ty, std::size_t _ReservedBits>
+__bits_t<_ReservedBits>
+ __enable_intrusive_from_this<_Ty, _ReservedBits>::__inc_ref() noexcept
{
- auto* __data =
- reinterpret_cast<__control_block<_Ty>*>(static_cast<_Ty*>(this));
- __data->__inc_ref_();
+ auto* __data = reinterpret_cast<__control_block<_Ty, _ReservedBits>*>(
+ &__c_downcast<_Ty>(*this));
+ return __data->__inc_ref_();
}
-template <class _Ty>
-void __enable_intrusive_from_this<_Ty>::__dec_ref() noexcept
+template <class _Ty, std::size_t _ReservedBits>
+__bits_t<_ReservedBits>
+ __enable_intrusive_from_this<_Ty, _ReservedBits>::__dec_ref() noexcept
{
- auto* __data =
- reinterpret_cast<__control_block<_Ty>*>(static_cast<_Ty*>(this));
- __data->__dec_ref_();
+ auto* __data = reinterpret_cast<__control_block<_Ty, _ReservedBits>*>(
+ &__c_downcast<_Ty>(*this));
+ return __data->__dec_ref_();
}
-template <class _Ty>
+template <class _Ty, std::size_t _ReservedBits>
+template <std::size_t _Bit>
+bool __enable_intrusive_from_this<_Ty, _ReservedBits>::__is_set() const noexcept
+{
+ auto* __data = reinterpret_cast<const __control_block<_Ty, _ReservedBits>*>(
+ &__c_downcast<_Ty>(*this));
+ return __data->template __is_set_<_Bit>();
+}
+
+template <class _Ty, std::size_t _ReservedBits>
+template <std::size_t _Bit>
+__bits_t<_ReservedBits>
+ __enable_intrusive_from_this<_Ty, _ReservedBits>::__set_bit() noexcept
+{
+ auto* __data = reinterpret_cast<__control_block<_Ty, _ReservedBits>*>(
+ &__c_downcast<_Ty>(*this));
+ return __data->template __set_bit_<_Bit>();
+}
+
+template <class _Ty, std::size_t _ReservedBits>
+template <std::size_t _Bit>
+__bits_t<_ReservedBits>
+ __enable_intrusive_from_this<_Ty, _ReservedBits>::__clear_bit() noexcept
+{
+ auto* __data = reinterpret_cast<__control_block<_Ty, _ReservedBits>*>(
+ &__c_downcast<_Ty>(*this));
+ return __data->template __clear_bit_<_Bit>();
+}
+
+template <class _Ty, std::size_t _ReservedBits>
struct __make_intrusive_t
{
template <class... _Us>
requires constructible_from<_Ty, _Us...>
- auto operator()(_Us&&... __us) const -> __intrusive_ptr<_Ty>
+ auto operator()(_Us&&... __us) const -> __intrusive_ptr<_Ty, _ReservedBits>
{
using _UncvTy = std::remove_cv_t<_Ty>;
- return __intrusive_ptr<_Ty>{
- ::new __control_block<_UncvTy>{static_cast<_Us&&>(__us)...}};
+ return __intrusive_ptr<_Ty, _ReservedBits>{
+ ::new __control_block<_UncvTy, _ReservedBits>{
+ static_cast<_Us&&>(__us)...}};
}
};
} // namespace __ptr
using __ptr::__enable_intrusive_from_this;
using __ptr::__intrusive_ptr;
-template <class _Ty>
-inline constexpr __ptr::__make_intrusive_t<_Ty> __make_intrusive{};
+template <class _Ty, std::size_t _ReservedBits = 0ul>
+inline constexpr __ptr::__make_intrusive_t<_Ty, _ReservedBits>
+ __make_intrusive{};
} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__intrusive_queue.hpp b/include/sdbusplus/async/stdexec/__detail/__intrusive_queue.hpp
index dbcccf4..dc3f1c3 100644
--- a/include/sdbusplus/async/stdexec/__detail/__intrusive_queue.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__intrusive_queue.hpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2021-2022 Facebook, Inc. and its affiliates
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
diff --git a/include/sdbusplus/async/stdexec/__detail/__intrusive_slist.hpp b/include/sdbusplus/async/stdexec/__detail/__intrusive_slist.hpp
new file mode 100644
index 0000000..8d3e03e
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__intrusive_slist.hpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 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 "__config.hpp"
+
+#include <cassert>
+#include <cstddef>
+#include <iterator>
+#include <utility>
+
+namespace stdexec
+{
+namespace __slist
+{
+template <auto _Next>
+class __intrusive_slist;
+
+template <class _Item, _Item* _Item::*_Next>
+class __intrusive_slist<_Next>
+{
+ public:
+ __intrusive_slist() noexcept = default;
+
+ __intrusive_slist(__intrusive_slist&& __other) noexcept :
+ __head_(std::exchange(__other.__head_, nullptr))
+ {}
+
+ __intrusive_slist(_Item* __head) noexcept : __head_(__head) {}
+
+ auto swap(__intrusive_slist& __other) noexcept -> void
+ {
+ std::swap(__head_, __other.__head_);
+ }
+
+ auto operator=(__intrusive_slist __other) noexcept -> __intrusive_slist&
+ {
+ swap(__other);
+ return *this;
+ }
+
+ [[nodiscard]] auto empty() const noexcept -> bool
+ {
+ return __head_ == nullptr;
+ }
+
+ auto front() const noexcept -> _Item*
+ {
+ return __head_;
+ }
+
+ void clear() noexcept
+ {
+ __head_ = nullptr;
+ }
+
+ [[nodiscard]] auto pop_front() noexcept -> _Item*
+ {
+ STDEXEC_ASSERT(!empty());
+ return std::exchange(__head_, __head_->*_Next);
+ }
+
+ void push_front(_Item* __item) noexcept
+ {
+ STDEXEC_ASSERT(__item != nullptr);
+ __item->*_Next = std::exchange(__head_, __item);
+ }
+
+ [[nodiscard]] _Item* remove(_Item* __item) noexcept
+ {
+ STDEXEC_ASSERT(__item != nullptr);
+ if (__head_ == __item)
+ {
+ return pop_front();
+ }
+
+ for (_Item* __current : *this)
+ {
+ if (__current->*_Next == __item)
+ {
+ __current->*_Next = __item->*_Next;
+ return __item;
+ }
+ }
+
+ return nullptr;
+ }
+
+ struct iterator
+ {
+ using iterator_category = std::forward_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = _Item*;
+ using reference = _Item*;
+ using pointer = _Item**;
+
+ _Item* __item_ = nullptr;
+
+ iterator() noexcept = default;
+
+ explicit iterator(_Item* __item) noexcept : __item_(__item) {}
+
+ [[nodiscard]] auto operator*() const noexcept -> _Item*
+ {
+ STDEXEC_ASSERT(__item_ != nullptr);
+ return __item_;
+ }
+
+ [[nodiscard]] auto operator->() const noexcept -> _Item**
+ {
+ STDEXEC_ASSERT(__item_ != nullptr);
+ return &__item_;
+ }
+
+ auto operator++() noexcept -> iterator&
+ {
+ STDEXEC_ASSERT(__item_ != nullptr);
+ __item_ = __item_->*_Next;
+ return *this;
+ }
+
+ auto operator++(int) noexcept -> iterator
+ {
+ iterator __result = *this;
+ ++*this;
+ return __result;
+ }
+
+ auto operator==(const iterator&) const noexcept -> bool = default;
+ };
+
+ [[nodiscard]] auto begin() const noexcept -> iterator
+ {
+ return iterator(__head_);
+ }
+
+ [[nodiscard]] auto end() const noexcept -> iterator
+ {
+ return iterator(nullptr);
+ }
+
+ private:
+ _Item* __head_ = nullptr;
+};
+} // namespace __slist
+
+using __slist::__intrusive_slist;
+
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__just.hpp b/include/sdbusplus/async/stdexec/__detail/__just.hpp
new file mode 100644
index 0000000..767bbd4
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__just.hpp
@@ -0,0 +1,119 @@
+/*
+ * 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 "__basic_sender.hpp"
+#include "__completion_signatures.hpp"
+#include "__execution_fwd.hpp"
+#include "__meta.hpp"
+#include "__receivers.hpp"
+#include "__type_traits.hpp"
+
+STDEXEC_PRAGMA_PUSH()
+STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.factories]
+namespace __just
+{
+template <class _JustTag>
+struct __impl : __sexpr_defaults
+{
+ using __tag_t = typename _JustTag::__tag_t;
+
+ static constexpr auto get_completion_signatures =
+ []<class _Sender>(_Sender&&, auto&&...) noexcept {
+ static_assert(sender_expr_for<_Sender, _JustTag>);
+ return completion_signatures<
+ __mapply<__qf<__tag_t>, __decay_t<__data_of<_Sender>>>>{};
+ };
+
+ static constexpr auto start =
+ []<class _State, class _Receiver>(_State& __state,
+ _Receiver& __rcvr) noexcept -> void {
+ __state.apply(
+ [&]<class... _Ts>(_Ts&... __ts) noexcept {
+ __tag_t()(static_cast<_Receiver&&>(__rcvr),
+ static_cast<_Ts&&>(__ts)...);
+ },
+ __state);
+ };
+};
+
+struct just_t
+{
+ using __tag_t = set_value_t;
+
+ template <__movable_value... _Ts>
+ STDEXEC_ATTRIBUTE((host, device))
+ auto operator()(_Ts&&... __ts) const
+ noexcept((__nothrow_decay_copyable<_Ts> && ...))
+ {
+ return __make_sexpr<just_t>(__tuple{static_cast<_Ts&&>(__ts)...});
+ }
+};
+
+struct just_error_t
+{
+ using __tag_t = set_error_t;
+
+ template <__movable_value _Error>
+ STDEXEC_ATTRIBUTE((host, device))
+ auto operator()(_Error&& __err) const
+ noexcept(__nothrow_decay_copyable<_Error>)
+ {
+ return __make_sexpr<just_error_t>(
+ __tuple{static_cast<_Error&&>(__err)});
+ }
+};
+
+struct just_stopped_t
+{
+ using __tag_t = set_stopped_t;
+
+ template <class _Tag = just_stopped_t>
+ STDEXEC_ATTRIBUTE((host, device))
+ auto operator()() const noexcept
+ {
+ return __make_sexpr<_Tag>(__tuple{});
+ }
+};
+} // namespace __just
+
+using __just::just_error_t;
+using __just::just_stopped_t;
+using __just::just_t;
+
+template <>
+struct __sexpr_impl<just_t> : __just::__impl<just_t>
+{};
+
+template <>
+struct __sexpr_impl<just_error_t> : __just::__impl<just_error_t>
+{};
+
+template <>
+struct __sexpr_impl<just_stopped_t> : __just::__impl<just_stopped_t>
+{};
+
+inline constexpr just_t just{};
+inline constexpr just_error_t just_error{};
+inline constexpr just_stopped_t just_stopped{};
+} // namespace stdexec
+
+STDEXEC_PRAGMA_POP()
diff --git a/include/sdbusplus/async/stdexec/__detail/__let.hpp b/include/sdbusplus/async/stdexec/__detail/__let.hpp
new file mode 100644
index 0000000..e72a452
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__let.hpp
@@ -0,0 +1,622 @@
+/*
+ * 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 "__diagnostics.hpp"
+#include "__domain.hpp"
+#include "__env.hpp"
+#include "__inline_scheduler.hpp"
+#include "__meta.hpp"
+#include "__receiver_ref.hpp"
+#include "__sender_adaptor_closure.hpp"
+#include "__senders.hpp"
+#include "__tag_invoke.hpp"
+#include "__transform_completion_signatures.hpp"
+#include "__transform_sender.hpp"
+#include "__variant.hpp"
+
+#include <exception>
+
+namespace stdexec
+{
+//////////////////////////////////////////////////////////////////////////////
+// [exec.let]
+namespace __let
+{
+// A dummy scheduler that is used by the metaprogramming below when the input
+// sender doesn't have a completion scheduler.
+struct __unknown_scheduler
+{
+ struct __env
+ {
+ static constexpr bool query(__is_scheduler_affine_t) noexcept
+ {
+ return true;
+ }
+
+ constexpr auto
+ query(get_completion_scheduler_t<set_value_t>) const noexcept
+ {
+ return __unknown_scheduler{};
+ }
+ };
+
+ struct __sender
+ {
+ using sender_concept = sender_t;
+
+ constexpr auto get_env() const noexcept -> __env
+ {
+ return __env();
+ }
+ };
+
+ auto schedule() const noexcept
+ {
+ return __sender();
+ }
+
+ bool operator==(const __unknown_scheduler&) const noexcept = default;
+};
+
+inline constexpr auto __get_rcvr =
+ [](auto& __op_state) noexcept -> decltype(auto) {
+ return (__op_state.__rcvr_);
+};
+
+inline constexpr auto __get_env =
+ [](auto& __op_state) noexcept -> decltype(auto) {
+ return __op_state.__state_.__get_env(__op_state.__rcvr_);
+};
+
+template <class _Set, class _Domain = dependent_domain>
+struct __let_t;
+
+template <class _Set>
+inline constexpr __mstring __in_which_let_msg{
+ "In stdexec::let_value(Sender, Function)..."};
+
+template <>
+inline constexpr __mstring __in_which_let_msg<set_error_t>{
+ "In stdexec::let_error(Sender, Function)..."};
+
+template <>
+inline constexpr __mstring __in_which_let_msg<set_stopped_t>{
+ "In stdexec::let_stopped(Sender, Function)..."};
+
+template <class _Set>
+using __on_not_callable = __callable_error<__in_which_let_msg<_Set>>;
+
+template <class _Receiver, class _Scheduler>
+struct __receiver_with_sched
+{
+ using receiver_concept = receiver_t;
+ _Receiver __rcvr_;
+ _Scheduler __sched_;
+
+ template <class... _As>
+ void set_value(_As&&... __as) noexcept
+ {
+ stdexec::set_value(static_cast<_Receiver&&>(__rcvr_),
+ static_cast<_As&&>(__as)...);
+ }
+
+ template <class _Error>
+ void set_error(_Error&& __err) noexcept
+ {
+ stdexec::set_error(static_cast<_Receiver&&>(__rcvr_),
+ static_cast<_Error&&>(__err));
+ }
+
+ void set_stopped() noexcept
+ {
+ stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr_));
+ }
+
+ auto get_env() const noexcept
+ {
+ return __env::__join(
+ prop{get_scheduler, __sched_},
+ __env::__without(stdexec::get_env(__rcvr_), get_domain));
+ }
+};
+
+template <class _Receiver, class _Scheduler>
+__receiver_with_sched(_Receiver, _Scheduler)
+ -> __receiver_with_sched<_Receiver, _Scheduler>;
+
+// If the input sender knows its completion scheduler, make it the current
+// scheduler in the environment seen by the result sender.
+template <class _Scheduler, class _Env>
+using __result_env_t =
+ __if_c<__is_scheduler_affine<schedule_result_t<_Scheduler>>, _Env,
+ __env::__join_t< //
+ prop<get_scheduler_t, _Scheduler>,
+ __env::__without_t<_Env, get_domain_t>>>;
+
+template <__mstring _Where, __mstring _What>
+struct _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_
+{};
+
+#if STDEXEC_NVHPC()
+template <class _Sender, class _Set, class... _Env>
+struct __bad_result_sender_
+{
+ using __t = __mexception<
+ _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_<
+ __in_which_let_msg<_Set>,
+ "The function must return a valid sender for the current environment"_mstr>,
+ _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>...>;
+};
+template <class _Sender, class _Set, class... _Env>
+using __bad_result_sender = __t<__bad_result_sender_<_Sender, _Set, _Env...>>;
+#else
+template <class _Sender, class _Set, class... _Env>
+using __bad_result_sender = __mexception<
+ _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_<
+ __in_which_let_msg<_Set>,
+ "The function must return a valid sender for the current environment"_mstr>,
+ _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>...>;
+#endif
+
+template <class _Sender, class... _Env>
+concept __potentially_valid_sender_in =
+ sender_in<_Sender, _Env...> || (sender<_Sender> && (sizeof...(_Env) == 0));
+
+template <class _Set, class _Sender, class... _Env>
+using __ensure_sender = //
+ __minvoke_if_c<__potentially_valid_sender_in<_Sender, _Env...>,
+ __q<__midentity>,
+ __mbind_back_q<__bad_result_sender, _Set, _Env...>, _Sender>;
+
+// A metafunction that computes the result sender type for a given set of
+// argument types
+template <class _Set, class _Fun, class _Sched, class... _Env>
+struct __result_sender_fn
+{
+ template <class... _Args>
+ using __f = //
+ __meval<
+ __ensure_sender, _Set,
+ __mcall<__mtry_catch_q<__call_result_t, __on_not_callable<_Set>>,
+ _Fun, __decay_t<_Args>&...>,
+ __result_env_t<_Sched, _Env>...>;
+};
+
+// The receiver that gets connected to the result sender is the input receiver,
+// possibly augmented with the input sender's completion scheduler (which is
+// where the result sender will be started).
+template <class _Receiver, class _Scheduler>
+using __result_receiver_t =
+ __if_c<__is_scheduler_affine<schedule_result_t<_Scheduler>>, _Receiver,
+ __receiver_with_sched<_Receiver, _Scheduler>>;
+
+template <class _ResultSender, class _Scheduler, class... _Env>
+using __receiver_ref_t = //
+ __meval<__any_::__receiver_ref,
+ __completion_signatures_of_t<_ResultSender,
+ __result_env_t<_Scheduler, _Env>...>,
+ __result_env_t<_Scheduler, _Env>...>;
+
+template <class _ResultSender, class _Scheduler, class _Receiver>
+concept __needs_receiver_ref =
+ __nothrow_connectable<
+ _ResultSender,
+ __receiver_ref_t<_ResultSender, _Scheduler, env_of_t<_Receiver>>> &&
+ !__nothrow_connectable<_ResultSender,
+ __result_receiver_t<_Receiver, _Scheduler>>;
+
+template <class _Sender, class _Receiver>
+using __nothrow_connectable_t =
+ __mbool<__nothrow_connectable<_Sender, _Receiver>>;
+
+template <class _ResultSender, class _Scheduler, class... _Env>
+using __nothrow_connectable_receiver_ref_t =
+ __meval<__nothrow_connectable_t, _ResultSender,
+ __receiver_ref_t<_ResultSender, _Scheduler, _Env...>>;
+
+template <class _ResultSender, class _Scheduler, class _Receiver>
+using __checked_result_receiver_t = //
+ __if_c<__needs_receiver_ref<_ResultSender, _Scheduler, _Receiver>,
+ __receiver_ref_t<_ResultSender, _Scheduler, env_of_t<_Receiver>>,
+ __result_receiver_t<_Receiver, _Scheduler>>;
+
+template <class _ResultSender, class _Scheduler, class _Receiver>
+using __op_state_t = connect_result_t<
+ _ResultSender,
+ __checked_result_receiver_t<_ResultSender, _Scheduler, _Receiver>>;
+
+template <class _SetTag, class _Fun, class _Sched, class... _Env>
+struct __transform_signal_fn
+{
+ template <class... _Args>
+ using __nothrow_connect =
+ __mand<__mbool<(__nothrow_decay_copyable<_Args> && ...) &&
+ __nothrow_callable<_Fun, _Args...>>,
+ __nothrow_connectable_receiver_ref_t<
+ __mcall<__result_sender_fn<_SetTag, _Fun, _Sched, _Env...>,
+ _Args...>,
+ _Sched, _Env...>>;
+
+ template <class... _Args>
+ using __f = //
+ __mcall<__mtry_q<__concat_completion_signatures>,
+ __completion_signatures_of_t<
+ __mcall<__result_sender_fn<_SetTag, _Fun, _Sched, _Env...>,
+ _Args...>,
+ __result_env_t<_Sched, _Env>...>,
+ __eptr_completion_if_t<__nothrow_connect<_Args...>>>;
+};
+
+template <class _Sender, class _Set>
+using __completion_sched =
+ __query_result_or_t<get_completion_scheduler_t<_Set>, env_of_t<_Sender>,
+ __unknown_scheduler>;
+
+template <class _LetTag, class _Fun, class _CvrefSender, class... _Env>
+using __completions = //
+ __gather_completion_signatures<
+ __completion_signatures_of_t<_CvrefSender, _Env...>, __t<_LetTag>,
+ __transform_signal_fn<__t<_LetTag>, _Fun,
+ __completion_sched<_CvrefSender, __t<_LetTag>>,
+ _Env...>::template __f,
+ __sigs::__default_completion,
+ __mtry_q<__concat_completion_signatures>::__f>;
+
+template <__mstring _Where, __mstring _What>
+struct _NO_COMMON_DOMAIN_
+{};
+
+template <class _Set>
+using __no_common_domain_t = //
+ _NO_COMMON_DOMAIN_<
+ __in_which_let_msg<_Set>,
+ "The senders returned by Function do not all share a common domain"_mstr>;
+
+template <class _Set>
+struct __try_common_domain_fn
+{
+ struct __error_fn
+ {
+ template <class... _Senders>
+ using __f = __mexception<__no_common_domain_t<_Set>,
+ _WITH_SENDERS_<_Senders...>>;
+ };
+
+ template <class... _Senders>
+ using __f = __mcall<__mtry_catch_q<__domain::__common_domain_t, __error_fn>,
+ _Senders...>;
+};
+
+// Compute all the domains of all the result senders and make sure they're all
+// the same
+template <class _Set, class _Child, class _Fun, class _Env, class _Sched>
+using __result_domain_t = //
+ __gather_completions<_Set, __completion_signatures_of_t<_Child, _Env>,
+ __result_sender_fn<_Set, _Fun, _Sched, _Env>,
+ __try_common_domain_fn<_Set>>;
+
+template <class _LetTag, class _Env>
+auto __mk_transform_env_fn(_Env&& __env) noexcept
+{
+ using _Set = __t<_LetTag>;
+ return [&]<class _Fun, class _Child>(__ignore, _Fun&&,
+ _Child&& __child) -> decltype(auto) {
+ using __completions_t = __completion_signatures_of_t<_Child, _Env>;
+ if constexpr (__merror<__completions_t>)
+ {
+ return __completions_t();
+ }
+ else
+ {
+ using _Scheduler = __completion_sched<_Child, _Set>;
+ if constexpr (__is_scheduler_affine<schedule_result_t<_Scheduler>>)
+ {
+ return (__env);
+ }
+ else
+ {
+ return __env::__join(
+ prop{get_scheduler, get_completion_scheduler<_Set>(
+ stdexec::get_env(__child))},
+ __env::__without(static_cast<_Env&&>(__env), get_domain));
+ }
+ }
+ };
+}
+
+template <class _LetTag, class _Env>
+auto __mk_transform_sender_fn(_Env&&) noexcept
+{
+ using _Set = __t<_LetTag>;
+
+ return
+ []<class _Fun, class _Child>(__ignore, _Fun&& __fun, _Child&& __child) {
+ using __completions_t = __completion_signatures_of_t<_Child, _Env>;
+
+ if constexpr (__merror<__completions_t>)
+ {
+ return __completions_t();
+ }
+ else
+ {
+ using _Sched = __completion_sched<_Child, _Set>;
+ using _Domain = __result_domain_t<_Set, _Child, _Fun, _Env, _Sched>;
+
+ if constexpr (__merror<_Domain>)
+ {
+ return _Domain();
+ }
+ else if constexpr (same_as<_Domain, dependent_domain>)
+ {
+ using _Domain2 = __late_domain_of_t<_Child, _Env>;
+ return __make_sexpr<__let_t<_Set, _Domain2>>(
+ static_cast<_Fun&&>(__fun), static_cast<_Child&&>(__child));
+ }
+ else
+ {
+ static_assert(!same_as<_Domain, __unknown_scheduler>);
+ return __make_sexpr<__let_t<_Set, _Domain>>(
+ static_cast<_Fun&&>(__fun), static_cast<_Child&&>(__child));
+ }
+ }
+ };
+}
+
+template <class _Receiver, class _Fun, class _Set, class _Sched>
+struct __op_state_for
+{
+ template <class... _Args>
+ using __f = __op_state_t<
+ __mcall<__result_sender_fn<_Set, _Fun, _Sched, env_of_t<_Receiver>>,
+ _Args...>,
+ _Sched, _Receiver>;
+};
+
+template <class _Receiver, class _Fun, class _Set, class _Sched,
+ class... _Tuples>
+struct __let_state
+{
+ using __fun_t = _Fun;
+ using __sched_t = _Sched;
+ using __env_t = __result_env_t<_Sched, env_of_t<_Receiver>>;
+ using __result_variant = __variant_for<__monostate, _Tuples...>;
+ using __op_state_variant = //
+ __variant_for<__monostate,
+ __mapply<__op_state_for<_Receiver, _Fun, _Set, _Sched>,
+ _Tuples>...>;
+
+ template <class _ResultSender, class _OpState>
+ auto __get_result_receiver(const _ResultSender&, _OpState& __op_state)
+ -> decltype(auto)
+ {
+ if constexpr (__needs_receiver_ref<_ResultSender, _Sched, _Receiver>)
+ {
+ using __receiver_ref =
+ __receiver_ref_t<_ResultSender, _Sched, env_of_t<_Receiver>>;
+ return __receiver_ref{__op_state, __let::__get_env,
+ __let::__get_rcvr};
+ }
+ else
+ {
+ _Receiver& __rcvr = __op_state.__rcvr_;
+ if constexpr (__is_scheduler_affine<schedule_result_t<_Sched>>)
+ {
+ return static_cast<_Receiver&&>(__rcvr);
+ }
+ else
+ {
+ return __receiver_with_sched{static_cast<_Receiver&&>(__rcvr),
+ this->__sched_};
+ }
+ }
+ }
+
+ auto __get_env(const _Receiver& __rcvr) const noexcept -> __env_t
+ {
+ if constexpr (__is_scheduler_affine<schedule_result_t<_Sched>>)
+ {
+ return stdexec::get_env(__rcvr);
+ }
+ else
+ {
+ return __env::__join(
+ prop{get_scheduler, __sched_},
+ __env::__without(stdexec::get_env(__rcvr), get_domain));
+ }
+ }
+
+ STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Fun __fun_;
+ STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Sched __sched_;
+ __result_variant __args_{};
+ __op_state_variant __op_state3_{};
+};
+
+template <class _Set, class _Domain>
+struct __let_t
+{
+ using __domain_t = _Domain;
+ using __t = _Set;
+
+ template <sender _Sender, __movable_value _Fun>
+ auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender
+ auto
+ {
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<__let_t<_Set>>(static_cast<_Fun&&>(__fun),
+ static_cast<_Sender&&>(__sndr)));
+ }
+
+ template <class _Fun>
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto operator()(_Fun __fun) const -> __binder_back<__let_t, _Fun>
+ {
+ return {{static_cast<_Fun&&>(__fun)}, {}, {}};
+ }
+
+ using _Sender = __1;
+ using _Function = __0;
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(__let_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(const _Sender&)),
+ _Sender, _Function),
+ tag_invoke_t(__let_t, _Sender, _Function)>;
+
+ template <sender_expr_for<__let_t<_Set>> _Sender, class _Env>
+ static auto transform_env(_Sender&& __sndr, const _Env& __env)
+ -> decltype(auto)
+ {
+ return __sexpr_apply(static_cast<_Sender&&>(__sndr),
+ __mk_transform_env_fn<__let_t<_Set>>(__env));
+ }
+
+ template <sender_expr_for<__let_t<_Set>> _Sender, class _Env>
+ requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
+ static auto transform_sender(_Sender&& __sndr, const _Env& __env)
+ -> decltype(auto)
+ {
+ return __sexpr_apply(static_cast<_Sender&&>(__sndr),
+ __mk_transform_sender_fn<__let_t<_Set>>(__env));
+ }
+};
+
+template <class _Set, class _Domain>
+struct __let_impl : __sexpr_defaults
+{
+ static constexpr auto get_attrs = //
+ []<class _Child>(__ignore, const _Child& __child) noexcept {
+ return __env::__join(prop{get_domain, _Domain()},
+ stdexec::get_env(__child));
+ };
+
+ static constexpr auto get_completion_signatures = //
+ []<class _Self, class... _Env>(_Self&&, _Env&&...) noexcept
+ -> __completions<__let_t<_Set, _Domain>, __data_of<_Self>,
+ __child_of<_Self>, _Env...> {
+ static_assert(sender_expr_for<_Self, __let_t<_Set, _Domain>>);
+ return {};
+ };
+
+ static constexpr auto get_state = //
+ []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver&) {
+ static_assert(sender_expr_for<_Sender, __let_t<_Set, _Domain>>);
+ using _Fun = __data_of<_Sender>;
+ using _Child = __child_of<_Sender>;
+ using _Sched = __completion_sched<_Child, _Set>;
+ using __mk_let_state =
+ __mbind_front_q<__let_state, _Receiver, _Fun, _Set, _Sched>;
+
+ using __let_state_t =
+ __gather_completions_of<_Set, _Child, env_of_t<_Receiver>,
+ __q<__decayed_tuple>, __mk_let_state>;
+
+ return __sndr.apply(static_cast<_Sender&&>(__sndr),
+ [&]<class _Fn, class _Child>(__ignore, _Fn&& __fn,
+ _Child&& __child) {
+ _Sched __sched = query_or(get_completion_scheduler<_Set>,
+ stdexec::get_env(__child),
+ __unknown_scheduler());
+ return __let_state_t{static_cast<_Fn&&>(__fn), __sched};
+ });
+ };
+
+ template <class _State, class _OpState, class... _As>
+ static void __bind_(_State& __state, _OpState& __op_state, _As&&... __as)
+ {
+ auto& __args = __state.__args_.emplace_from(
+ __tup::__mktuple, static_cast<_As&&>(__as)...);
+ auto __sndr2 = __args.apply(std::move(__state.__fun_), __args);
+ auto __rcvr2 = __state.__get_result_receiver(__sndr2, __op_state);
+ auto& __op2 = __state.__op_state3_.emplace_from(
+ stdexec::connect, std::move(__sndr2), std::move(__rcvr2));
+ stdexec::start(__op2);
+ }
+
+ template <class _OpState, class... _As>
+ static void __bind(_OpState& __op_state, _As&&... __as) noexcept
+ {
+ using _State = decltype(__op_state.__state_);
+ using _Receiver = decltype(__op_state.__rcvr_);
+ using _Fun = typename _State::__fun_t;
+ using _Sched = typename _State::__sched_t;
+ using _ResultSender =
+ __mcall<__result_sender_fn<_Set, _Fun, _Sched, env_of_t<_Receiver>>,
+ _As...>;
+
+ _State& __state = __op_state.__state_;
+ _Receiver& __rcvr = __op_state.__rcvr_;
+
+ if constexpr ((__nothrow_decay_copyable<_As> && ...) &&
+ __nothrow_callable<_Fun, _As...> &&
+ __v<__nothrow_connectable_receiver_ref_t<
+ _ResultSender, _Sched, env_of_t<_Receiver>>>)
+ {
+ __bind_(__state, __op_state, static_cast<_As&&>(__as)...);
+ }
+ else
+ {
+ try
+ {
+ __bind_(__state, __op_state, static_cast<_As&&>(__as)...);
+ }
+ catch (...)
+ {
+ using _Receiver = decltype(__op_state.__rcvr_);
+ stdexec::set_error(static_cast<_Receiver&&>(__rcvr),
+ std::current_exception());
+ }
+ }
+ }
+
+ static constexpr auto complete = //
+ []<class _OpState, class _Tag, class... _As>(
+ __ignore, _OpState& __op_state, _Tag,
+ _As&&... __as) noexcept -> void {
+ if constexpr (__same_as<_Tag, _Set>)
+ {
+ __bind(__op_state, static_cast<_As&&>(__as)...);
+ }
+ else
+ {
+ using _Receiver = decltype(__op_state.__rcvr_);
+ _Tag()(static_cast<_Receiver&&>(__op_state.__rcvr_),
+ static_cast<_As&&>(__as)...);
+ }
+ };
+};
+} // namespace __let
+
+using let_value_t = __let::__let_t<set_value_t>;
+inline constexpr let_value_t let_value{};
+
+using let_error_t = __let::__let_t<set_error_t>;
+inline constexpr let_error_t let_error{};
+
+using let_stopped_t = __let::__let_t<set_stopped_t>;
+inline constexpr let_stopped_t let_stopped{};
+
+template <class _Set, class _Domain>
+struct __sexpr_impl<__let::__let_t<_Set, _Domain>> :
+ __let::__let_impl<_Set, _Domain>
+{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__meta.hpp b/include/sdbusplus/async/stdexec/__detail/__meta.hpp
index a926d68..a309bec 100644
--- a/include/sdbusplus/async/stdexec/__detail/__meta.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__meta.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
@@ -18,8 +18,10 @@
#include "__concepts.hpp"
#include "__config.hpp"
#include "__type_traits.hpp"
+#include "__utility.hpp"
#include <cassert>
+#include <compare>
#include <cstddef>
#include <exception>
#include <type_traits>
@@ -27,61 +29,18 @@
namespace stdexec
{
-
-template <class...>
-struct __undefined;
-
-struct __
-{};
-
-struct __ignore
-{
- __ignore() = default;
-
- STDEXEC_ATTRIBUTE((always_inline))
- constexpr __ignore(auto&&...) noexcept {}
-};
-
-struct __none_such
-{};
-
-namespace
-{
-struct __anon
-{};
-} // namespace
-
-struct __immovable
-{
- __immovable() = default;
-
- private:
- STDEXEC_IMMOVABLE(__immovable);
-};
-
-struct __move_only
-{
- __move_only() = default;
-
- __move_only(__move_only&&) noexcept = default;
- auto operator=(__move_only&&) noexcept -> __move_only& = default;
-
- __move_only(const __move_only&) = delete;
- auto operator=(const __move_only&) -> __move_only& = delete;
-};
-
template <class _Tp>
using __t = typename _Tp::__t;
-template <bool _Bp>
-using __mbool = std::bool_constant<_Bp>;
-
template <class _Ty>
struct __mtype
{
using __t = _Ty;
};
+template <class...>
+inline constexpr bool __mnever = false;
+
template <auto _Value>
using __mtypeof = decltype(_Value);
@@ -91,14 +50,49 @@
template <class _Tp>
using __midentity = _Tp;
+template <auto _Np>
+struct __mconstant
+{
+ using type = __mconstant;
+ using value_type = __mtypeof<_Np>;
+ static constexpr auto value = _Np;
+
+ constexpr operator value_type() const noexcept
+ {
+ return value;
+ }
+
+ constexpr auto operator()() const noexcept -> value_type
+ {
+ return value;
+ }
+};
+
+// Because of nvc++ nvbugs#4679848, we can't make __mbool a simple alias for
+// __mconstant, and because of nvc++ nvbugs#4668709 it can't be a simple alias
+// for std::bool_constant, either. :-( template <bool _Bp> using __mbool =
+// __mconstant<_Bp>;
+
+template <bool _Bp>
+struct __mbool : std::bool_constant<_Bp>
+{};
+
+using __mtrue = __mbool<true>;
+using __mfalse = __mbool<false>;
+
+// nvbugs#4679848 and nvbugs#4668709 also preclude __mconstant from representing
+// a compile-time size_t.
+enum class __muchar : unsigned char
+{
+};
+
+#if STDEXEC_MSVC()
template <std::size_t _Np>
-using __msize_t = char[_Np + 1];
-
-template <auto _Np>
-struct __mconstant_;
-
-template <auto _Np>
-using __mconstant = __mconstant_<_Np>*;
+using __msize_t = __mconstant<_Np>;
+#else
+template <std::size_t _Np>
+using __msize_t = __muchar (*)[_Np + 1]; // +1 to avoid zero-size array
+#endif
template <class _Tp, class _Up>
using __mfirst = _Tp;
@@ -106,6 +100,9 @@
template <class _Tp, class _Up>
using __msecond = _Up;
+template <class...>
+struct __undefined;
+
template <class _Tp>
extern const __undefined<_Tp> __v;
@@ -113,6 +110,8 @@
requires __typename<__mtypeof<_Tp::value>>
inline constexpr auto __v<_Tp> = _Tp::value;
+// These specializations exist because instantiating a variable template is
+// cheaper than instantiating a class template.
template <class _Tp, class _Up>
inline constexpr bool __v<std::is_same<_Tp, _Up>> = false;
@@ -123,98 +122,171 @@
inline constexpr _Tp __v<std::integral_constant<_Tp, _Ip>> = _Ip;
template <auto _Np>
-inline constexpr __mtypeof<_Np> __v<__mconstant<_Np>> = _Np;
-
-template <std::size_t _Ip>
-inline constexpr std::size_t __v<char[_Ip]> = _Ip - 1;
-
-template <std::size_t... _Is>
-using __indices = std::index_sequence<_Is...>*;
+inline constexpr auto __v<__mconstant<_Np>> = _Np;
template <std::size_t _Np>
-using __make_indices = std::make_index_sequence<_Np>*;
+inline constexpr std::size_t __v<__muchar (*)[_Np]> =
+ _Np - 1; // see definition of __msize_t
+
+namespace __pack
+{
+template <std::size_t... _Is>
+struct __t;
+} // namespace __pack
+
+template <std::size_t... _Is>
+using __indices = __pack::__t<_Is...>*;
+
+#if STDEXEC_MSVC()
+namespace __pack
+{
+template <class _Ty, _Ty... _Is>
+struct __idx;
+
+template <class>
+extern int __mkidx;
+
+template <std::size_t... _Is>
+extern __indices<_Is...> __mkidx<__idx<std::size_t, _Is...>>;
+} // namespace __pack
+
+template <std::size_t _Np>
+using __make_indices = //
+ decltype(__pack::__mkidx<
+ __make_integer_seq<__pack::__idx, std::size_t, _Np>>);
+#elif STDEXEC_HAS_BUILTIN(__make_integer_seq)
+namespace __pack
+{
+template <class _Ty, _Ty... _Is>
+using __idx = __indices<_Is...>;
+} // namespace __pack
+
+template <std::size_t _Np>
+using __make_indices = __make_integer_seq<__pack::__idx, std::size_t, _Np>;
+#elif STDEXEC_HAS_BUILTIN(__integer_pack)
+namespace __pack
+{
+template <std::size_t _Np>
+extern __indices<__integer_pack(_Np)...> __make_indices;
+} // namespace __pack
+
+template <std::size_t _Np>
+using __make_indices = decltype(__pack::__make_indices<_Np>);
+#else
+namespace __pack
+{
+template <std::size_t... _Is>
+auto __mk_indices(__indices<0, _Is...>) -> __indices<_Is...>;
+
+template <std::size_t _Np, class = char[_Np], std::size_t... _Is>
+auto __mk_indices(__indices<_Np, _Is...>)
+ -> decltype(__mk_indices(__indices<_Np - 1, 0, (_Is + 1)...>{}));
+} // namespace __pack
+
+template <std::size_t _Np>
+using __make_indices = decltype(__pack::__mk_indices(__indices<_Np>{}));
+#endif
template <class... _Ts>
using __indices_for = __make_indices<sizeof...(_Ts)>;
-template <class _Char>
-concept __mchar = __same_as<_Char, char>;
+STDEXEC_PRAGMA_PUSH()
+STDEXEC_PRAGMA_IGNORE_MSVC(4293)
+
+constexpr std::size_t __mpow2(std::size_t __size) noexcept
+{
+ --__size;
+ __size |= __size >> 1;
+ __size |= __size >> 2;
+ __size |= __size >> 4;
+ __size |= __size >> 8;
+ if constexpr (sizeof(__size) >= 4)
+ __size |= __size >> 16;
+ if constexpr (sizeof(__size) >= 8)
+ __size |= __size >> 32;
+ return ++__size;
+}
+
+STDEXEC_PRAGMA_POP()
template <std::size_t _Len>
-class __mstring
+struct __mstring
{
- template <std::size_t... _Is>
- constexpr __mstring(const char (&__str)[_Len], __indices<_Is...>) noexcept :
- __what_{__str[_Is]...}
+#if STDEXEC_NVHPC()
+ template <std::size_t _Ny, std::size_t... _Is>
+ constexpr __mstring(const char (&__str)[_Ny], __indices<_Is...>) noexcept :
+ __what_{(_Is < _Ny ? __str[_Is] : '\0')...}
{}
- public:
- constexpr __mstring(const char (&__str)[_Len]) noexcept :
+ template <std::size_t _Ny>
+ constexpr __mstring(const char (&__str)[_Ny], int = 0) noexcept :
__mstring{__str, __make_indices<_Len>{}}
{}
-
- template <__mchar... _Char>
- requires(sizeof...(_Char) == _Len)
- constexpr __mstring(_Char... __chars) noexcept : __what_{__chars...}
- {}
+#else
+ template <std::size_t _Ny>
+ constexpr __mstring(const char (&__str)[_Ny], int = 0) noexcept : __what_{}
+ {
+ for (auto __i = 0ull; char __ch : __str)
+ {
+ __what_[__i++] = __ch;
+ }
+ }
+#endif
static constexpr auto __length() noexcept -> std::size_t
{
return _Len;
}
- template <std::size_t... _Is>
- constexpr auto __equal(__mstring __other, __indices<_Is...>) const noexcept
+ constexpr auto operator==(const __mstring&) const noexcept
+ -> bool = default;
+
+ template <std::size_t _OtherLen>
+ constexpr auto operator==(const __mstring<_OtherLen>&) const noexcept
-> bool
{
- return ((__what_[_Is] == __other.__what_[_Is]) && ...);
+ return false;
}
- constexpr auto operator==(__mstring __other) const noexcept -> bool
+#if !STDEXEC_NVHPC()
+ constexpr auto operator<=>(const __mstring&) const noexcept
+ -> std::strong_ordering = default;
+#endif
+
+ template <std::size_t _OtherLen>
+ constexpr auto
+ operator<=>(const __mstring<_OtherLen>& __other) const noexcept
+ -> std::strong_ordering
{
- return __equal(__other, __make_indices<_Len>());
+ constexpr std::size_t __len = _Len < _OtherLen ? _Len : _OtherLen;
+ for (std::size_t __i = 0; __i < __len; ++__i)
+ {
+ auto __cmp = (__what_[__i] <=> __other.__what_[__i]);
+ if (__cmp != 0)
+ {
+ return __cmp;
+ }
+ }
+ if constexpr (_Len == _OtherLen)
+ {
+ return std::strong_ordering::equal;
+ }
+ return (_Len < _OtherLen) ? std::strong_ordering::less
+ : std::strong_ordering::greater;
}
- const char __what_[_Len];
+ char __what_[_Len];
};
template <std::size_t _Len>
__mstring(const char (&__str)[_Len]) -> __mstring<_Len>;
+template <std::size_t _Len>
+__mstring(const char (&__str)[_Len], int) -> __mstring<__mpow2(_Len)>;
+
STDEXEC_PRAGMA_PUSH()
STDEXEC_PRAGMA_IGNORE_GNU("-Wuser-defined-literals")
-#if STDEXEC_NVHPC() && (__EDG_VERSION__ < 604)
-// Use a non-standard extension for older nvc++ releases
-template <__mchar _Char, _Char... _Str>
-[[deprecated("Use _mstr instead")]] constexpr __mstring<sizeof...(_Str)>
- operator""__csz() noexcept
-{
- return {_Str...};
-}
-
-// Use a non-standard extension for older nvc++ releases
-template <__mchar _Char, _Char... _Str>
-constexpr __mstring<sizeof...(_Str)> operator""_mstr() noexcept
-{
- return {_Str...};
-}
-#elif STDEXEC_NVHPC() && (__EDG_VERSION__ < 605)
-// This is to work around an unfiled (by me) EDG bug that fixed in build 605
-template <__mstring _Str>
-[[deprecated("Use _mstr instead")]] constexpr const __mtypeof<_Str>
- operator""__csz() noexcept
-{
- return _Str;
-}
-
-// This is to work around an unfiled (by me) EDG bug that fixed in build 605
-template <__mstring _Str>
-constexpr const __mtypeof<_Str> operator""_mstr() noexcept
-{
- return _Str;
-}
-#else
// Use a standard user-defined string literal template
template <__mstring _Str>
[[deprecated("Use _mstr instead")]] constexpr auto operator""__csz() noexcept
@@ -229,10 +301,19 @@
{
return _Str;
}
-#endif
STDEXEC_PRAGMA_POP()
+template <class T>
+constexpr auto __mnameof() noexcept
+{
+#if STDEXEC_MSVC()
+ return __mstring{__FUNCSIG__, 0};
+#else
+ return __mstring{__PRETTY_FUNCTION__, 0};
+#endif
+}
+
using __msuccess = int;
template <class _What, class... _With>
@@ -245,7 +326,7 @@
auto operator,(__msuccess) const noexcept -> _ERROR_;
};
-template <__mstring _What>
+template <__mstring... _What>
struct _WHAT_
{};
@@ -265,15 +346,15 @@
using __disp = decltype((__msuccess(), ..., __ok_t<_Ts>()));
template <class _Arg>
-concept __ok = __same_as<__ok_t<_Arg>, __msuccess>;
+concept __ok = STDEXEC_IS_SAME(__ok_t<_Arg>, __msuccess);
template <class _Arg>
-concept __merror = !__ok<_Arg>;
+concept __merror = !STDEXEC_IS_SAME(__ok_t<_Arg>, __msuccess);
template <class... _Args>
-concept _Ok = (__ok<_Args> && ...);
+concept _Ok = (STDEXEC_IS_SAME(__ok_t<_Args>, __msuccess) && ...);
-template <bool _AllOK>
+template <bool _ArgsOK, bool _FnOK = true>
struct __i;
#if STDEXEC_NVHPC()
@@ -281,7 +362,9 @@
// nvc++ does not. So we memoize the type computations by
// indirecting through a class template specialization.
template <template <class...> class _Fn, class... _Args>
-using __meval__ = typename __i<_Ok<_Args...>>::template __g<_Fn, _Args...>;
+using __meval__ = //
+ typename __i<_Ok<_Args...>> //
+ ::template __g<_Fn, _Args...>; //
template <template <class...> class _Fn, class... _Args>
struct __meval_
@@ -298,7 +381,10 @@
using __meval = __t<__meval_<_Fn, _Args...>>;
template <class _Fn, class... _Args>
-using __minvoke__ = typename __i<_Ok<_Fn>>::template __h<_Fn, _Args...>;
+using __minvoke__ = //
+ typename __i<_Ok<_Args...>, _Ok<_Fn>> //
+ ::template __f<_Fn> //
+ ::template __f<_Args...>; //
template <class _Fn, class... _Args>
struct __minvoke_
@@ -317,40 +403,89 @@
#else
template <template <class...> class _Fn, class... _Args>
-using __meval = typename __i<_Ok<_Args...>>::template __g<_Fn, _Args...>;
+using __meval = //
+ typename __i<_Ok<_Args...>> //
+ ::template __g<_Fn, _Args...>; //
template <class _Fn, class... _Args>
-using __minvoke = typename __i<_Ok<_Fn>>::template __h<_Fn, _Args...>;
+using __minvoke = //
+ typename __i<_Ok<_Args...>, _Ok<_Fn>> //
+ ::template __f<_Fn> //
+ ::template __f<_Args...>; //
#endif
-template <bool _AllOK>
-struct __i
+template <class _Fn, class... _Args>
+using __mcall = typename _Fn::template __f<_Args...>;
+
+struct __disp_q
+{
+ template <class... _Args>
+ using __f = __disp<_Args...>;
+};
+
+template <>
+struct __i<true, true>
{
template <template <class...> class _Fn, class... _Args>
using __g = _Fn<_Args...>;
- template <class _Fn, class... _Args>
- using __h = __meval<_Fn::template __f, _Args...>;
+ template <class _Fn>
+ using __f = _Fn;
};
template <>
-struct __i<false>
+struct __i<false, true>
{
template <template <class...> class, class... _Args>
using __g = __disp<_Args...>;
- template <class _Fn, class...>
- using __h = _Fn;
+ template <class>
+ using __f = __disp_q;
+};
+
+template <bool _ArgsOK>
+struct __i<_ArgsOK, false>
+{
+ template <class _Fn>
+ using __f = _Fn;
};
template <template <class...> class _Fn>
struct __q
{
template <class... _Args>
- using __f = __meval<_Fn, _Args...>;
+ using __f = typename __i<_Ok<_Args...>>::template __g<_Fn, _Args...>;
};
+template <template <class...> class _Fn>
+struct __qq
+{
+ template <class... _Args>
+ using __f = _Fn<_Args...>;
+};
+
+template <template <class> class _Fn>
+struct __q1
+{
+ template <class _Ty>
+ using __f = _Fn<_Ty>;
+};
+
+template <template <class, class> class _Fn>
+struct __q2
+{
+ template <class _Ty, class _Uy>
+ using __f = _Fn<_Ty, _Uy>;
+};
+
+template <template <class...> class _Fn>
+using __mtry_q = __q<_Fn>;
+
+template <class _Fn>
+struct __mtry : __mtry_q<_Fn::template __f>
+{};
+
template <template <class...> class _Fn, class... _Front>
struct __mbind_front_q
{
@@ -385,12 +520,12 @@
__ok<__minvoke<_Fn, _Args...>>;
template <class _Fn, class... _Args>
-struct __force_minvoke_
+struct __minvoke_force_
{
using __t = __minvoke<_Fn, _Args...>;
};
template <class _Fn, class... _Args>
-using __force_minvoke = __t<__force_minvoke_<_Fn, _Args...>>;
+using __minvoke_force = __t<__minvoke_force_<_Fn, _Args...>>;
template <class _Fn, class... _Args>
struct __mdefer_
@@ -527,8 +662,11 @@
template <class _Ty, class... _Default>
using __msuccess_or_t = __if_c<__ok<_Ty>, _Ty, _Default...>;
+template <class _Ty, class... _Default>
+using __merror_or_t = __if_c<__merror<_Ty>, _Ty, _Default...>;
+
template <class _Fn, class _Continuation = __q<__types>>
-struct __transform
+struct __mtransform
{
template <class... _Args>
using __f = __minvoke<_Continuation, __minvoke<_Fn, _Args>...>;
@@ -557,102 +695,97 @@
__minvoke<__mfold_right_<sizeof...(_Args) == 0>, _Fn, _Init, _Args...>;
};
-template <class _Continuation, class... _As>
-struct __mconcat_
-{};
-
-template <class _Continuation, class... _As>
- requires(sizeof...(_As) == 0) && __minvocable<_Continuation, _As...>
-struct __mconcat_<_Continuation, _As...>
+template <bool>
+struct __mfold_left_
{
- using __t = __minvoke<_Continuation, _As...>;
+ template <class _Fn, class _State, class _Head, class... _Tail>
+ using __f = __minvoke<
+ _Fn,
+ __mcall<__mfold_left_<sizeof...(_Tail) == 0>, _Fn, _State, _Tail...>,
+ _Head>;
};
-template <class _Continuation, template <class...> class _Ap, class... _As>
- requires __minvocable<_Continuation, _As...>
-struct __mconcat_<_Continuation, _Ap<_As...>>
-{
- using __t = __minvoke<_Continuation, _As...>;
+template <>
+struct __mfold_left_<true>
+{ // empty pack
+ template <class _Fn, class _State, class...>
+ using __f = _State;
};
-template < //
- class _Continuation, //
- template <class...> class _Ap,
- class... _As, //
- template <class...> class _Bp, class... _Bs>
- requires __minvocable<_Continuation, _As..., _Bs...>
-struct __mconcat_<_Continuation, _Ap<_As...>, _Bp<_Bs...>>
-{
- using __t = __minvoke<_Continuation, _As..., _Bs...>;
-};
-
-template < //
- class _Continuation, //
- template <class...> class _Ap,
- class... _As, //
- template <class...> class _Bp,
- class... _Bs, //
- template <class...> class _Cp, class... _Cs>
- requires __minvocable<_Continuation, _As..., _Bs..., _Cs...>
-struct __mconcat_<_Continuation, _Ap<_As...>, _Bp<_Bs...>, _Cp<_Cs...>>
-{
- using __t = __minvoke<_Continuation, _As..., _Bs..., _Cs...>;
-};
-
-template < //
- class _Continuation, //
- template <class...> class _Ap,
- class... _As, //
- template <class...> class _Bp,
- class... _Bs, //
- template <class...> class _Cp,
- class... _Cs, //
- template <class...> class _Dp,
- class... _Ds, //
- class... _Tail>
-struct __mconcat_<_Continuation, _Ap<_As...>, _Bp<_Bs...>, _Cp<_Cs...>,
- _Dp<_Ds...>, _Tail...> :
- __mconcat_<_Continuation, __types<_As..., _Bs..., _Cs..., _Ds...>, _Tail...>
-{};
-
-template <class _Continuation = __q<__types>>
-struct __mconcat
+template <class _Init, class _Fn>
+struct __mfold_left
{
template <class... _Args>
- using __f = __t<__mconcat_<_Continuation, _Args...>>;
+ using __f =
+ __minvoke<__mfold_left_<sizeof...(_Args) == 0>, _Fn, _Init, _Args...>;
};
template <class _Fn>
-struct __curry
+struct __mcurry
{
template <class... _Ts>
using __f = __minvoke<_Fn, _Ts...>;
};
-template <class _Fn, class _Tp>
-struct __uncurry_;
+template <class _Tp>
+struct __muncurry_;
-template <__merror _Fn, class _Tp>
-struct __uncurry_<_Fn, _Tp>
+template <template <class...> class _Ap, class... _As>
+struct __muncurry_<_Ap<_As...>>
{
- using __t = _Fn;
+ template <class _Fn>
+ using __f = __minvoke<_Fn, _As...>;
};
-template <class _Fn, template <class...> class _Ap, class... _As>
- requires __minvocable<_Fn, _As...>
-struct __uncurry_<_Fn, _Ap<_As...>>
+template <class _What, class... _With>
+struct __muncurry_<_ERROR_<_What, _With...>>
{
- using __t = __minvoke<_Fn, _As...>;
+ template <class _Fn>
+ using __f = _ERROR_<_What, _With...>;
};
template <class _Fn>
-struct __uncurry
+struct __muncurry
{
template <class _Tp>
- using __f = __t<__uncurry_<_Fn, _Tp>>;
+ using __f = typename __muncurry_<_Tp>::template __f<_Fn>;
};
+
template <class _Fn, class _List>
-using __mapply = __minvoke<__uncurry<_Fn>, _List>;
+using __mapply = __minvoke<__muncurry<_Fn>, _List>;
+
+template <bool>
+struct __mconcat_
+{
+ template <class... _Ts, template <class...> class _Ap = __types,
+ class... _As, template <class...> class _Bp = __types,
+ class... _Bs, template <class...> class _Cp = __types,
+ class... _Cs, template <class...> class _Dp = __types,
+ class... _Ds, class... _Tail>
+ static auto __f(__types<_Ts...>*, _Ap<_As...>*, _Bp<_Bs...>* = nullptr,
+ _Cp<_Cs...>* = nullptr, _Dp<_Ds...>* = nullptr,
+ _Tail*... __tail)
+ -> decltype(__mconcat_<(sizeof...(_Tail) == 0)>::__f(
+ static_cast<__types<_Ts..., _As..., _Bs..., _Cs..., _Ds...>*>(
+ nullptr),
+ __tail...));
+};
+
+template <>
+struct __mconcat_<true>
+{
+ template <class... _As>
+ static auto __f(__types<_As...>*) -> __types<_As...>;
+};
+
+template <class _Continuation = __qq<__types>>
+struct __mconcat
+{
+ template <class... _Args>
+ using __f = __mapply<_Continuation,
+ decltype(__mconcat_<(sizeof...(_Args) == 0)>::__f(
+ {}, static_cast<_Args*>(nullptr)...))>;
+};
struct __msize
{
@@ -675,38 +808,19 @@
};
template <class _Tp>
-struct __contains
+struct __mcontains
{
template <class... _Args>
using __f = __mbool<(__same_as<_Tp, _Args> || ...)>;
};
template <class _Continuation = __q<__types>>
-struct __push_back
+struct __mpush_back
{
template <class _List, class _Item>
using __f = __mapply<__mbind_back<_Continuation, _Item>, _List>;
};
-template <class _Continuation = __q<__types>>
-struct __push_back_unique
-{
- template <class _List, class _Item>
- using __f = //
- __mapply<__if<__mapply<__contains<_Item>, _List>, _Continuation,
- __mbind_back<_Continuation, _Item>>,
- _List>;
-};
-
-template <class _Continuation = __q<__types>>
-struct __munique
-{
- template <class... _Ts>
- using __f = __mapply<
- _Continuation,
- __minvoke<__mfold_right<__types<>, __push_back_unique<>>, _Ts...>>;
-};
-
template <class...>
struct __mcompose
{};
@@ -731,8 +845,15 @@
__minvoke<__mcompose<_Penultimate, _Rest...>, _Args...>>;
};
+template <template <class...> class _Second, template <class...> class _First>
+struct __mcompose_q
+{
+ template <class... _Args>
+ using __f = _Second<_First<_Args...>>;
+};
+
template <class _Old, class _New, class _Continuation = __q<__types>>
-struct __replace
+struct __mreplace
{
template <class... _Args>
using __f = __minvoke<_Continuation,
@@ -740,7 +861,7 @@
};
template <class _Old, class _Continuation = __q<__types>>
-struct __remove
+struct __mremove
{
template <class... _Args>
using __f = //
@@ -749,7 +870,7 @@
};
template <class _Pred, class _Continuation = __q<__types>>
-struct __remove_if
+struct __mremove_if
{
template <class... _Args>
using __f = //
@@ -777,13 +898,6 @@
template <class _Default>
using __msingle_or = __mbind_front_q<__msingle_or_, _Default>;
-template <class _Continuation = __q<__types>>
-struct __pop_front
-{
- template <class, class... _Ts>
- using __f = __minvoke<_Continuation, _Ts...>;
-};
-
template <class _Ty>
concept __has_id = requires { typename _Ty::__id; };
@@ -835,24 +949,22 @@
using __call_result_t = decltype(__declval<_Fun>()(__declval<_As>()...));
#endif
+// BUGBUG TODO file this bug with nvc++
+#if STDEXEC_NVHPC()
template <const auto& _Fun, class... _As>
using __result_of = __call_result_t<decltype(_Fun), _As...>;
+#else
+template <const auto& _Fun, class... _As>
+using __result_of = decltype(_Fun(__declval<_As>()...));
+#endif
-// For working around clang's lack of support for CWG#2369:
-// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2369
-struct __qcall_result
-{
- template <class _Fun, class... _As>
- using __f = __call_result_t<_Fun, _As...>;
-};
-template <bool _Enable, class _Fun, class... _As>
-using __call_result_if_t =
- __minvoke<__if<__mbool<_Enable>, __qcall_result, __>, _Fun, _As...>;
+template <const auto& _Fun, class... _As>
+inline constexpr bool __noexcept_of = noexcept(_Fun(__declval<_As>()...));
// For emplacing non-movable types into optionals:
template <class _Fn>
requires std::is_nothrow_move_constructible_v<_Fn>
-struct __conv
+struct __emplace_from
{
_Fn __fn_;
using __t = __call_result_t<_Fn>;
@@ -867,18 +979,9 @@
return static_cast<_Fn&&>(__fn_)();
}
};
-template <class _Fn>
-__conv(_Fn) -> __conv<_Fn>;
-// Implemented as a class instead of a free function
-// because of a bizarre nvc++ compiler bug:
-struct __cref_fn
-{
- template <class _Ty>
- auto operator()(const _Ty&) -> const _Ty&;
-};
-template <class _Ty>
-using __cref_t = decltype(__cref_fn{}(__declval<_Ty>()));
+template <class _Fn>
+__emplace_from(_Fn) -> __emplace_from<_Fn>;
template <class, class, class, class>
struct __mzip_with2_;
@@ -904,27 +1007,6 @@
using __f = __t<__mzip_with2_<_Fn, _Continuation, _Cp, _Dp>>;
};
-#if STDEXEC_GCC() && (__GNUC__ < 12)
-template <class>
-extern int __mconvert_indices;
-template <std::size_t... _Indices>
-extern __types<__msize_t<_Indices>...>
- __mconvert_indices<std::index_sequence<_Indices...>>;
-template <std::size_t _Np>
-using __mmake_index_sequence =
- decltype(stdexec::__mconvert_indices<std::make_index_sequence<_Np>>);
-#else
-template <std::size_t... _Indices>
-auto __mconvert_indices(std::index_sequence<_Indices...>*)
- -> __types<__msize_t<_Indices>...>;
-template <std::size_t _Np>
-using __mmake_index_sequence = decltype(stdexec::__mconvert_indices(
- static_cast<std::make_index_sequence<_Np>*>(nullptr)));
-#endif
-
-template <class... _Ts>
-using __mindex_sequence_for = __mmake_index_sequence<sizeof...(_Ts)>;
-
template <bool>
struct __mfind_if_
{
@@ -960,20 +1042,37 @@
__v<__minvoke<__mfind_if<_Fn, __msize>, _Args...>>)>;
};
-template <class... _Booleans>
-using __mand_ = __mbool<(__v<_Booleans> && ...)>;
-template <class... _Booleans>
-using __mand = __meval<__mand_, _Booleans...>;
+#if STDEXEC_MSVC()
+#define __mvalue_of(...) __VA_ARGS__::value
+#else
+#define __mvalue_of(...) __v<__VA_ARGS__>
+#endif
template <class... _Booleans>
-using __mor_ = __mbool<(__v<_Booleans> || ...)>;
+using __mand_t = __mbool<(__mvalue_of(_Booleans) && ...)>;
template <class... _Booleans>
-using __mor = __meval<__mor_, _Booleans...>;
+using __mand = __meval<__mand_t, _Booleans...>;
+
+template <class... _Booleans>
+using __mor_t = __mbool<(__mvalue_of(_Booleans) || ...)>;
+template <class... _Booleans>
+using __mor = __meval<__mor_t, _Booleans...>;
template <class _Boolean>
-using __mnot_ = __mbool<!__v<_Boolean>>;
+using __mnot_t = __mbool<!__mvalue_of(_Boolean)>;
template <class _Boolean>
-using __mnot = __meval<__mnot_, _Boolean>;
+using __mnot = __meval<__mnot_t, _Boolean>;
+
+#if STDEXEC_NVHPC()
+template <class... _Ints>
+struct __mplus_t : __mconstant<(__v<_Ints> + ...)>
+{};
+#else
+template <class... _Ints>
+using __mplus_t = __mconstant<(__mvalue_of(_Ints) + ...)>;
+#endif
+
+#undef __mvalue_of
template <class _Fn>
struct __mall_of
@@ -996,15 +1095,25 @@
using __f = __mor<__minvoke<_Fn, _Args>...>;
};
-#if STDEXEC_HAS_BUILTIN(__type_pack_element)
-template <std::size_t _Np, class... _Ts>
-struct __m_at_
-{
- using __t = __type_pack_element<_Np, _Ts...>;
-};
+#if defined(__cpp_pack_indexing)
+template <class _Np, class... _Ts>
+using __m_at = _Ts...[__v<_Np>];
template <std::size_t _Np, class... _Ts>
-using __m_at_c = __t<__m_at_<_Np, _Ts...>>;
+using __m_at_c = _Ts...[_Np];
+#elif STDEXEC_HAS_BUILTIN(__type_pack_element)
+template <bool>
+struct __m_at_
+{
+ template <class _Np, class... _Ts>
+ using __f = __type_pack_element<__v<_Np>, _Ts...>;
+};
+
+template <class _Np, class... _Ts>
+using __m_at = __minvoke<__m_at_<__v<_Np> == ~0ul>, _Np, _Ts...>;
+
+template <std::size_t _Np, class... _Ts>
+using __m_at_c = __minvoke<__m_at_<_Np == ~0ul>, __msize_t<_Np>, _Ts...>;
#else
template <std::size_t>
using __void_ptr = void*;
@@ -1016,7 +1125,7 @@
struct __m_at_;
template <std::size_t... _Is>
-struct __m_at_<std::index_sequence<_Is...>>
+struct __m_at_<__indices<_Is...>>
{
template <class _Up, class... _Us>
static _Up __f_(__void_ptr<_Is>..., _Up*, _Us*...);
@@ -1025,11 +1134,11 @@
};
template <std::size_t _Np, class... _Ts>
-using __m_at_c = __minvoke<__m_at_<std::make_index_sequence<_Np>>, _Ts...>;
-#endif
+using __m_at_c = __minvoke<__m_at_<__make_indices<_Np>>, _Ts...>;
template <class _Np, class... _Ts>
using __m_at = __m_at_c<__v<_Np>, _Ts...>;
+#endif
template <class... _Ts>
using __mback = __m_at_c<sizeof...(_Ts) - 1, _Ts...>;
@@ -1059,7 +1168,7 @@
constexpr __placeholder(void*) noexcept {}
- friend constexpr auto __get_placeholder_offset(__placeholder) noexcept
+ constexpr friend auto __get_placeholder_offset(__placeholder) noexcept
-> std::size_t
{
return _Np;
@@ -1071,22 +1180,19 @@
using __2 = __placeholder<2>;
using __3 = __placeholder<3>;
-#if STDEXEC_MSVC()
-// MSVCBUG
-// https://developercommunity.visualstudio.com/t/Incorrect-function-template-argument-sub/10437827
-
-template <std::size_t>
-struct __ignore_t
+#if defined(__cpp_pack_indexing)
+template <std::size_t _Np>
+struct __nth_pack_element_t
{
- __ignore_t() = default;
-
- constexpr __ignore_t(auto&&...) noexcept {}
+ template <class... _Ts>
+ STDEXEC_ATTRIBUTE((always_inline))
+ constexpr decltype(auto) operator()(_Ts&&... __ts) const noexcept
+ {
+ static_assert(_Np < sizeof...(_Ts));
+ return (static_cast<_Ts&&>(__ts)...[_Np]);
+ }
};
#else
-template <std::size_t>
-using __ignore_t = __ignore;
-#endif
-
template <class... _Ignore>
struct __nth_pack_element_impl
{
@@ -1116,6 +1222,7 @@
return __impl(__make_indices<_Np>())(static_cast<_Ts&&>(__ts)...);
}
};
+#endif
template <std::size_t _Np>
inline constexpr __nth_pack_element_t<_Np> __nth_pack_element{};
@@ -1268,7 +1375,7 @@
__callable<
__mdispatch_<__minvoke<__mpop_back<__qf<_Ret>>, _Args...>*>,
_Ts...>
- auto operator()(_Ts&&... __ts) const
+ auto operator()(_Ts&&... __ts) const //
noexcept(__nothrow_callable<
__mdispatch_<__minvoke<__mpop_back<__qf<_Ret>>, _Args...>*>,
_Ts...>)
@@ -1327,4 +1434,59 @@
__minvoke<__mtry_catch<__mcompose<__q<__mdispatch>, __which<_Signatures>>,
_DefaultFn>,
_Args...>;
+
+template <class _Set, class... _Ty>
+concept __mset_contains = (STDEXEC_IS_BASE_OF(__mtype<_Ty>, _Set) && ...);
+
+namespace __set
+{
+template <class... _Ts>
+struct __inherit
+{};
+
+template <class _Ty, class... _Ts>
+struct __inherit<_Ty, _Ts...> : __mtype<_Ty>, __inherit<_Ts...>
+{};
+
+template <class... _Set>
+auto operator+(__inherit<_Set...>&) -> __inherit<_Set...>;
+
+template <class... _Set, class _Ty>
+auto operator%(__inherit<_Set...>&, __mtype<_Ty>&) //
+ -> __if_c< //
+ __mset_contains<__inherit<_Set...>, _Ty>, __inherit<_Set...>,
+ __inherit<_Ty, _Set...>>&;
+
+template <class _ExpectedSet, class... _Ts>
+concept __mset_eq = //
+ (sizeof...(_Ts) == __v<__mapply<__msize, _ExpectedSet>>)&& //
+ __mset_contains<_ExpectedSet, _Ts...>;
+
+template <class _ExpectedSet>
+struct __eq
+{
+ template <class... _Ts>
+ using __f = __mbool<__mset_eq<_ExpectedSet, _Ts...>>;
+};
+} // namespace __set
+
+template <class... _Ts>
+using __mset = __set::__inherit<_Ts...>;
+
+template <class _Set, class... _Ts>
+using __mset_insert =
+ decltype(+(__declval<_Set&>() % ... % __declval<__mtype<_Ts>&>()));
+
+template <class... _Ts>
+using __mmake_set = __mset_insert<__mset<>, _Ts...>;
+
+template <class _Set1, class _Set2>
+concept __mset_eq = __v<__mapply<__set::__eq<_Set1>, _Set2>>;
+
+template <class _Continuation = __q<__types>>
+struct __munique
+{
+ template <class... _Ts>
+ using __f = __mapply<_Continuation, __mmake_set<_Ts...>>;
+};
} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__on.hpp b/include/sdbusplus/async/stdexec/__detail/__on.hpp
new file mode 100644
index 0000000..3e5f704
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__on.hpp
@@ -0,0 +1,267 @@
+/*
+ * 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 "__continue_on.hpp"
+#include "__cpo.hpp"
+#include "__diagnostics.hpp"
+#include "__domain.hpp"
+#include "__env.hpp"
+#include "__inline_scheduler.hpp"
+#include "__meta.hpp"
+#include "__schedulers.hpp"
+#include "__sender_adaptor_closure.hpp"
+#include "__sender_introspection.hpp"
+#include "__senders_core.hpp"
+#include "__transform_sender.hpp"
+#include "__type_traits.hpp"
+#include "__utility.hpp"
+#include "__write_env.hpp"
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.on]
+namespace __on_v2
+{
+inline constexpr __mstring __on_context =
+ "In stdexec::on(Scheduler, Sender)..."_mstr;
+inline constexpr __mstring __no_scheduler_diag =
+ "stdexec::on() requires a scheduler to transition back to."_mstr;
+inline constexpr __mstring __no_scheduler_details =
+ "The provided environment lacks a value for the get_scheduler() query."_mstr;
+
+template <__mstring _Context = __on_context,
+ __mstring _Diagnostic = __no_scheduler_diag,
+ __mstring _Details = __no_scheduler_details>
+struct _CANNOT_RESTORE_EXECUTION_CONTEXT_AFTER_ON_
+{};
+
+struct on_t;
+
+template <class _Sender, class _Env>
+struct __no_scheduler_in_environment
+{
+ using sender_concept = sender_t;
+
+ static auto get_completion_signatures(const __no_scheduler_in_environment&,
+ const auto&) noexcept
+ {
+ return __mexception<_CANNOT_RESTORE_EXECUTION_CONTEXT_AFTER_ON_<>,
+ _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>{};
+ }
+};
+
+template <class _Scheduler, class _Closure>
+struct __continue_on_data
+{
+ _Scheduler __sched_;
+ _Closure __clsur_;
+};
+template <class _Scheduler, class _Closure>
+__continue_on_data(_Scheduler, _Closure)
+ -> __continue_on_data<_Scheduler, _Closure>;
+
+template <class _Scheduler>
+struct __with_sched
+{
+ using __t = __with_sched;
+ using __id = __with_sched;
+
+ _Scheduler __sched_;
+
+ auto query(get_scheduler_t) const noexcept -> _Scheduler
+ {
+ return __sched_;
+ }
+
+ auto query(get_domain_t) const noexcept
+ {
+ return query_or(get_domain, __sched_, default_domain());
+ }
+};
+
+template <class _Scheduler>
+__with_sched(_Scheduler) -> __with_sched<_Scheduler>;
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+struct on_t
+{
+ template <scheduler _Scheduler, sender _Sender>
+ auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
+ -> __well_formed_sender auto
+ {
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<on_t>(static_cast<_Scheduler&&>(__sched),
+ static_cast<_Sender&&>(__sndr)));
+ }
+
+ template <sender _Sender, scheduler _Scheduler,
+ __sender_adaptor_closure_for<_Sender> _Closure>
+ auto operator()(_Sender&& __sndr, _Scheduler&& __sched,
+ _Closure&& __clsur) const -> __well_formed_sender auto
+ {
+ 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)));
+ }
+
+ template <scheduler _Scheduler, __sender_adaptor_closure _Closure>
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto operator()(_Scheduler&& __sched, _Closure&& __clsur) const
+ {
+ return __binder_back<on_t, __decay_t<_Scheduler>, __decay_t<_Closure>>{
+ {{static_cast<_Scheduler&&>(__sched)},
+ {static_cast<_Closure&&>(__clsur)}},
+ {},
+ {}};
+ }
+
+ template <class _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ static auto __transform_env_fn(_Env&& __env) noexcept
+ {
+ return [&]<class _Data>(__ignore, _Data&& __data,
+ __ignore) noexcept -> decltype(auto) {
+ if constexpr (scheduler<_Data>)
+ {
+ return __detail::__mkenv_sched(static_cast<_Env&&>(__env),
+ static_cast<_Data&&>(__data));
+ }
+ else
+ {
+ return static_cast<_Env>(static_cast<_Env&&>(__env));
+ }
+ };
+ }
+
+ template <class _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ static auto __transform_sender_fn(const _Env& __env) noexcept
+ {
+ return [&]<class _Data, class _Child>(__ignore, _Data&& __data,
+ _Child&& __child) {
+ if constexpr (scheduler<_Data>)
+ {
+ // This branch handles the case where `on` was called like
+ // `on(sch, snd)`
+ auto __old = query_or(get_scheduler, __env, __none_such{});
+ if constexpr (__same_as<decltype(__old), __none_such>)
+ {
+ if constexpr (__is_root_env<_Env>)
+ {
+ return continue_on(
+ start_on(static_cast<_Data&&>(__data),
+ static_cast<_Child&&>(__child)),
+ __inln::__scheduler{});
+ }
+ else
+ {
+ return __none_such{};
+ }
+ }
+ else
+ {
+ return continue_on(start_on(static_cast<_Data&&>(__data),
+ static_cast<_Child&&>(__child)),
+ static_cast<decltype(__old)&&>(__old));
+ }
+ }
+ else
+ {
+ // This branch handles the case where `on` was called like
+ // `on(snd, sch, clsur)`
+ auto __old = query_or(
+ get_completion_scheduler<set_value_t>, get_env(__child),
+ query_or(get_scheduler, __env, __none_such{}));
+ if constexpr (__same_as<decltype(__old), __none_such>)
+ {
+ return __none_such{};
+ }
+ else
+ {
+ auto&& [__sched, __clsur] = static_cast<_Data&&>(__data);
+ return __write_env( //
+ continue_on( //
+ __forward_like<_Data>(__clsur)( //
+ continue_on( //
+ __write_env(static_cast<_Child&&>(__child),
+ __with_sched{__old}), //
+ __sched)), //
+ __old),
+ __with_sched{__sched});
+ }
+ }
+ };
+ }
+
+ template <class _Sender, class _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ 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>
+ STDEXEC_ATTRIBUTE((always_inline))
+ static auto transform_sender(_Sender&& __sndr, const _Env& __env)
+ {
+ auto __tfx_sndr_fn = __transform_sender_fn(__env);
+ using _TfxSndrFn = decltype(__tfx_sndr_fn);
+ using _NewSndr = __sexpr_apply_result_t<_Sender, _TfxSndrFn>;
+ if constexpr (__same_as<_NewSndr, __none_such>)
+ {
+ return __no_scheduler_in_environment<_Sender, _Env>{};
+ }
+ else
+ {
+ return __sexpr_apply(static_cast<_Sender&&>(__sndr),
+ static_cast<_TfxSndrFn&&>(__tfx_sndr_fn));
+ }
+ }
+};
+} // namespace __on_v2
+
+namespace v2
+{
+using __on_v2::on_t;
+inline constexpr on_t on{};
+
+using continue_on_t = v2::on_t;
+inline constexpr continue_on_t continue_on{}; // for back-compat
+} // namespace v2
+
+template <>
+struct __sexpr_impl<v2::on_t> : __sexpr_defaults
+{
+ static constexpr auto get_completion_signatures = //
+ []<class _Sender>(_Sender&&) noexcept //
+ -> __merror_or_t< //
+ __completion_signatures_of_t< //
+ transform_sender_result_t<default_domain, _Sender, empty_env>>,
+ dependent_completions> { return {}; };
+};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__operation_states.hpp b/include/sdbusplus/async/stdexec/__detail/__operation_states.hpp
new file mode 100644
index 0000000..a59817e
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__operation_states.hpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "__tag_invoke.hpp"
+
+#include <type_traits>
+
+namespace stdexec
+{
+// operation state tag type
+struct operation_state_t
+{};
+
+/////////////////////////////////////////////////////////////////////////////
+// [execution.op_state]
+namespace __start
+{
+struct start_t
+{
+ template <__same_as<start_t> _Self, class _OpState>
+ STDEXEC_ATTRIBUTE((always_inline))
+ friend auto tag_invoke(_Self, _OpState& __op) noexcept
+ -> decltype(__op.start())
+ {
+ static_assert(noexcept(__op.start()),
+ "start() members must be noexcept");
+ static_assert(__same_as<decltype(__op.start()), void>,
+ "start() members must return void");
+ __op.start();
+ }
+
+ template <class _Op>
+ requires tag_invocable<start_t, _Op&>
+ STDEXEC_ATTRIBUTE((always_inline)) void operator()(_Op& __op) const noexcept
+ {
+ static_assert(nothrow_tag_invocable<start_t, _Op&>);
+ (void)tag_invoke(start_t{}, __op);
+ }
+};
+} // namespace __start
+
+using __start::start_t;
+inline constexpr start_t start{};
+
+/////////////////////////////////////////////////////////////////////////////
+// [execution.op_state]
+template <class _Op>
+concept operation_state = //
+ destructible<_Op> && //
+ std::is_object_v<_Op> && //
+ requires(_Op& __op) { //
+ stdexec::start(__op);
+ };
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__optional.hpp b/include/sdbusplus/async/stdexec/__detail/__optional.hpp
new file mode 100644
index 0000000..7591e20
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__optional.hpp
@@ -0,0 +1,170 @@
+/*
+ * 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 "__type_traits.hpp"
+
+#include <exception>
+#include <memory>
+#include <new>
+#include <utility>
+
+namespace stdexec
+{
+namespace __opt
+{
+struct __bad_optional_access : std::exception
+{
+ const char* what() const noexcept override
+ {
+ return "stdexec::__optional: bad access";
+ }
+};
+
+inline constexpr struct __nullopt_t
+{
+} __nullopt{};
+
+// A simplified version of std::optional for better compile times
+template <class _Tp>
+struct __optional
+{
+ static_assert(destructible<_Tp>);
+
+ union
+ {
+ _Tp __value;
+ };
+
+ bool __has_value = false;
+
+ __optional() noexcept {}
+
+ __optional(__nullopt_t) noexcept {}
+
+ __optional(__optional&&) = delete; // immovable for simplicity's sake
+
+ template <__not_decays_to<__optional> _Up>
+ requires constructible_from<_Tp, _Up>
+ __optional(_Up&& __v) : __value(static_cast<_Up&&>(__v)), __has_value(true)
+ {}
+
+ template <class... _Us>
+ requires constructible_from<_Tp, _Us...>
+ __optional(std::in_place_t, _Us&&... __us) :
+ __value(static_cast<_Us&&>(__us)...), __has_value(true)
+ {}
+
+ ~__optional()
+ {
+ if (__has_value)
+ {
+ std::destroy_at(std::addressof(__value));
+ }
+ }
+
+ template <class... _Us>
+ requires constructible_from<_Tp, _Us...>
+ _Tp& emplace(_Us&&... __us) noexcept(
+ __nothrow_constructible_from<_Tp, _Us...>)
+ {
+ reset(); // sets __has_value to false in case the next line throws
+ ::new (&__value) _Tp{static_cast<_Us&&>(__us)...};
+ __has_value = true;
+ return __value;
+ }
+
+ _Tp& value() &
+ {
+ if (!__has_value)
+ {
+ throw __bad_optional_access();
+ }
+ return __value;
+ }
+
+ const _Tp& value() const&
+ {
+ if (!__has_value)
+ {
+ throw __bad_optional_access();
+ }
+ return __value;
+ }
+
+ _Tp&& value() &&
+ {
+ if (!__has_value)
+ {
+ throw __bad_optional_access();
+ }
+ return static_cast<_Tp&&>(__value);
+ }
+
+ _Tp& operator*() & noexcept
+ {
+ STDEXEC_ASSERT(__has_value);
+ return __value;
+ }
+
+ const _Tp& operator*() const& noexcept
+ {
+ STDEXEC_ASSERT(__has_value);
+ return __value;
+ }
+
+ _Tp&& operator*() && noexcept
+ {
+ STDEXEC_ASSERT(__has_value);
+ return static_cast<_Tp&&>(__value);
+ }
+
+ _Tp* operator->() & noexcept
+ {
+ STDEXEC_ASSERT(__has_value);
+ return &__value;
+ }
+
+ const _Tp* operator->() const& noexcept
+ {
+ STDEXEC_ASSERT(__has_value);
+ return &__value;
+ }
+
+ bool has_value() const noexcept
+ {
+ return __has_value;
+ }
+
+ void reset() noexcept
+ {
+ if (__has_value)
+ {
+ std::destroy_at(std::addressof(__value));
+ __has_value = false;
+ }
+ }
+};
+} // namespace __opt
+
+using __opt::__bad_optional_access;
+using __opt::__nullopt;
+using __opt::__optional;
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__read_env.hpp b/include/sdbusplus/async/stdexec/__detail/__read_env.hpp
new file mode 100644
index 0000000..48bef46
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__read_env.hpp
@@ -0,0 +1,167 @@
+/*
+ * 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 "__completion_signatures.hpp"
+#include "__concepts.hpp"
+#include "__diagnostics.hpp"
+#include "__env.hpp"
+#include "__meta.hpp"
+#include "__optional.hpp"
+#include "__receivers.hpp"
+#include "__tag_invoke.hpp"
+
+#include <exception>
+
+namespace stdexec
+{
+namespace __read
+{
+template <class _Tag, class _ReceiverId>
+using __result_t = __call_result_t<_Tag, env_of_t<stdexec::__t<_ReceiverId>>>;
+
+template <class _Tag, class _ReceiverId>
+concept __nothrow_t =
+ __nothrow_callable<_Tag, env_of_t<stdexec::__t<_ReceiverId>>>;
+
+inline constexpr __mstring __query_failed_diag =
+ "The current execution environment doesn't have a value for the given query."_mstr;
+
+template <class _Tag, class _Env>
+using __query_failed_error = //
+ __mexception< //
+ _NOT_CALLABLE_<"In stdexec::read()..."_mstr, __query_failed_diag>,
+ _WITH_QUERY_<_Tag>, _WITH_ENVIRONMENT_<_Env>>;
+
+template <class _Tag, class _Env>
+ requires __callable<_Tag, _Env>
+using __completions_t = //
+ __if_c<__nothrow_callable<_Tag, _Env>,
+ completion_signatures<set_value_t(__call_result_t<_Tag, _Env>)>,
+ completion_signatures<set_value_t(__call_result_t<_Tag, _Env>),
+ set_error_t(std::exception_ptr)>>;
+
+template <class _Tag, class _Ty>
+struct __state
+{
+ using __query = _Tag;
+ using __result = _Ty;
+ __optional<_Ty> __result_;
+};
+
+template <class _Tag, class _Ty>
+ requires __same_as<_Ty, _Ty&&>
+struct __state<_Tag, _Ty>
+{
+ using __query = _Tag;
+ using __result = _Ty;
+};
+
+struct __read_env_t
+{
+ template <class _Tag>
+ constexpr auto operator()(_Tag) const noexcept
+ {
+ return __make_sexpr<__read_env_t>(_Tag());
+ }
+};
+
+struct __read_env_impl : __sexpr_defaults
+{
+ template <class _Tag, class _Env>
+ using __completions_t = __minvoke<
+ __mtry_catch_q<__read::__completions_t, __q<__query_failed_error>>,
+ _Tag, _Env>;
+
+ static constexpr auto get_completion_signatures = //
+ []<class _Self, class _Env>(const _Self&, _Env&&) noexcept
+ -> __completions_t<__data_of<_Self>, _Env> { return {}; };
+
+ static constexpr auto get_state = //
+ []<class _Self, class _Receiver>(const _Self&, _Receiver&) noexcept {
+ using __query = __data_of<_Self>;
+ using __result = __call_result_t<__query, env_of_t<_Receiver>>;
+ return __state<__query, __result>();
+ };
+
+ static constexpr auto start = //
+ []<class _State, class _Receiver>(_State& __state,
+ _Receiver& __rcvr) noexcept -> void {
+ using __query = typename _State::__query;
+ using __result = typename _State::__result;
+ if constexpr (__same_as<__result, __result&&>)
+ {
+ // The query returns a reference type; pass it straight through to
+ // the receiver.
+ stdexec::__set_value_invoke(static_cast<_Receiver&&>(__rcvr),
+ __query(), stdexec::get_env(__rcvr));
+ }
+ else
+ {
+ constexpr bool _Nothrow =
+ __nothrow_callable<__query, env_of_t<_Receiver>>;
+ auto __query_fn = [&]() noexcept(_Nothrow) -> __result&& {
+ __state.__result_.emplace(
+ __emplace_from{[&]() noexcept(_Nothrow) {
+ return __query()(stdexec::get_env(__rcvr));
+ }});
+ return static_cast<__result&&>(*__state.__result_);
+ };
+ stdexec::__set_value_invoke(static_cast<_Receiver&&>(__rcvr),
+ __query_fn);
+ }
+ };
+};
+} // namespace __read
+
+inline constexpr __read::__read_env_t read{};
+inline constexpr __read::__read_env_t read_env{};
+
+template <>
+struct __sexpr_impl<__read::__read_env_t> : __read::__read_env_impl
+{};
+
+namespace __queries
+{
+template <class _Tag>
+inline auto get_scheduler_t::operator()() const noexcept
+{
+ return read_env(get_scheduler);
+}
+
+template <class _Tag>
+inline auto get_delegatee_scheduler_t::operator()() const noexcept
+{
+ return read_env(get_delegatee_scheduler);
+}
+
+template <class _Tag>
+inline auto get_allocator_t::operator()() const noexcept
+{
+ return read_env(get_allocator);
+}
+
+template <class _Tag>
+inline auto get_stop_token_t::operator()() const noexcept
+{
+ return read_env(get_stop_token);
+}
+} // namespace __queries
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__receiver_adaptor.hpp b/include/sdbusplus/async/stdexec/__detail/__receiver_adaptor.hpp
new file mode 100644
index 0000000..95a8c29
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__receiver_adaptor.hpp
@@ -0,0 +1,187 @@
+/*
+ * 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 "__concepts.hpp"
+#include "__cpo.hpp"
+#include "__execution_fwd.hpp"
+#include "__receivers.hpp"
+#include "__tag_invoke.hpp"
+#include "__type_traits.hpp"
+#include "__utility.hpp"
+
+namespace stdexec
+{
+namespace __adaptors
+{
+namespace __no
+{
+struct __nope
+{};
+
+struct __receiver : __nope
+{
+ using receiver_concept = receiver_t;
+
+ void set_error(std::exception_ptr) noexcept;
+ void set_stopped() noexcept;
+ auto get_env() const noexcept -> empty_env;
+};
+} // namespace __no
+
+using __not_a_receiver = __no::__receiver;
+
+template <class _Base>
+struct __adaptor_base
+{
+ template <class _T1>
+ requires constructible_from<_Base, _T1>
+ explicit __adaptor_base(_T1&& __base) : __base_(static_cast<_T1&&>(__base))
+ {}
+
+ private:
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _Base __base_;
+
+ protected:
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ _Base& base() & noexcept
+ {
+ return __base_;
+ }
+
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ const _Base& base() const& noexcept
+ {
+ return __base_;
+ }
+
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ _Base&& base() && noexcept
+ {
+ return static_cast<_Base&&>(__base_);
+ }
+};
+
+template <derived_from<__no::__nope> _Base>
+struct __adaptor_base<_Base>
+{};
+
+// BUGBUG Not to spec: on gcc and nvc++, member functions in derived classes
+// don't shadow type aliases of the same name in base classes. :-O
+// On mingw gcc, 'bool(type::existing_member_function)' evaluates to true,
+// but 'int(type::existing_member_function)' is an error (as desired).
+#define STDEXEC_DISPATCH_MEMBER(_TAG) \
+ template <class _Self, class... _Ts> \
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) \
+ static auto __call_##_TAG(_Self&& __self, _Ts&&... __ts) noexcept \
+ -> decltype((static_cast<_Self&&>(__self)) \
+ ._TAG(static_cast<_Ts&&>(__ts)...)) \
+ { \
+ static_assert(noexcept((static_cast<_Self&&>(__self)) \
+ ._TAG(static_cast<_Ts&&>(__ts)...))); \
+ return static_cast<_Self&&>(__self)._TAG(static_cast<_Ts&&>(__ts)...); \
+ } /**/
+#define STDEXEC_CALL_MEMBER(_TAG, ...) __call_##_TAG(__VA_ARGS__)
+
+#if STDEXEC_CLANG()
+// Only clang gets this right.
+#define STDEXEC_MISSING_MEMBER(_Dp, _TAG) requires { typename _Dp::_TAG; }
+#define STDEXEC_DEFINE_MEMBER(_TAG) \
+ STDEXEC_DISPATCH_MEMBER(_TAG) using _TAG = void
+#else
+#define STDEXEC_MISSING_MEMBER(_Dp, _TAG) (__missing_##_TAG<_Dp>())
+#define STDEXEC_DEFINE_MEMBER(_TAG) \
+ template <class _Dp> \
+ static constexpr bool __missing_##_TAG() noexcept \
+ { \
+ return requires { requires bool(int(_Dp::_TAG)); }; \
+ } \
+ STDEXEC_DISPATCH_MEMBER(_TAG) \
+ static constexpr int _TAG = 1 /**/
+#endif
+
+template <__class _Derived, class _Base = __not_a_receiver>
+struct receiver_adaptor : __adaptor_base<_Base>, receiver_t
+{
+ static constexpr bool __has_base = !derived_from<_Base, __no::__nope>;
+
+ template <class _Self>
+ using __base_from_derived_t = decltype(__declval<_Self>().base());
+
+ using __get_base_fn =
+ __if_c<__has_base, __mbind_back_q<__copy_cvref_t, _Base>,
+ __q<__base_from_derived_t>>;
+
+ template <class _Self>
+ using __base_t = __minvoke<__get_base_fn, _Self&&>;
+
+ template <class _Self>
+ STDEXEC_ATTRIBUTE((host, device))
+ static auto __get_base(_Self&& __self) noexcept -> __base_t<_Self>
+ {
+ if constexpr (__has_base)
+ {
+ return __c_upcast<receiver_adaptor>(static_cast<_Self&&>(__self))
+ .base();
+ }
+ else
+ {
+ return static_cast<_Self&&>(__self).base();
+ }
+ }
+
+ public:
+ using receiver_concept = receiver_t;
+
+ receiver_adaptor() = default;
+ using __adaptor_base<_Base>::__adaptor_base;
+
+ template <class... _As, class _Self = _Derived>
+ requires __callable<set_value_t, __base_t<_Self>, _As...>
+ STDEXEC_ATTRIBUTE((host, device)) void set_value(_As&&... __as) && noexcept
+ {
+ return stdexec::set_value(__get_base(static_cast<_Self&&>(*this)),
+ static_cast<_As&&>(__as)...);
+ }
+
+ template <class _Error, class _Self = _Derived>
+ requires __callable<set_error_t, __base_t<_Self>, _Error>
+ STDEXEC_ATTRIBUTE((host, device)) void set_error(_Error&& __err) && noexcept
+ {
+ return stdexec::set_error(__get_base(static_cast<_Self&&>(*this)),
+ static_cast<_Error&&>(__err));
+ }
+
+ template <class _Self = _Derived>
+ requires __callable<set_stopped_t, __base_t<_Self>>
+ STDEXEC_ATTRIBUTE((host, device)) void set_stopped() && noexcept
+ {
+ return stdexec::set_stopped(__get_base(static_cast<_Self&&>(*this)));
+ }
+
+ template <class _Self = _Derived>
+ STDEXEC_ATTRIBUTE((host, device))
+ auto get_env() const noexcept -> env_of_t<__base_t<const _Self&>>
+ {
+ return stdexec::get_env(__get_base(static_cast<const _Self&>(*this)));
+ }
+};
+} // namespace __adaptors
+
+template <__class _Derived, receiver _Base = __adaptors::__not_a_receiver>
+using receiver_adaptor = __adaptors::receiver_adaptor<_Derived, _Base>;
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__receiver_ref.hpp b/include/sdbusplus/async/stdexec/__detail/__receiver_ref.hpp
new file mode 100644
index 0000000..fdb2d7e
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__receiver_ref.hpp
@@ -0,0 +1,134 @@
+/*
+ * 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 "__completion_signatures.hpp"
+#include "__cpo.hpp"
+#include "__env.hpp"
+#include "__execution_fwd.hpp"
+#include "__schedulers.hpp"
+
+#include <functional>
+
+namespace stdexec
+{
+namespace __any_
+{
+template <class _Sig>
+struct __rcvr_vfun;
+
+template <class _Tag, class... _Args>
+struct __rcvr_vfun<_Tag(_Args...)>
+{
+ void (*__complete_)(void*, _Args&&...) noexcept;
+
+ void operator()(void* __obj, _Tag, _Args&&... __args) const noexcept
+ {
+ __complete_(__obj, static_cast<_Args&&>(__args)...);
+ }
+};
+
+template <class _GetReceiver = std::identity, class _Obj, class _Tag,
+ class... _Args>
+constexpr auto __rcvr_vfun_fn(_Obj*, _Tag (*)(_Args...)) noexcept
+{
+ return +[](void* __ptr, _Args&&... __args) noexcept {
+ _Obj* __obj = static_cast<_Obj*>(__ptr);
+ _Tag()(std::move(_GetReceiver()(*__obj)),
+ static_cast<_Args&&>(__args)...);
+ };
+}
+
+template <class _Sigs, class _Env>
+struct __receiver_vtable_for;
+
+template <class _Env, class... _Sigs>
+struct __receiver_vtable_for<completion_signatures<_Sigs...>, _Env> :
+ __rcvr_vfun<_Sigs>...
+{
+ _Env (*__do_get_env)(const void* __op_state) noexcept;
+
+ template <class _OpState, class _GetEnv>
+ static auto __s_get_env(const void* __ptr) noexcept -> _Env
+ {
+ auto* __op_state = static_cast<const _OpState*>(__ptr);
+ return _GetEnv()(*__op_state);
+ }
+
+ template <class _OpState, class _GetEnv, class _GetReceiver = std::identity>
+ explicit constexpr __receiver_vtable_for(_OpState* __op, _GetEnv,
+ _GetReceiver = {}) noexcept :
+ __rcvr_vfun<_Sigs>{__rcvr_vfun_fn<_GetReceiver>(
+ __op, static_cast<_Sigs*>(nullptr))}...,
+ __do_get_env{&__s_get_env<_OpState, _GetEnv>}
+ {}
+
+ using __rcvr_vfun<_Sigs>::operator()...;
+
+ auto __get_env(const void* __op_state) const noexcept -> _Env
+ {
+ return __do_get_env(__op_state);
+ }
+};
+
+template <class _OpState, class _GetEnv, class _GetReceiver, class _Env,
+ class _Sigs>
+inline constexpr __receiver_vtable_for<_Sigs, _Env> __receiver_vtable_for_v{
+ static_cast<_OpState*>(nullptr), _GetEnv{}, _GetReceiver{}};
+
+template <class _Sigs, class _Env = empty_env>
+class __receiver_ref
+{
+ public:
+ using receiver_concept = receiver_t;
+ using __t = __receiver_ref;
+ using __id = __receiver_ref;
+
+ template <class _OpState, class _GetEnv, class _GetReceiver = std::identity>
+ __receiver_ref(_OpState& __op_state, _GetEnv, _GetReceiver = {}) noexcept :
+ __vtable_{&__any_::__receiver_vtable_for_v<_OpState, _GetEnv,
+ _GetReceiver, _Env, _Sigs>},
+ __op_state_{&__op_state}
+ {}
+
+ auto get_env() const noexcept -> decltype(auto)
+ {
+ return __vtable_->__get_env(__op_state_);
+ }
+
+ template <class... _As>
+ void set_value(_As&&... __as) noexcept
+ {
+ (*__vtable_)(__op_state_, set_value_t(), static_cast<_As&&>(__as)...);
+ }
+
+ template <class _Error>
+ void set_error(_Error&& __err) noexcept
+ {
+ (*__vtable_)(__op_state_, set_error_t(), static_cast<_Error&&>(__err));
+ }
+
+ void set_stopped() noexcept
+ {
+ (*__vtable_)(__op_state_, set_stopped_t());
+ }
+
+ private:
+ const __receiver_vtable_for<_Sigs, _Env>* __vtable_;
+ void* __op_state_;
+};
+} // namespace __any_
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__receivers.hpp b/include/sdbusplus/async/stdexec/__detail/__receivers.hpp
new file mode 100644
index 0000000..cd51c93
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__receivers.hpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2022-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 "../functional.hpp"
+#include "__concepts.hpp"
+#include "__diagnostics.hpp"
+#include "__env.hpp"
+#include "__execution_fwd.hpp"
+#include "__tag_invoke.hpp"
+
+#include <exception>
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.receivers]
+namespace __rcvrs
+{
+struct set_value_t
+{
+ template <class _Fn, class... _Args>
+ using __f = __minvoke<_Fn, _Args...>;
+
+ template <__same_as<set_value_t> _Self, class _Receiver, class... _As>
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ friend auto tag_invoke(_Self, _Receiver&& __rcvr, _As&&... __as) noexcept
+ -> decltype(static_cast<_Receiver&&>(__rcvr).set_value(
+ static_cast<_As&&>(__as)...))
+ {
+ static_assert(noexcept(static_cast<_Receiver&&>(__rcvr).set_value(
+ static_cast<_As&&>(__as)...)),
+ "set_value member functions must be noexcept");
+ static_assert(
+ __same_as<decltype(static_cast<_Receiver&&>(__rcvr).set_value(
+ static_cast<_As&&>(__as)...)),
+ void>,
+ "set_value member functions must return void");
+ static_cast<_Receiver&&>(__rcvr).set_value(static_cast<_As&&>(__as)...);
+ }
+
+ template <class _Receiver, class... _As>
+ requires tag_invocable<set_value_t, _Receiver, _As...>
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) void
+ operator()(_Receiver&& __rcvr, _As&&... __as) const noexcept
+ {
+ static_assert(nothrow_tag_invocable<set_value_t, _Receiver, _As...>);
+ (void)tag_invoke(stdexec::set_value_t{},
+ static_cast<_Receiver&&>(__rcvr),
+ static_cast<_As&&>(__as)...);
+ }
+};
+
+struct set_error_t
+{
+ template <class _Fn, class... _Args>
+ requires(sizeof...(_Args) == 1)
+ using __f = __minvoke<_Fn, _Args...>;
+
+ template <__same_as<set_error_t> _Self, class _Receiver, class _Error>
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ friend auto tag_invoke(_Self, _Receiver&& __rcvr, _Error&& __err) noexcept
+ -> decltype(static_cast<_Receiver&&>(__rcvr).set_error(
+ static_cast<_Error&&>(__err)))
+ {
+ static_assert(noexcept(static_cast<_Receiver&&>(__rcvr).set_error(
+ static_cast<_Error&&>(__err))),
+ "set_error member functions must be noexcept");
+ static_assert(
+ __same_as<decltype(static_cast<_Receiver&&>(__rcvr).set_error(
+ static_cast<_Error&&>(__err))),
+ void>,
+ "set_error member functions must return void");
+ static_cast<_Receiver&&>(__rcvr).set_error(
+ static_cast<_Error&&>(__err));
+ }
+
+ template <class _Receiver, class _Error>
+ requires tag_invocable<set_error_t, _Receiver, _Error>
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) void
+ operator()(_Receiver&& __rcvr, _Error&& __err) const noexcept
+ {
+ static_assert(nothrow_tag_invocable<set_error_t, _Receiver, _Error>);
+ (void)tag_invoke(stdexec::set_error_t{},
+ static_cast<_Receiver&&>(__rcvr),
+ static_cast<_Error&&>(__err));
+ }
+};
+
+struct set_stopped_t
+{
+ template <class _Fn, class... _Args>
+ requires(sizeof...(_Args) == 0)
+ using __f = __minvoke<_Fn, _Args...>;
+
+ template <__same_as<set_stopped_t> _Self, class _Receiver>
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ friend auto tag_invoke(_Self, _Receiver&& __rcvr) noexcept
+ -> decltype(static_cast<_Receiver&&>(__rcvr).set_stopped())
+ {
+ static_assert(noexcept(static_cast<_Receiver&&>(__rcvr).set_stopped()),
+ "set_stopped member functions must be noexcept");
+ static_assert(
+ __same_as<decltype(static_cast<_Receiver&&>(__rcvr).set_stopped()),
+ void>,
+ "set_stopped member functions must return void");
+ static_cast<_Receiver&&>(__rcvr).set_stopped();
+ }
+
+ template <class _Receiver>
+ requires tag_invocable<set_stopped_t, _Receiver>
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) void
+ operator()(_Receiver&& __rcvr) const noexcept
+ {
+ static_assert(nothrow_tag_invocable<set_stopped_t, _Receiver>);
+ (void)tag_invoke(stdexec::set_stopped_t{},
+ static_cast<_Receiver&&>(__rcvr));
+ }
+};
+} // namespace __rcvrs
+
+using __rcvrs::set_error_t;
+using __rcvrs::set_stopped_t;
+using __rcvrs::set_value_t;
+inline constexpr set_value_t set_value{};
+inline constexpr set_error_t set_error{};
+inline constexpr set_stopped_t set_stopped{};
+
+struct receiver_t
+{
+ using receiver_concept = receiver_t; // NOT TO SPEC
+};
+
+namespace __detail
+{
+template <class _Receiver>
+concept __enable_receiver = //
+ (STDEXEC_NVHPC(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,
+ _Receiver); // NOT TO SPEC, for receiver_adaptor
+} // namespace __detail
+
+template <class _Receiver>
+inline constexpr bool enable_receiver =
+ __detail::__enable_receiver<_Receiver>; // NOT TO SPEC
+
+template <class _Receiver>
+concept receiver = //
+ enable_receiver<__decay_t<_Receiver>> //
+ && environment_provider<__cref_t<_Receiver>> //
+ && move_constructible<__decay_t<_Receiver>> //
+ && constructible_from<__decay_t<_Receiver>, _Receiver>;
+
+namespace __detail
+{
+template <class _Receiver, class _Tag, class... _Args>
+auto __try_completion(_Tag (*)(_Args...))
+ -> __mexception<_MISSING_COMPLETION_SIGNAL_<_Tag(_Args...)>,
+ _WITH_RECEIVER_<_Receiver>>;
+
+template <class _Receiver, class _Tag, class... _Args>
+ requires nothrow_tag_invocable<_Tag, _Receiver, _Args...>
+auto __try_completion(_Tag (*)(_Args...)) -> __msuccess;
+
+template <class _Receiver, class... _Sigs>
+auto __try_completions(completion_signatures<_Sigs...>*) //
+ -> decltype((
+ __msuccess(), ...,
+ __detail::__try_completion<_Receiver>(static_cast<_Sigs*>(nullptr))));
+} // namespace __detail
+
+template <class _Receiver, class _Completions>
+concept receiver_of = //
+ receiver<_Receiver> && //
+ requires(_Completions* __completions) {
+ {
+ __detail::__try_completions<__decay_t<_Receiver>>(__completions)
+ } -> __ok;
+ };
+
+template <class _Receiver, class _Sender>
+concept __receiver_from =
+ receiver_of<_Receiver,
+ __completion_signatures_of_t<_Sender, env_of_t<_Receiver>>>;
+
+/// A utility for calling set_value with the result of a function invocation:
+template <bool _CanThrow = false, class _Receiver, class _Fun, class... _As>
+void __set_value_invoke(_Receiver&& __rcvr, _Fun&& __fun,
+ _As&&... __as) noexcept(!_CanThrow)
+{
+ if constexpr (_CanThrow || __nothrow_invocable<_Fun, _As...>)
+ {
+ if constexpr (same_as<void, __invoke_result_t<_Fun, _As...>>)
+ {
+ __invoke(static_cast<_Fun&&>(__fun), static_cast<_As&&>(__as)...);
+ stdexec::set_value(static_cast<_Receiver&&>(__rcvr));
+ }
+ else
+ {
+ set_value(static_cast<_Receiver&&>(__rcvr),
+ __invoke(static_cast<_Fun&&>(__fun),
+ static_cast<_As&&>(__as)...));
+ }
+ }
+ else
+ {
+ try
+ {
+ stdexec::__set_value_invoke<true>(static_cast<_Receiver&&>(__rcvr),
+ static_cast<_Fun&&>(__fun),
+ static_cast<_As&&>(__as)...);
+ }
+ catch (...)
+ {
+ stdexec::set_error(static_cast<_Receiver&&>(__rcvr),
+ std::current_exception());
+ }
+ }
+}
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__run_loop.hpp b/include/sdbusplus/async/stdexec/__detail/__run_loop.hpp
new file mode 100644
index 0000000..0323f89
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__run_loop.hpp
@@ -0,0 +1,264 @@
+/*
+ * 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 "__completion_signatures.hpp"
+#include "__cpo.hpp"
+#include "__env.hpp"
+#include "__meta.hpp"
+#include "__receivers.hpp"
+#include "__utility.hpp"
+
+#include <condition_variable>
+#include <exception>
+#include <mutex>
+#include <utility>
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// run_loop
+namespace __loop
+{
+class run_loop;
+
+struct __task : __immovable
+{
+ __task* __next_ = this;
+
+ union
+ {
+ __task* __tail_ = nullptr;
+ void (*__execute_)(__task*) noexcept;
+ };
+
+ void __execute() noexcept
+ {
+ (*__execute_)(this);
+ }
+};
+
+template <class _ReceiverId>
+struct __operation
+{
+ using _Receiver = stdexec::__t<_ReceiverId>;
+
+ struct __t : __task
+ {
+ using __id = __operation;
+
+ run_loop* __loop_;
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _Receiver __rcvr_;
+
+ static void __execute_impl(__task* __p) noexcept
+ {
+ auto& __rcvr = static_cast<__t*>(__p)->__rcvr_;
+ try
+ {
+ if (stdexec::get_stop_token(stdexec::get_env(__rcvr))
+ .stop_requested())
+ {
+ stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr));
+ }
+ else
+ {
+ stdexec::set_value(static_cast<_Receiver&&>(__rcvr));
+ }
+ }
+ catch (...)
+ {
+ stdexec::set_error(static_cast<_Receiver&&>(__rcvr),
+ std::current_exception());
+ }
+ }
+
+ explicit __t(__task* __tail) noexcept : __task{{}, this, __tail} {}
+
+ __t(__task* __next, run_loop* __loop, _Receiver __rcvr) :
+ __task{{}, __next, {}}, __loop_{__loop},
+ __rcvr_{static_cast<_Receiver&&>(__rcvr)}
+ {
+ __execute_ = &__execute_impl;
+ }
+
+ void start() & noexcept;
+ };
+};
+
+class run_loop
+{
+ template <class>
+ friend struct __operation;
+
+ public:
+ struct __scheduler
+ {
+ private:
+ struct __schedule_task
+ {
+ using __t = __schedule_task;
+ using __id = __schedule_task;
+ using sender_concept = sender_t;
+ using completion_signatures =
+ stdexec::completion_signatures<set_value_t(),
+ set_error_t(std::exception_ptr),
+ set_stopped_t()>;
+
+ template <class _Receiver>
+ using __operation =
+ stdexec::__t<__operation<stdexec::__id<_Receiver>>>;
+
+ template <class _Receiver>
+ auto connect(_Receiver __rcvr) const -> __operation<_Receiver>
+ {
+ return {&__loop_->__head_, __loop_,
+ static_cast<_Receiver&&>(__rcvr)};
+ }
+
+ private:
+ friend __scheduler;
+
+ struct __env
+ {
+ using __t = __env;
+ using __id = __env;
+
+ run_loop* __loop_;
+
+ template <class _CPO>
+ auto query(get_completion_scheduler_t<_CPO>) const noexcept
+ -> __scheduler
+ {
+ return __loop_->get_scheduler();
+ }
+ };
+
+ explicit __schedule_task(run_loop* __loop) noexcept :
+ __loop_(__loop)
+ {}
+
+ run_loop* const __loop_;
+
+ public:
+ auto get_env() const noexcept -> __env
+ {
+ return __env{__loop_};
+ }
+ };
+
+ friend run_loop;
+
+ explicit __scheduler(run_loop* __loop) noexcept : __loop_(__loop) {}
+
+ run_loop* __loop_;
+
+ public:
+ using __t = __scheduler;
+ using __id = __scheduler;
+ auto operator==(const __scheduler&) const noexcept -> bool = default;
+
+ [[nodiscard]] auto schedule() const noexcept -> __schedule_task
+ {
+ return __schedule_task{__loop_};
+ }
+
+ auto query(get_forward_progress_guarantee_t) const noexcept
+ -> stdexec::forward_progress_guarantee
+ {
+ return stdexec::forward_progress_guarantee::parallel;
+ }
+
+ // BUGBUG NOT TO SPEC
+ auto query(execute_may_block_caller_t) const noexcept -> bool
+ {
+ return false;
+ }
+ };
+
+ auto get_scheduler() noexcept -> __scheduler
+ {
+ return __scheduler{this};
+ }
+
+ void run();
+
+ void finish();
+
+ private:
+ void __push_back_(__task* __task);
+ auto __pop_front_() -> __task*;
+
+ std::mutex __mutex_;
+ std::condition_variable __cv_;
+ __task __head_{{}, &__head_, {&__head_}};
+ bool __stop_ = false;
+};
+
+template <class _ReceiverId>
+inline void __operation<_ReceiverId>::__t::start() & noexcept
+{
+ try
+ {
+ __loop_->__push_back_(this);
+ }
+ catch (...)
+ {
+ stdexec::set_error(static_cast<_Receiver&&>(__rcvr_),
+ std::current_exception());
+ }
+}
+
+inline void run_loop::run()
+{
+ for (__task* __task; (__task = __pop_front_()) != &__head_;)
+ {
+ __task->__execute();
+ }
+}
+
+inline void run_loop::finish()
+{
+ std::unique_lock __lock{__mutex_};
+ __stop_ = true;
+ __cv_.notify_all();
+}
+
+inline void run_loop::__push_back_(__task* __task)
+{
+ std::unique_lock __lock{__mutex_};
+ __task->__next_ = &__head_;
+ __head_.__tail_ = __head_.__tail_->__next_ = __task;
+ __cv_.notify_one();
+}
+
+inline auto run_loop::__pop_front_() -> __task*
+{
+ std::unique_lock __lock{__mutex_};
+ __cv_.wait(__lock,
+ [this] { return __head_.__next_ != &__head_ || __stop_; });
+ if (__head_.__tail_ == __head_.__next_)
+ __head_.__tail_ = &__head_;
+ return std::exchange(__head_.__next_, __head_.__next_->__next_);
+}
+} // namespace __loop
+
+// NOT TO SPEC
+using run_loop = __loop::run_loop;
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__schedule_from.hpp b/include/sdbusplus/async/stdexec/__detail/__schedule_from.hpp
new file mode 100644
index 0000000..9d0da1e
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__schedule_from.hpp
@@ -0,0 +1,285 @@
+/*
+ * 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 "__domain.hpp"
+#include "__env.hpp"
+#include "__meta.hpp"
+#include "__operation_states.hpp"
+#include "__schedulers.hpp"
+#include "__senders_core.hpp"
+#include "__transform_completion_signatures.hpp"
+#include "__tuple.hpp"
+#include "__variant.hpp"
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.schedule_from]
+namespace __schfr
+{
+template <class... _Ts>
+using __tuple_t = __tuple_for<__decay_t<_Ts>...>;
+
+template <class... _Ts>
+using __variant_t = __variant_for<__monostate, _Ts...>;
+
+// Compute a variant type that is capable of storing the results of the
+// input sender when it completes. The variant has type:
+// variant<
+// monostate,
+// tuple<set_stopped_t>,
+// tuple<set_value_t, __decay_t<_Values1>...>,
+// tuple<set_value_t, __decay_t<_Values2>...>,
+// ...
+// tuple<set_error_t, __decay_t<_Error1>>,
+// tuple<set_error_t, __decay_t<_Error2>>,
+// ...
+// >
+template <class _CvrefSender, class _Env>
+using __variant_for = //
+ __for_each_completion_signature<
+ __completion_signatures_of_t<_CvrefSender, _Env>, __tuple_t,
+ __munique<__qq<__variant_for>>::__f>;
+
+template <class... _Values>
+using __decay_value_sig = set_value_t (*)(__decay_t<_Values>...);
+
+template <class _Error>
+using __decay_error_sig = set_error_t (*)(__decay_t<_Error>);
+
+template <class... _Ts>
+using __all_nothrow_decay_copyable =
+ __mbool<(__nothrow_decay_copyable<_Ts> && ...)>;
+
+template <class _CvrefSender, class... _Env>
+using __all_nothrow_decay_copyable_results = //
+ __for_each_completion_signature<
+ __completion_signatures_of_t<_CvrefSender, _Env...>,
+ __all_nothrow_decay_copyable, __mand_t>;
+
+template <class _Scheduler, class _CvrefSender, class... _Env>
+using __completions_t = //
+ __mtry_q<__concat_completion_signatures>::__f<
+ __transform_completion_signatures<
+ __completion_signatures_of_t<_CvrefSender, _Env...>,
+ __decay_value_sig, __decay_error_sig, set_stopped_t (*)(),
+ __completion_signature_ptrs>,
+ transform_completion_signatures<
+ __completion_signatures_of_t<schedule_result_t<_Scheduler>,
+ _Env...>,
+ __eptr_completion_if_t<
+ __all_nothrow_decay_copyable_results<_CvrefSender, _Env...>>,
+ __mconst<completion_signatures<>>::__f>>;
+
+template <class _SchedulerId>
+struct __environ
+{
+ using _Scheduler = stdexec::__t<_SchedulerId>;
+
+ struct __t
+ {
+ using __id = __environ;
+
+ _Scheduler __sched_;
+
+ template <__one_of<set_value_t, set_stopped_t> _Tag>
+ auto query(get_completion_scheduler_t<_Tag>) const noexcept
+ {
+ return __sched_;
+ }
+
+ auto query(get_domain_t) const noexcept
+ {
+ return query_or(get_domain, __sched_, default_domain());
+ }
+ };
+};
+
+template <class _Scheduler, class _Sexpr, class _Receiver>
+struct __state;
+
+template <class _State>
+STDEXEC_ATTRIBUTE((always_inline))
+auto __make_visitor_fn(_State* __state) noexcept
+{
+ return [__state]<class _Tup>(_Tup& __tupl) noexcept -> void {
+ if constexpr (__same_as<_Tup, __monostate>)
+ {
+ std::terminate(); // reaching this indicates a bug in schedule_from
+ }
+ else
+ {
+ __tupl.apply(
+ [&]<class... _Args>(auto __tag,
+ _Args&... __args) noexcept -> void {
+ __tag(std::move(__state->__receiver()),
+ static_cast<_Args&&>(__args)...);
+ },
+ __tupl);
+ }
+ };
+}
+
+// This receiver is to be completed on the execution context associated with the
+// scheduler. When the source sender completes, the completion information is
+// saved off in the operation state so that when this receiver completes, it can
+// read the completion out of the operation state and forward it to the output
+// receiver after transitioning to the scheduler's context.
+template <class _Scheduler, class _Sexpr, class _Receiver>
+struct __receiver2
+{
+ using receiver_concept = receiver_t;
+
+ void set_value() noexcept
+ {
+ __state_->__data_.visit(__schfr::__make_visitor_fn(__state_),
+ __state_->__data_);
+ }
+
+ template <class _Error>
+ void set_error(_Error&& __err) noexcept
+ {
+ stdexec::set_error(static_cast<_Receiver&&>(__state_->__receiver()),
+ static_cast<_Error&&>(__err));
+ }
+
+ void set_stopped() noexcept
+ {
+ stdexec::set_stopped(static_cast<_Receiver&&>(__state_->__receiver()));
+ }
+
+ auto get_env() const noexcept -> env_of_t<_Receiver>
+ {
+ return stdexec::get_env(__state_->__receiver());
+ }
+
+ __state<_Scheduler, _Sexpr, _Receiver>* __state_;
+};
+
+template <class _Scheduler, class _Sexpr, class _Receiver>
+struct __state : __enable_receiver_from_this<_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))
+ {}
+};
+
+struct schedule_from_t
+{
+ template <scheduler _Scheduler, sender _Sender>
+ auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
+ -> __well_formed_sender auto
+ {
+ using _Env = __t<__environ<__id<__decay_t<_Scheduler>>>>;
+ auto __env = _Env{{static_cast<_Scheduler&&>(__sched)}};
+ auto __domain = query_or(get_domain, __sched, default_domain());
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<schedule_from_t>(
+ std::move(__env), static_cast<_Sender&&>(__sndr)));
+ }
+
+ using _Sender = __1;
+ using _Env = __0;
+ using __legacy_customizations_t = __types<tag_invoke_t(
+ schedule_from_t, get_completion_scheduler_t<set_value_t>(_Env&),
+ _Sender)>;
+};
+
+struct __schedule_from_impl : __sexpr_defaults
+{
+ template <class _Sender>
+ using __scheduler_t =
+ __decay_t<__call_result_t<get_completion_scheduler_t<set_value_t>,
+ env_of_t<_Sender>>>;
+
+ static constexpr auto get_attrs = //
+ []<class _Data, class _Child>(const _Data& __data,
+ const _Child& __child) noexcept {
+ return __env::__join(__data, stdexec::get_env(__child));
+ };
+
+ static constexpr auto get_completion_signatures = //
+ []<class _Sender, class... _Env>(_Sender&&, _Env&&...) noexcept
+ -> __completions_t<__scheduler_t<_Sender>, __child_of<_Sender>,
+ _Env...> {
+ static_assert(sender_expr_for<_Sender, schedule_from_t>);
+ return {};
+ };
+
+ static constexpr auto get_state =
+ []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver&) {
+ static_assert(sender_expr_for<_Sender, schedule_from_t>);
+ auto __sched =
+ get_completion_scheduler<set_value_t>(stdexec::get_env(__sndr));
+ using _Scheduler = decltype(__sched);
+ return __state<_Scheduler, _Sender, _Receiver>{__sched};
+ };
+
+ static constexpr auto complete = //
+ []<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...>)
+ {
+ __state.__data_.emplace_from(__tup::__mktuple, __tag,
+ static_cast<_Args&&>(__args)...);
+ }
+ else
+ {
+ try
+ {
+ __state.__data_.emplace_from(__tup::__mktuple, __tag,
+ static_cast<_Args&&>(__args)...);
+ }
+ catch (...)
+ {
+ stdexec::set_error(static_cast<_Receiver&&>(__rcvr),
+ std::current_exception());
+ return;
+ }
+ }
+
+ // Enqueue the schedule operation so the completion happens on the
+ // scheduler's execution context.
+ stdexec::start(__state.__state2_);
+ };
+};
+} // namespace __schfr
+
+using __schfr::schedule_from_t;
+inline constexpr schedule_from_t schedule_from{};
+
+template <>
+struct __sexpr_impl<schedule_from_t> : __schfr::__schedule_from_impl
+{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__schedulers.hpp b/include/sdbusplus/async/stdexec/__detail/__schedulers.hpp
new file mode 100644
index 0000000..d29c080
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__schedulers.hpp
@@ -0,0 +1,161 @@
+/*
+ * 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 "__cpo.hpp"
+#include "__env.hpp"
+#include "__senders.hpp"
+#include "__tag_invoke.hpp"
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.schedule]
+namespace __sched
+{
+struct schedule_t
+{
+ template <__same_as<schedule_t> _Self, class _Scheduler>
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ friend auto tag_invoke(_Self, _Scheduler&& __sched) //
+ noexcept(noexcept(static_cast<_Scheduler&&>(__sched).schedule()))
+ -> decltype(static_cast<_Scheduler&&>(__sched).schedule())
+ {
+ static_assert(
+ sender<decltype(static_cast<_Scheduler&&>(__sched).schedule())>,
+ "schedule() member functions must return a sender");
+ return static_cast<_Scheduler&&>(__sched).schedule();
+ }
+
+ template <class _Scheduler>
+ requires tag_invocable<schedule_t, _Scheduler>
+ STDEXEC_ATTRIBUTE((host, device)) auto
+ operator()(_Scheduler&& __sched) const
+ noexcept(nothrow_tag_invocable<schedule_t, _Scheduler>)
+ {
+ static_assert(sender<tag_invoke_result_t<schedule_t, _Scheduler>>);
+ return tag_invoke(schedule_t{}, static_cast<_Scheduler&&>(__sched));
+ }
+
+ static constexpr auto query(forwarding_query_t) noexcept -> bool
+ {
+ return false;
+ }
+};
+} // namespace __sched
+
+using __sched::schedule_t;
+inline constexpr schedule_t schedule{};
+
+template <class _Scheduler>
+concept __has_schedule = //
+ requires(_Scheduler&& __sched) {
+ {
+ schedule(static_cast<_Scheduler&&>(__sched))
+ } -> sender;
+ };
+
+template <class _Scheduler>
+concept __sender_has_completion_scheduler =
+ requires(_Scheduler&& __sched) {
+ {
+ stdexec::__decay_copy(get_completion_scheduler<set_value_t>(
+ get_env(schedule(static_cast<_Scheduler&&>(__sched)))))
+ } -> same_as<__decay_t<_Scheduler>>;
+ };
+
+template <class _Scheduler>
+concept scheduler = //
+ __has_schedule<_Scheduler> //
+ && __sender_has_completion_scheduler<_Scheduler> //
+ && equality_comparable<__decay_t<_Scheduler>> //
+ && copy_constructible<__decay_t<_Scheduler>>;
+
+template <scheduler _Scheduler>
+using schedule_result_t = __call_result_t<schedule_t, _Scheduler>;
+
+template <class _SchedulerProvider>
+concept __scheduler_provider = //
+ requires(const _SchedulerProvider& __sp) {
+ {
+ get_scheduler(__sp)
+ } -> scheduler;
+ };
+
+namespace __queries
+{
+template <class _Env>
+ requires tag_invocable<get_scheduler_t, const _Env&>
+inline auto get_scheduler_t::operator()(const _Env& __env) const noexcept
+ -> tag_invoke_result_t<get_scheduler_t, const _Env&>
+{
+ static_assert(nothrow_tag_invocable<get_scheduler_t, const _Env&>);
+ static_assert(scheduler<tag_invoke_result_t<get_scheduler_t, const _Env&>>);
+ return tag_invoke(get_scheduler_t{}, __env);
+}
+
+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&>
+{
+ static_assert(
+ nothrow_tag_invocable<get_delegatee_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);
+}
+
+template <__completion_tag _Tag>
+template <__has_completion_scheduler_for<_Tag> _Env>
+auto get_completion_scheduler_t<_Tag>::operator()(
+ const _Env& __env) const noexcept
+ -> tag_invoke_result_t<get_completion_scheduler_t<_Tag>, const _Env&>
+{
+ static_assert(
+ nothrow_tag_invocable<get_completion_scheduler_t<_Tag>, const _Env&>,
+ "get_completion_scheduler<_Tag> should be noexcept");
+ static_assert(
+ scheduler<tag_invoke_result_t<get_completion_scheduler_t<_Tag>,
+ const _Env&>>);
+ return tag_invoke(*this, __env);
+}
+} // namespace __queries
+
+namespace __detail
+{
+// A handy utility for augmenting an environment with a scheduler.
+template <class _Env, class _Scheduler>
+STDEXEC_ATTRIBUTE((always_inline))
+auto __mkenv_sched(_Env&& __env, _Scheduler __sched)
+{
+ auto __env2 =
+ __env::__join(prop{get_scheduler, __sched},
+ __env::__without(static_cast<_Env&&>(__env), get_domain));
+ using _Env2 = decltype(__env2);
+
+ struct __env_t : _Env2
+ {};
+
+ return __env_t{static_cast<_Env2&&>(__env2)};
+}
+} // namespace __detail
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__sender_adaptor_closure.hpp b/include/sdbusplus/async/stdexec/__detail/__sender_adaptor_closure.hpp
new file mode 100644
index 0000000..5a7fa8d
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__sender_adaptor_closure.hpp
@@ -0,0 +1,143 @@
+/*
+ * 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 "__concepts.hpp"
+#include "__execution_fwd.hpp"
+#include "__senders_core.hpp"
+#include "__tuple.hpp"
+#include "__type_traits.hpp"
+
+namespace stdexec
+{
+// NOT TO SPEC:
+namespace __closure
+{
+template <__class _Dp>
+struct sender_adaptor_closure;
+} // namespace __closure
+
+using __closure::sender_adaptor_closure;
+
+template <class _Tp>
+concept __sender_adaptor_closure =
+ derived_from<__decay_t<_Tp>, sender_adaptor_closure<__decay_t<_Tp>>> &&
+ move_constructible<__decay_t<_Tp>> //
+ && constructible_from<__decay_t<_Tp>, _Tp>;
+
+template <class _Tp, class _Sender>
+concept __sender_adaptor_closure_for = //
+ __sender_adaptor_closure<_Tp> //
+ && sender<__decay_t<_Sender>> //
+ && __callable<_Tp, __decay_t<_Sender>> //
+ && sender<__call_result_t<_Tp, __decay_t<_Sender>>>;
+
+namespace __closure
+{
+template <class _T0, class _T1>
+struct __compose : sender_adaptor_closure<__compose<_T0, _T1>>
+{
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _T0 __t0_;
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _T1 __t1_;
+
+ template <sender _Sender>
+ requires __callable<_T0, _Sender> &&
+ __callable<_T1, __call_result_t<_T0, _Sender>>
+ STDEXEC_ATTRIBUTE((always_inline))
+ __call_result_t<_T1, __call_result_t<_T0, _Sender>>
+ operator()(_Sender&& __sndr) &&
+ {
+ return static_cast<_T1&&>(__t1_)(
+ static_cast<_T0&&>(__t0_)(static_cast<_Sender&&>(__sndr)));
+ }
+
+ template <sender _Sender>
+ requires __callable<const _T0&, _Sender> &&
+ __callable<const _T1&, __call_result_t<const _T0&, _Sender>>
+ STDEXEC_ATTRIBUTE((always_inline))
+ __call_result_t<_T1, __call_result_t<_T0, _Sender>>
+ operator()(_Sender&& __sndr) const&
+ {
+ return __t1_(__t0_(static_cast<_Sender&&>(__sndr)));
+ }
+};
+
+template <__class _Dp>
+struct sender_adaptor_closure
+{};
+
+template <sender _Sender, __sender_adaptor_closure_for<_Sender> _Closure>
+STDEXEC_ATTRIBUTE((always_inline))
+__call_result_t<_Closure, _Sender> operator|(_Sender&& __sndr,
+ _Closure&& __clsur)
+{
+ return static_cast<_Closure&&>(__clsur)(static_cast<_Sender&&>(__sndr));
+}
+
+template <__sender_adaptor_closure _T0, __sender_adaptor_closure _T1>
+STDEXEC_ATTRIBUTE((always_inline))
+__compose<__decay_t<_T0>, __decay_t<_T1>> operator|(_T0&& __t0, _T1&& __t1)
+{
+ return {{}, static_cast<_T0&&>(__t0), static_cast<_T1&&>(__t1)};
+}
+
+template <class _Fun, class... _As>
+struct __binder_back :
+ __tuple_for<_As...>,
+ sender_adaptor_closure<__binder_back<_Fun, _As...>>
+{
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _Fun __fun_{};
+
+ 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...>)
+ {
+ return this->apply(
+ [&__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);
+ }
+
+ template <sender _Sender>
+ requires __callable<const _Fun&, _Sender, const _As&...>
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) auto
+ operator()(_Sender&& __sndr) const& //
+ noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>)
+ -> __call_result_t<const _Fun&, _Sender, const _As&...>
+ {
+ return this->apply(
+ [&__sndr, this](const _As&... __as) noexcept(
+ __nothrow_callable<_Fun, _Sender, const _As&...>)
+ -> __call_result_t<const _Fun&, _Sender, const _As&...> {
+ return __fun_(static_cast<_Sender&&>(__sndr), __as...);
+ },
+ *this);
+ }
+};
+} // namespace __closure
+
+using __closure::__binder_back;
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__sender_introspection.hpp b/include/sdbusplus/async/stdexec/__detail/__sender_introspection.hpp
new file mode 100644
index 0000000..1c76e81
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__sender_introspection.hpp
@@ -0,0 +1,116 @@
+/*
+ * 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"
+
+namespace stdexec
+{
+namespace __detail
+{
+// A function object that is to senders what std::apply is to tuples:
+struct __sexpr_apply_t
+{
+ template <class _Sender, class _ApplyFn>
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto operator()(_Sender&& __sndr, _ApplyFn&& __fun) const //
+ noexcept(noexcept(__sndr.apply(static_cast<_Sender&&>(__sndr),
+ static_cast<_ApplyFn&&>(__fun)))) //
+ -> decltype(__sndr.apply(static_cast<_Sender&&>(__sndr),
+ static_cast<_ApplyFn&&>(__fun)))
+ {
+ return __sndr.apply(static_cast<_Sender&&>(__sndr),
+ static_cast<_ApplyFn&&>(__fun)); //
+ }
+};
+
+// A type that describes a sender's metadata
+template <class _Tag, class _Data, class... _Child>
+struct __desc
+{
+ using __tag = _Tag;
+ using __data = _Data;
+ using __children = __types<_Child...>;
+
+ template <class _Fn>
+ using __f = __minvoke<_Fn, _Tag, _Data, _Child...>;
+};
+
+template <class _Fn>
+struct __sexpr_uncurry_fn
+{
+ template <class _Tag, class _Data, class... _Child>
+ constexpr auto operator()(_Tag, _Data&&, _Child&&...) const noexcept
+ -> __minvoke<_Fn, _Tag, _Data, _Child...>;
+};
+
+template <class _CvrefSender, class _Fn>
+using __sexpr_uncurry =
+ __call_result_t<__sexpr_apply_t, _CvrefSender, __sexpr_uncurry_fn<_Fn>>;
+
+template <class _Sender>
+using __desc_of = __sexpr_uncurry<_Sender, __q<__desc>>;
+
+using __get_desc = __sexpr_uncurry_fn<__q<__desc>>;
+} // namespace __detail
+
+using __detail::__sexpr_apply_t;
+inline constexpr __sexpr_apply_t __sexpr_apply{};
+
+template <class _Sender, class _ApplyFn>
+using __sexpr_apply_result_t =
+ __call_result_t<__sexpr_apply_t, _Sender, _ApplyFn>;
+
+template <class _Sender>
+using tag_of_t = typename __detail::__desc_of<_Sender>::__tag;
+
+template <class _Sender>
+using __data_of = typename __detail::__desc_of<_Sender>::__data;
+
+template <class _Sender, class _Continuation = __q<__types>>
+using __children_of = //
+ __mapply<_Continuation, typename __detail::__desc_of<_Sender>::__children>;
+
+template <class _Ny, class _Sender>
+using __nth_child_of = __children_of<_Sender, __mbind_front_q<__m_at, _Ny>>;
+
+template <std::size_t _Ny, class _Sender>
+using __nth_child_of_c =
+ __children_of<_Sender, __mbind_front_q<__m_at, __msize_t<_Ny>>>;
+
+template <class _Sender>
+using __child_of = __children_of<_Sender, __q<__mfront>>;
+
+template <class _Sender>
+inline constexpr std::size_t __nbr_children_of =
+ __v<__children_of<_Sender, __msize>>;
+
+template <class _Tp>
+ requires __mvalid<tag_of_t, _Tp>
+struct __muncurry_<_Tp>
+{
+ template <class _Fn>
+ using __f = __detail::__sexpr_uncurry<_Tp, _Fn>;
+};
+
+template <class _Sender>
+concept sender_expr = //
+ __mvalid<tag_of_t, _Sender>;
+
+template <class _Sender, class _Tag>
+concept sender_expr_for = //
+ sender_expr<_Sender> && same_as<tag_of_t<_Sender>, _Tag>;
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__senders.hpp b/include/sdbusplus/async/stdexec/__detail/__senders.hpp
new file mode 100644
index 0000000..02d9ef2
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__senders.hpp
@@ -0,0 +1,420 @@
+/*
+ * 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 "__awaitable.hpp"
+#include "__completion_signatures.hpp"
+#include "__concepts.hpp"
+#include "__connect_awaitable.hpp"
+#include "__debug.hpp"
+#include "__env.hpp"
+#include "__operation_states.hpp"
+#include "__receivers.hpp"
+#include "__senders_core.hpp"
+#include "__transform_completion_signatures.hpp"
+#include "__transform_sender.hpp"
+#include "__type_traits.hpp"
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.get_completion_signatures]
+namespace __detail
+{
+struct __dependent_completions
+{};
+
+template <class _Completions>
+concept __well_formed_sender =
+ __valid_completion_signatures<_Completions> ||
+ __same_as<_Completions, _ERROR_<__dependent_completions>>;
+} // namespace __detail
+
+using dependent_completions = _ERROR_<__detail::__dependent_completions>;
+
+namespace __sigs
+{
+template <class _Sender, class _Env>
+using __tfx_sender =
+ transform_sender_result_t<__late_domain_of_t<_Sender, _Env>, _Sender, _Env>;
+
+template <class _Sender, class... _Env>
+using __member_result_t =
+ decltype(__declval<_Sender>().get_completion_signatures(
+ __declval<_Env>()...));
+
+template <class _Sender, class... _Env>
+using __static_member_result_t = //
+ decltype(STDEXEC_REMOVE_REFERENCE(_Sender) //
+ ::get_completion_signatures(__declval<_Sender>(),
+ __declval<_Env>()...));
+
+template <class _Sender, class... _Env>
+concept __with_member = __mvalid<__member_result_t, _Sender, _Env...>;
+
+template <class _Sender, class... _Env>
+concept __with_static_member =
+ __mvalid<__static_member_result_t, _Sender, _Env...>;
+
+template <class _Sender, class... _Env>
+concept __with_tag_invoke = //
+ tag_invocable<get_completion_signatures_t, _Sender, _Env...>;
+
+template <class _Sender, class... _Env>
+concept __with_legacy_tag_invoke = //
+ (sizeof...(_Env) == 0) &&
+ tag_invocable<get_completion_signatures_t, _Sender, empty_env>;
+
+template <class _Sender>
+using __member_alias_t = //
+ typename __decay_t<_Sender>::completion_signatures;
+
+template <class _Sender>
+concept __with_member_alias = __mvalid<__member_alias_t, _Sender>;
+
+struct get_completion_signatures_t
+{
+ template <class _Sender, class... _Env>
+ static auto __impl()
+ {
+ // Compute the type of the transformed sender:
+ using __tfx_fn =
+ __if_c<sizeof...(_Env) == 0, __mconst<_Sender>, __q<__tfx_sender>>;
+ using _TfxSender = __minvoke<__tfx_fn, _Sender, _Env...>;
+
+ if constexpr (__merror<_TfxSender>)
+ {
+ // Computing the type of the transformed sender returned an error
+ // type. Propagate it.
+ return static_cast<_TfxSender (*)()>(nullptr);
+ }
+ else if constexpr (__with_member_alias<_TfxSender>)
+ {
+ using _Result = __member_alias_t<_TfxSender>;
+ return static_cast<_Result (*)()>(nullptr);
+ }
+ else if constexpr (__with_static_member<_TfxSender, _Env...>)
+ {
+ using _Result = __static_member_result_t<_TfxSender, _Env...>;
+ return static_cast<_Result (*)()>(nullptr);
+ }
+ else if constexpr (__with_member<_TfxSender, _Env...>)
+ {
+ using _Result = __member_result_t<_TfxSender, _Env...>;
+ return static_cast<_Result (*)()>(nullptr);
+ }
+ else if constexpr (__with_tag_invoke<_TfxSender, _Env...>)
+ {
+ using _Result = tag_invoke_result_t<get_completion_signatures_t,
+ _TfxSender, _Env...>;
+ return static_cast<_Result (*)()>(nullptr);
+ }
+ else if constexpr (__with_legacy_tag_invoke<_TfxSender, _Env...>)
+ {
+ // This branch is strictly for backwards compatibility
+ using _Result = tag_invoke_result_t<get_completion_signatures_t,
+ _Sender, empty_env>;
+ return static_cast<_Result (*)()>(nullptr);
+ // [WAR] The explicit cast to bool below is to work around a bug in
+ // nvc++ (nvbug#4707793)
+ }
+ else if constexpr (bool(__awaitable<_TfxSender,
+ __env::__promise<_Env>...>))
+ {
+ using _AwaitResult =
+ __await_result_t<_TfxSender, __env::__promise<_Env>...>;
+ using _Result = completion_signatures<
+ // set_value_t() or set_value_t(T)
+ __minvoke<__mremove<void, __qf<set_value_t>>, _AwaitResult>,
+ set_error_t(std::exception_ptr), set_stopped_t()>;
+ return static_cast<_Result (*)()>(nullptr);
+ }
+ else if constexpr (sizeof...(_Env) == 0)
+ {
+ // It's possible this is a dependent sender.
+ return static_cast<dependent_completions (*)()>(nullptr);
+ }
+ else if constexpr ((__is_debug_env<_Env> || ...))
+ {
+ using __tag_invoke::tag_invoke;
+ // This ought to cause a hard error that indicates where the problem
+ // is.
+ using _Completions [[maybe_unused]] =
+ tag_invoke_result_t<get_completion_signatures_t, _Sender,
+ _Env...>;
+ return static_cast<__debug::__completion_signatures (*)()>(nullptr);
+ }
+ else
+ {
+ using _Result = __mexception<_UNRECOGNIZED_SENDER_TYPE_<>,
+ _WITH_SENDER_<_Sender>,
+ _WITH_ENVIRONMENT_<_Env>...>;
+ return static_cast<_Result (*)()>(nullptr);
+ }
+ }
+
+ // NOT TO SPEC: if we're unable to compute the completion signatures,
+ // return an error type instead of SFINAE.
+ template <class _Sender, class... _Env>
+ requires(sizeof...(_Env) <= 1)
+ constexpr auto operator()(_Sender&&, _Env&&...) const noexcept //
+ -> decltype(__impl<_Sender, _Env...>()())
+ {
+ return {};
+ }
+};
+} // namespace __sigs
+
+using __sigs::get_completion_signatures_t;
+inline constexpr get_completion_signatures_t get_completion_signatures{};
+
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.connect]
+namespace __connect
+{
+template <class _Sender, class _Receiver>
+using __tfx_sender = //
+ transform_sender_result_t<__late_domain_of_t<_Sender, env_of_t<_Receiver>>,
+ _Sender, env_of_t<_Receiver>>;
+
+template <class _Sender, class _Receiver>
+using __member_result_t =
+ decltype(__declval<_Sender>().connect(__declval<_Receiver>()));
+
+template <class _Sender, class _Receiver>
+using __static_member_result_t =
+ decltype(STDEXEC_REMOVE_REFERENCE(_Sender) //
+ ::connect(__declval<_Sender>(), __declval<_Receiver>()));
+
+template <class _Sender, class _Receiver>
+concept __with_member = __mvalid<__member_result_t, _Sender, _Receiver>;
+
+template <class _Sender, class _Receiver>
+concept __with_static_member =
+ __mvalid<__static_member_result_t, _Sender, _Receiver>;
+
+template <class _Sender, class _Receiver>
+concept __with_tag_invoke = tag_invocable<connect_t, _Sender, _Receiver>;
+
+template <class _Sender, class _Receiver>
+concept __with_co_await = __callable<__connect_awaitable_t, _Sender, _Receiver>;
+
+struct connect_t
+{
+ template <class _Sender, class _Env>
+ static constexpr auto __check_signatures() -> bool
+ {
+ if constexpr (sender_in<_Sender, _Env>)
+ {
+ // Instantiate __debug_sender via completion_signatures_of_t to
+ // check that the actual completions match the expected completions.
+ //
+ // Instantiate completion_signatures_of_t only if sender_in is true
+ // to workaround Clang not implementing CWG#2369 yet (connect() does
+ // not have a constraint for _Sender satisfying sender_in).
+ using __checked_signatures
+ [[maybe_unused]] = completion_signatures_of_t<_Sender, _Env>;
+ }
+ return true;
+ }
+
+ template <class _Sender, class _Receiver>
+ static constexpr auto __select_impl() noexcept
+ {
+ using _Domain = __late_domain_of_t<_Sender, env_of_t<_Receiver>>;
+ using _TfxSender = __tfx_sender<_Sender, _Receiver>;
+ constexpr bool _NothrowTfxSender =
+ __nothrow_callable<transform_sender_t, _Domain, _Sender,
+ env_of_t<_Receiver>>;
+
+#if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING()
+ static_assert(__check_signatures<_TfxSender, env_of_t<_Receiver>>());
+#endif
+
+ if constexpr (__with_static_member<_TfxSender, _Receiver>)
+ {
+ using _Result = __static_member_result_t<_TfxSender, _Receiver>;
+ constexpr bool _Nothrow = //
+ _NothrowTfxSender&& noexcept(__declval<_TfxSender>().connect(
+ __declval<_TfxSender>(), __declval<_Receiver>()));
+ return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
+ }
+ else if constexpr (__with_member<_TfxSender, _Receiver>)
+ {
+ using _Result = __member_result_t<_TfxSender, _Receiver>;
+ constexpr bool _Nothrow = //
+ _NothrowTfxSender&& noexcept(
+ __declval<_TfxSender>().connect(__declval<_Receiver>()));
+ return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
+ }
+ else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>)
+ {
+ using _Result =
+ tag_invoke_result_t<connect_t, _TfxSender, _Receiver>;
+ constexpr bool _Nothrow = //
+ _NothrowTfxSender &&
+ nothrow_tag_invocable<connect_t, _TfxSender, _Receiver>;
+ return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
+ }
+ else if constexpr (__with_co_await<_TfxSender, _Receiver>)
+ {
+ using _Result =
+ __call_result_t<__connect_awaitable_t, _TfxSender, _Receiver>;
+ return static_cast<_Result (*)()>(nullptr);
+ }
+ else
+ {
+ using _Result = __debug::__debug_operation;
+ return static_cast<_Result (*)() noexcept(_NothrowTfxSender)>(
+ nullptr);
+ }
+ }
+
+ template <class _Sender, class _Receiver>
+ using __select_impl_t = decltype(__select_impl<_Sender, _Receiver>());
+
+ template <sender _Sender, receiver _Receiver>
+ requires __with_static_member<__tfx_sender<_Sender, _Receiver>,
+ _Receiver> ||
+ __with_member<__tfx_sender<_Sender, _Receiver>, _Receiver> ||
+ __with_tag_invoke<__tfx_sender<_Sender, _Receiver>,
+ _Receiver> ||
+ __with_co_await<__tfx_sender<_Sender, _Receiver>, _Receiver> ||
+ __is_debug_env<env_of_t<_Receiver>>
+ auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const
+ noexcept(__nothrow_callable<__select_impl_t<_Sender, _Receiver>>)
+ -> __call_result_t<__select_impl_t<_Sender, _Receiver>>
+ {
+ using _TfxSender = __tfx_sender<_Sender, _Receiver>;
+ auto&& __env = get_env(__rcvr);
+ auto __domain = __get_late_domain(__sndr, __env);
+
+ if constexpr (__with_static_member<_TfxSender, _Receiver>)
+ {
+ static_assert(
+ operation_state<
+ __static_member_result_t<_TfxSender, _Receiver>>,
+ "Sender::connect(sender, receiver) must return a type that "
+ "satisfies the operation_state concept");
+ auto&& __tfx_sndr = transform_sender(
+ __domain, static_cast<_Sender&&>(__sndr), __env);
+ return __tfx_sndr.connect(static_cast<_TfxSender&&>(__tfx_sndr),
+ static_cast<_Receiver&&>(__rcvr));
+ }
+ else if constexpr (__with_member<_TfxSender, _Receiver>)
+ {
+ static_assert(
+ operation_state<__member_result_t<_TfxSender, _Receiver>>,
+ "sender.connect(receiver) must return a type that "
+ "satisfies the operation_state concept");
+ return transform_sender(__domain, static_cast<_Sender&&>(__sndr),
+ __env)
+ .connect(static_cast<_Receiver&&>(__rcvr));
+ }
+ else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>)
+ {
+ static_assert(
+ operation_state<
+ tag_invoke_result_t<connect_t, _TfxSender, _Receiver>>,
+ "stdexec::connect(sender, receiver) must return a type that "
+ "satisfies the operation_state concept");
+ return tag_invoke(connect_t(),
+ transform_sender(__domain,
+ static_cast<_Sender&&>(__sndr),
+ __env),
+ static_cast<_Receiver&&>(__rcvr));
+ }
+ else if constexpr (__with_co_await<_TfxSender, _Receiver>)
+ {
+ return __connect_awaitable( //
+ transform_sender(__domain, static_cast<_Sender&&>(__sndr),
+ __env),
+ static_cast<_Receiver&&>(__rcvr));
+ }
+ else
+ {
+ // This should generate an instantiation backtrace that contains
+ // useful debugging information.
+ using __tag_invoke::tag_invoke;
+ tag_invoke(*this,
+ transform_sender(__domain,
+ static_cast<_Sender&&>(__sndr), __env),
+ static_cast<_Receiver&&>(__rcvr));
+ }
+ }
+
+ static constexpr auto query(forwarding_query_t) noexcept -> bool
+ {
+ return false;
+ }
+};
+} // namespace __connect
+
+using __connect::connect_t;
+inline constexpr __connect::connect_t connect{};
+
+/////////////////////////////////////////////////////////////////////////////
+// [exec.snd]
+template <class _Sender, class _Receiver>
+concept sender_to = //
+ receiver<_Receiver> //
+ && sender_in<_Sender, env_of_t<_Receiver>> //
+ && __receiver_from<_Receiver, _Sender> //
+ && requires(_Sender&& __sndr, _Receiver&& __rcvr) {
+ connect(static_cast<_Sender&&>(__sndr),
+ static_cast<_Receiver&&>(__rcvr));
+ };
+
+template <class _Tag, class... _Args>
+auto __tag_of_sig_(_Tag (*)(_Args...)) -> _Tag;
+template <class _Sig>
+using __tag_of_sig_t =
+ decltype(stdexec::__tag_of_sig_(static_cast<_Sig*>(nullptr)));
+
+template <class _Sender, class _SetSig, class _Env = empty_env>
+concept sender_of = //
+ sender_in<_Sender, _Env> //
+ &&
+ same_as<
+ __types<_SetSig>,
+ __gather_completions_of<
+ __tag_of_sig_t<_SetSig>, _Sender, _Env,
+ __mcompose_q<__types, __qf<__tag_of_sig_t<_SetSig>>::template __f>,
+ __mconcat<__qq<__types>>>>;
+
+template <class _Error>
+ requires false
+using __nofail_t = _Error;
+
+template <class _Sender, class _Env = empty_env>
+concept __nofail_sender =
+ sender_in<_Sender, _Env> &&
+ requires {
+ typename __gather_completion_signatures<
+ __completion_signatures_of_t<_Sender, _Env>, set_error_t,
+ __nofail_t, __sigs::__default_completion, __types>;
+ };
+
+/////////////////////////////////////////////////////////////////////////////
+// early sender type-checking
+template <class _Sender>
+concept __well_formed_sender = __detail::__well_formed_sender<__minvoke<
+ __with_default_q<__completion_signatures_of_t, dependent_completions>,
+ _Sender>>;
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__senders_core.hpp b/include/sdbusplus/async/stdexec/__detail/__senders_core.hpp
new file mode 100644
index 0000000..e711b06
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__senders_core.hpp
@@ -0,0 +1,68 @@
+/*
+ * 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 "__awaitable.hpp"
+#include "__completion_signatures.hpp"
+#include "__concepts.hpp"
+#include "__domain.hpp"
+#include "__env.hpp"
+#include "__receivers.hpp"
+#include "__type_traits.hpp"
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders]
+struct sender_t
+{
+ using sender_concept = sender_t;
+};
+
+namespace __detail
+{
+template <class _Sender>
+concept __enable_sender = //
+ derived_from<typename _Sender::sender_concept, sender_t> ||
+ requires { typename _Sender::is_sender; } // NOT TO SPEC back compat
+ || __awaitable<_Sender, __env::__promise<empty_env>>;
+} // namespace __detail
+
+template <class _Sender>
+inline constexpr bool enable_sender = __detail::__enable_sender<_Sender>;
+
+template <class _Sender>
+concept sender = //
+ enable_sender<__decay_t<_Sender>> //
+ && environment_provider<__cref_t<_Sender>> //
+ && __detail::__consistent_completion_domains<_Sender> //
+ && move_constructible<__decay_t<_Sender>> //
+ && constructible_from<__decay_t<_Sender>, _Sender>;
+
+template <class _Sender, class... _Env>
+concept sender_in =
+ (sizeof...(_Env) <= 1) //
+ && sender<_Sender> //
+ && requires(_Sender&& __sndr, _Env&&... __env) {
+ {
+ get_completion_signatures(static_cast<_Sender&&>(__sndr),
+ static_cast<_Env&&>(__env)...)
+ } -> __valid_completion_signatures;
+ };
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__shared.hpp b/include/sdbusplus/async/stdexec/__detail/__shared.hpp
new file mode 100644
index 0000000..49f9473
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__shared.hpp
@@ -0,0 +1,543 @@
+/*
+ * 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 "../functional.hpp"
+#include "../stop_token.hpp"
+#include "__basic_sender.hpp"
+#include "__cpo.hpp"
+#include "__env.hpp"
+#include "__intrusive_ptr.hpp"
+#include "__intrusive_slist.hpp"
+#include "__meta.hpp"
+#include "__optional.hpp"
+#include "__transform_completion_signatures.hpp"
+#include "__tuple.hpp"
+#include "__variant.hpp"
+
+#include <exception>
+#include <mutex>
+
+namespace stdexec
+{
+////////////////////////////////////////////////////////////////////////////
+// shared components of split and ensure_started
+//
+// The split and ensure_started algorithms are very similar in implementation.
+// The salient differences are:
+//
+// split: the input async operation is always connected. It is only
+// started when one of the split senders is connected and started.
+// split senders are copyable, so there are multiple operation states
+// to be notified on completion. These are stored in an instrusive
+// linked list.
+//
+// ensure_started: the input async operation is always started, so
+// the internal receiver will always be completed. The ensure_started
+// sender is move-only and single-shot, so there will only ever be one
+// operation state to be notified on completion.
+//
+// The shared state should add-ref itself when the input async
+// operation is started and release itself when its completion
+// is notified.
+namespace __shared
+{
+template <class _BaseEnv>
+using __env_t = //
+ __env::__join_t<prop<get_stop_token_t, inplace_stop_token>,
+ _BaseEnv>; // BUGBUG NOT TO SPEC
+
+template <class _Receiver>
+auto __make_notify_visitor(_Receiver& __rcvr) noexcept
+{
+ return [&]<class _Tuple>(_Tuple&& __tupl) noexcept -> void {
+ __tupl.apply(
+ [&](auto __tag, auto&&... __args) noexcept -> void {
+ __tag(static_cast<_Receiver&&>(__rcvr),
+ __forward_like<_Tuple>(__args)...);
+ },
+ __tupl);
+ };
+}
+
+struct __local_state_base : __immovable
+{
+ using __notify_fn = void(__local_state_base*) noexcept;
+
+ __notify_fn* __notify_{};
+ __local_state_base* __next_{};
+};
+
+template <class _CvrefSender, class _Env>
+struct __shared_state;
+
+// The operation state of ensure_started, and each operation state of split, has
+// one of these, created when the sender is connected. There are 0 or more of
+// them for each underlying async operation. It is what ensure_started- and
+// split-sender's `get_state` fn returns. It holds a ref count to the shared
+// state.
+template <class _CvrefSender, class _Receiver>
+struct __local_state :
+ __local_state_base,
+ __enable_receiver_from_this<_CvrefSender, _Receiver>
+{
+ using __tag_t = tag_of_t<_CvrefSender>;
+ using __stok_t = stop_token_of_t<env_of_t<_Receiver>>;
+ static_assert(__one_of<__tag_t, __split::__split_t,
+ __ensure_started::__ensure_started_t>);
+
+ explicit __local_state(_CvrefSender&& __sndr) noexcept :
+ __local_state::__local_state_base{{},
+ &__notify<tag_of_t<_CvrefSender>>},
+ __sh_state_(__get_sh_state(__sndr))
+ {}
+
+ ~__local_state()
+ {
+ __sh_state_t::__detach(__sh_state_);
+ }
+
+ // Stop request callback:
+ void operator()() noexcept
+ {
+ // We reach here when a split/ensure_started sender has received a stop
+ // request from the receiver to which it is connected.
+ if (std::unique_lock __lock{__sh_state_->__mutex_})
+ {
+ // Remove this operation from the waiters list. Removal can fail if:
+ // 1. It was already removed by another thread, or
+ // 2. It hasn't been added yet (see `start` below), or
+ // 3. The underlying operation has already completed.
+ //
+ // In each case, the right thing to do is nothing. If (1) then we
+ // raced with another thread and lost. In that case, the other
+ // thread will take care of it. If (2) then `start` will take care
+ // of it. If (3) then this stop request is safe to ignore.
+ if (!__sh_state_->__waiters_.remove(this))
+ return;
+ }
+
+ // The following code and the __notify function cannot both execute.
+ // This is because the
+ // __notify function is called from the shared state's __notify_waiters
+ // function, which first sets __waiters_ to the completed state. As a
+ // result, the attempt to remove `this` from the waiters list above will
+ // fail and this stop request is ignored.
+ __sh_state_t::__detach(__sh_state_);
+ stdexec::set_stopped(static_cast<_Receiver&&>(this->__receiver()));
+ }
+
+ // This is called from __shared_state::__notify_waiters when the input async
+ // operation completes; or, if it has already completed when start is
+ // called, it is called from start:
+ // __notify cannot race with __on_stop_request. See comment in
+ // __on_stop_request.
+ template <class _Tag>
+ static void __notify(__local_state_base* __base) noexcept
+ {
+ auto* const __self = static_cast<__local_state*>(__base);
+
+ // The split algorithm sends by T const&. ensure_started sends by T&&.
+ constexpr bool __is_split = same_as<__split::__split_t, _Tag>;
+ using __variant_t = decltype(__self->__sh_state_->__results_);
+ using __cv_variant_t =
+ __if_c<__is_split, const __variant_t&, __variant_t>;
+
+ __self->__on_stop_.reset();
+
+ auto __visitor = __make_notify_visitor(__self->__receiver());
+ __variant_t::visit(__visitor, static_cast<__cv_variant_t&&>(
+ __self->__sh_state_->__results_));
+ }
+
+ static auto __get_sh_state(_CvrefSender& __sndr) noexcept
+ {
+ return __sndr
+ .apply(static_cast<_CvrefSender&&>(__sndr), __detail::__get_data())
+ .__sh_state_;
+ }
+
+ using __sh_state_ptr_t = __result_of<__get_sh_state, _CvrefSender&>;
+ using __sh_state_t = typename __sh_state_ptr_t::element_type;
+
+ __optional<stop_callback_for_t<__stok_t, __local_state&>> __on_stop_{};
+ __sh_state_ptr_t __sh_state_;
+};
+
+template <class _CvrefSenderId, class _EnvId>
+struct __receiver
+{
+ using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
+ using _Env = stdexec::__t<_EnvId>;
+
+ struct __t
+ {
+ using receiver_concept = receiver_t;
+ using __id = __receiver;
+
+ template <class... _As>
+ STDEXEC_ATTRIBUTE((always_inline))
+ void set_value(_As&&... __as) noexcept
+ {
+ __sh_state_->__complete(set_value_t(), static_cast<_As&&>(__as)...);
+ }
+
+ template <class _Error>
+ STDEXEC_ATTRIBUTE((always_inline))
+ void set_error(_Error&& __err) noexcept
+ {
+ __sh_state_->__complete(set_error_t(),
+ static_cast<_Error&&>(__err));
+ }
+
+ STDEXEC_ATTRIBUTE((always_inline))
+ void set_stopped() noexcept
+ {
+ __sh_state_->__complete(set_stopped_t());
+ }
+
+ auto get_env() const noexcept -> const __env_t<_Env>&
+ {
+ return __sh_state_->__env_;
+ }
+
+ // The receiver does not hold a reference to the shared state.
+ __shared_state<_CvrefSender, _Env>* __sh_state_;
+ };
+};
+
+inline __local_state_base* __get_tombstone() noexcept
+{
+ static __local_state_base __tombstone_{{}, nullptr, nullptr};
+ return &__tombstone_;
+}
+
+template <class _CvrefSender, class _Env>
+struct __shared_state :
+ private __enable_intrusive_from_this<__shared_state<_CvrefSender, _Env>, 2>
+{
+ using __receiver_t = __t<__receiver<__cvref_id<_CvrefSender>, __id<_Env>>>;
+ using __waiters_list_t = __intrusive_slist<&__local_state_base::__next_>;
+
+ using __variant_t = //
+ __transform_completion_signatures<
+ __completion_signatures_of_t<_CvrefSender, _Env>,
+ __mbind_front_q<__decayed_tuple, set_value_t>::__f,
+ __mbind_front_q<__decayed_tuple, set_error_t>::__f,
+ __tuple_for<set_error_t, std::exception_ptr>,
+ __munique<__mbind_front_q<__variant_for,
+ __tuple_for<set_stopped_t>>>::__f,
+ __tuple_for<set_error_t, std::exception_ptr>>;
+
+ static constexpr std::size_t __started_bit = 0;
+ static constexpr std::size_t __completed_bit = 1;
+
+ inplace_stop_source __stop_source_{};
+ __env_t<_Env> __env_;
+ __variant_t __results_{}; // Defaults to the "set_stopped" state
+ std::mutex __mutex_; // This mutex guards access to __waiters_.
+ __waiters_list_t __waiters_{};
+ connect_result_t<_CvrefSender, __receiver_t> __shared_op_;
+
+ explicit __shared_state(_CvrefSender&& __sndr, _Env __env) :
+ __env_(__env::__join(prop{get_stop_token, __stop_source_.get_token()},
+ static_cast<_Env&&>(__env))),
+ __shared_op_(
+ connect(static_cast<_CvrefSender&&>(__sndr), __receiver_t{this}))
+ {
+ // add one ref count to account for the case where there are no watchers
+ // left but the shared op is still running.
+ this->__inc_ref();
+ }
+
+ // The caller of this wants to release their reference to the shared state.
+ // The ref count must be at least 2 at this point: one owned by the caller,
+ // and one added in the
+ // __shared_state ctor.
+ static void __detach(__intrusive_ptr<__shared_state, 2>& __ptr) noexcept
+ {
+ // Ask the intrusive ptr to stop managing the reference count so we can
+ // manage it manually.
+ if (auto* __self = __ptr.__release_())
+ {
+ auto __old = __self->__dec_ref();
+ STDEXEC_ASSERT(__count(__old) >= 2);
+
+ if (__count(__old) == 2)
+ {
+ // The last watcher has released its reference. Asked the shared
+ // op to stop.
+ static_cast<__shared_state*>(__self)
+ ->__stop_source_.request_stop();
+
+ // Additionally, if the shared op was never started, or if it
+ // has already completed, then the shared state is no longer
+ // needed. Decrement the ref count to 0 here, which will delete
+ // __self.
+ if (!__bit<__started_bit>(__old) ||
+ __bit<__completed_bit>(__old))
+ {
+ __self->__dec_ref();
+ }
+ }
+ }
+ }
+
+ /// @post The started bit is set in the shared state's ref count, OR the
+ /// __waiters_ list is set to the known "tombstone" value indicating
+ /// completion.
+ void __try_start() noexcept
+ {
+ // With the split algorithm, multiple split senders can be started
+ // simultaneously, but only one should start the shared async operation.
+ // If the "started" bit is set, then someone else has already started
+ // the shared operation. Do nothing.
+ if (this->template __is_set<__started_bit>())
+ {
+ return;
+ }
+ else if (__bit<__started_bit>(
+ this->template __set_bit<__started_bit>()))
+ {
+ return;
+ }
+ else if (__stop_source_.stop_requested())
+ {
+ // Stop has already been requested. Rather than starting the
+ // operation, complete with set_stopped immediately.
+ // 1. Sets __waiters_ to a known "tombstone" value
+ // 2. Notifies all the waiters that the operation has stopped
+ // 3. Sets the "completed" bit in the ref count.
+ __notify_waiters();
+ return;
+ }
+ else
+ {
+ stdexec::start(__shared_op_);
+ }
+ }
+
+ template <class _StopToken>
+ bool __try_add_waiter(__local_state_base* __waiter,
+ _StopToken __stok) noexcept
+ {
+ std::unique_lock __lock{__mutex_};
+ if (__waiters_.front() == __get_tombstone())
+ {
+ // The work has already completed. Notify the waiter immediately.
+ __lock.unlock();
+ __waiter->__notify_(__waiter);
+ return true;
+ }
+ else if (__stok.stop_requested())
+ {
+ // Stop has been requested. Do not add the waiter.
+ return false;
+ }
+ else
+ {
+ // Add the waiter to the list.
+ __waiters_.push_front(__waiter);
+ return true;
+ }
+ }
+
+ /// @brief This is called when the shared async operation completes.
+ /// @post __waiters_ is set to a known "tombstone" value.
+ template <class _Tag, class... _As>
+ void __complete(_Tag, _As&&... __as) noexcept
+ {
+ try
+ {
+ using __tuple_t = __decayed_tuple<_Tag, _As...>;
+ __results_.template emplace<__tuple_t>(_Tag(),
+ static_cast<_As&&>(__as)...);
+ }
+ catch (...)
+ {
+ using __tuple_t = __decayed_tuple<set_error_t, std::exception_ptr>;
+ __results_.template emplace<__tuple_t>(set_error,
+ std::current_exception());
+ }
+
+ __notify_waiters();
+ }
+
+ /// @brief This is called when the shared async operation completes.
+ /// @post __waiters_ is set to a known "tombstone" value.
+ void __notify_waiters() noexcept
+ {
+ __waiters_list_t __waiters_copy{__get_tombstone()};
+
+ // Set the waiters list to a known "tombstone" value that we can check
+ // later.
+ {
+ std::lock_guard __lock{__mutex_};
+ __waiters_.swap(__waiters_copy);
+ }
+
+ STDEXEC_ASSERT(__waiters_copy.front() != __get_tombstone());
+ for (auto __itr = __waiters_copy.begin();
+ __itr != __waiters_copy.end();)
+ {
+ __local_state_base* __item = *__itr;
+
+ // We must increment the iterator before calling notify, since
+ // notify may end up triggering *__item to be destructed on another
+ // thread, and the intrusive slist's iterator increment relies on
+ // __item.
+ ++__itr;
+
+ __item->__notify_(__item);
+ }
+
+ // Set the "completed" bit in the ref count. If the ref count is 1, then
+ // there are no more waiters. Release the final reference.
+ if (__count(this->template __set_bit<__completed_bit>()) == 1)
+ {
+ this->__dec_ref(); // release the extra ref count, deletes this
+ }
+ }
+};
+
+template <class _Cvref, class _CvrefSender, class _Env>
+using __make_completions = //
+ __try_make_completion_signatures<
+ // NOT TO SPEC:
+ // See https://github.com/cplusplus/sender-receiver/issues/23
+ _CvrefSender, __env_t<_Env>,
+ completion_signatures<set_error_t(
+ __minvoke<_Cvref, std::exception_ptr>),
+ set_stopped_t()>, // NOT TO SPEC
+ __mtransform<_Cvref,
+ __mcompose<__q<completion_signatures>, __qf<set_value_t>>>,
+ __mtransform<
+ _Cvref, __mcompose<__q<completion_signatures>, __qf<set_error_t>>>>;
+
+// split completes with const T&. ensure_started completes with T&&.
+template <class _Tag>
+using __cvref_results_t = //
+ __mcompose<__if_c<same_as<_Tag, __split::__split_t>, __cpclr, __cp>,
+ __q<__decay_t>>;
+
+// NOTE: the use of __mapply in the return type below takes advantage of the
+// fact that _ShState denotes an instance of the __shared_state template, which
+// is parameterized on the cvref-qualified sender and the environment.
+template <class _Tag, class _ShState>
+using __completions = //
+ __mapply<__mbind_front_q<__make_completions, __cvref_results_t<_Tag>>,
+ _ShState>;
+
+template <class _CvrefSender, class _Env, bool _Copyable = true>
+struct __box
+{
+ using __tag_t = __if_c<_Copyable, __split::__split_t,
+ __ensure_started::__ensure_started_t>;
+ using __sh_state_t = __shared_state<_CvrefSender, _Env>;
+
+ __box(__tag_t, __intrusive_ptr<__sh_state_t, 2> __sh_state) noexcept :
+ __sh_state_(std::move(__sh_state))
+ {}
+
+ __box(__box&&) noexcept = default;
+ __box(const __box&) noexcept
+ requires _Copyable
+ = default;
+
+ ~__box()
+ {
+ __sh_state_t::__detach(__sh_state_);
+ }
+
+ __intrusive_ptr<__sh_state_t, 2> __sh_state_;
+};
+
+template <class _CvrefSender, class _Env>
+__box(__split::__split_t,
+ __intrusive_ptr<__shared_state<_CvrefSender, _Env>, 2>) //
+ ->__box<_CvrefSender, _Env, true>;
+
+template <class _CvrefSender, class _Env>
+__box(__ensure_started::__ensure_started_t,
+ __intrusive_ptr<__shared_state<_CvrefSender, _Env>, 2>)
+ -> __box<_CvrefSender, _Env, false>;
+
+template <class _Tag>
+struct __shared_impl : __sexpr_defaults
+{
+ static constexpr auto get_state = //
+ []<class _CvrefSender, class _Receiver>(
+ _CvrefSender&& __sndr,
+ _Receiver&) noexcept -> __local_state<_CvrefSender, _Receiver> {
+ static_assert(sender_expr_for<_CvrefSender, _Tag>);
+ return __local_state<_CvrefSender, _Receiver>{
+ static_cast<_CvrefSender&&>(__sndr)};
+ };
+
+ static constexpr auto get_completion_signatures = //
+ []<class _Self>(const _Self&, auto&&...) noexcept
+ -> __completions<_Tag, typename __data_of<_Self>::__sh_state_t> {
+ static_assert(sender_expr_for<_Self, _Tag>);
+ return {};
+ };
+
+ static constexpr auto start = //
+ []<class _Sender, class _Receiver>(
+ __local_state<_Sender, _Receiver>& __self,
+ _Receiver& __rcvr) noexcept -> void {
+ using __sh_state_t =
+ typename __local_state<_Sender, _Receiver>::__sh_state_t;
+ // Scenario: there are no more split senders, this is the only operation
+ // state, the underlying operation has not yet been started, and the
+ // receiver's stop token is already in the "stop requested" state. Then
+ // registering the stop callback will call
+ // __on_stop_request on __self synchronously. It may also be called
+ // asynchronously at any point after the callback is registered. Beware.
+ // We are guaranteed, however, that
+ // __on_stop_request will not complete the operation or decrement the
+ // shared state's ref count until after __self has been added to the
+ // waiters list.
+ const auto __stok = stdexec::get_stop_token(stdexec::get_env(__rcvr));
+ __self.__on_stop_.emplace(__stok, __self);
+
+ // We haven't put __self in the waiters list yet and we are holding a
+ // ref count to
+ // __sh_state_, so nothing can happen to the __sh_state_ here.
+
+ // Start the shared op. As an optimization, skip it if the receiver's
+ // stop token has already been signaled.
+ if (!__stok.stop_requested())
+ {
+ __self.__sh_state_->__try_start();
+ if (__self.__sh_state_->__try_add_waiter(&__self, __stok))
+ {
+ // successfully added the waiter
+ return;
+ }
+ }
+
+ // Otherwise, failed to add the waiter because of a stop-request.
+ // Complete synchronously with set_stopped().
+ __self.__on_stop_.reset();
+ __sh_state_t::__detach(__self.__sh_state_);
+ stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr));
+ };
+};
+} // namespace __shared
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__spin_loop_pause.hpp b/include/sdbusplus/async/stdexec/__detail/__spin_loop_pause.hpp
new file mode 100644
index 0000000..7cae36f
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__spin_loop_pause.hpp
@@ -0,0 +1,68 @@
+/*
+ * 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 "../../stdexec/__detail/__config.hpp"
+
+// The below code for spin_loop_pause is taken from
+// https://github.com/max0x7ba/atomic_queue/blob/master/include/atomic_queue/defs.h
+// Copyright (c) 2019 Maxim Egorushkin. MIT License.
+
+#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \
+ defined(_M_IX86)
+#if STDEXEC_MSVC()
+#include <intrin.h>
+#endif
+namespace stdexec
+{
+STDEXEC_ATTRIBUTE((always_inline))
+static void __spin_loop_pause() noexcept
+{
+#if STDEXEC_MSVC()
+ _mm_pause();
+#else
+ __builtin_ia32_pause();
+#endif
+}
+} // namespace stdexec
+#elif defined(__arm__) || defined(__aarch64__) || defined(_M_ARM64)
+namespace stdexec
+{
+STDEXEC_ATTRIBUTE((always_inline))
+static void __spin_loop_pause() noexcept
+{
+#if (defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || \
+ defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) || \
+ defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || \
+ defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || \
+ defined(__ARM_ARCH_7S__) || defined(__ARM_ARCH_8A__) || \
+ defined(__aarch64__))
+ asm volatile("yield" ::: "memory");
+#elif defined(_M_ARM64)
+ __yield();
+#else
+ asm volatile("nop" ::: "memory");
+#endif
+}
+} // namespace stdexec
+#else
+namespace stdexec
+{
+STDEXEC_ATTRIBUTE((always_inline))
+static void __spin_loop_pause() noexcept {}
+} // namespace stdexec
+#endif
diff --git a/include/sdbusplus/async/stdexec/__detail/__split.hpp b/include/sdbusplus/async/stdexec/__detail/__split.hpp
new file mode 100644
index 0000000..371bb82
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__split.hpp
@@ -0,0 +1,113 @@
+/*
+ * 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 "__intrusive_ptr.hpp"
+#include "__meta.hpp"
+#include "__sender_adaptor_closure.hpp"
+#include "__senders.hpp"
+#include "__shared.hpp"
+#include "__transform_sender.hpp"
+#include "__type_traits.hpp"
+
+#include <utility>
+
+namespace stdexec
+{
+////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.split]
+namespace __split
+{
+using namespace __shared;
+
+struct __split_t
+{};
+
+struct split_t
+{
+ template <sender _Sender, class _Env = empty_env>
+ requires sender_in<_Sender, _Env> && __decay_copyable<env_of_t<_Sender>>
+ auto operator()(_Sender&& __sndr, _Env&& __env = {}) const
+ -> __well_formed_sender auto
+ {
+ auto __domain = __get_late_domain(__sndr, __env);
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<split_t>(static_cast<_Env&&>(__env),
+ static_cast<_Sender&&>(__sndr)));
+ }
+
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto operator()() const noexcept -> __binder_back<split_t>
+ {
+ return {{}, {}, {}};
+ }
+
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(split_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(const _Sender&)),
+ _Sender),
+ tag_invoke_t(split_t, _Sender)>;
+
+ template <class _CvrefSender, class _Env>
+ using __receiver_t =
+ __t<__meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>>>;
+
+ template <class _Sender>
+ static auto transform_sender(_Sender&& __sndr)
+ {
+ using _Receiver =
+ __receiver_t<__child_of<_Sender>, __decay_t<__data_of<_Sender>>>;
+ static_assert(sender_to<__child_of<_Sender>, _Receiver>);
+
+ return __sexpr_apply(static_cast<_Sender&&>(__sndr),
+ [&]<class _Env, class _Child>(
+ __ignore, _Env&& __env, _Child&& __child) {
+ // The shared state starts life with a ref-count of one.
+ auto __sh_state =
+ __make_intrusive<__shared_state<_Child, __decay_t<_Env>>, 2>(
+ static_cast<_Child&&>(__child), static_cast<_Env&&>(__env));
+
+ return __make_sexpr<__split_t>(
+ __box{__split_t(), std::move(__sh_state)});
+ });
+ }
+};
+} // namespace __split
+
+using __split::split_t;
+inline constexpr split_t split{};
+
+template <>
+struct __sexpr_impl<__split::__split_t> :
+ __shared::__shared_impl<__split::__split_t>
+{};
+
+template <>
+struct __sexpr_impl<split_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>> {};
+};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__start_detached.hpp b/include/sdbusplus/async/stdexec/__detail/__start_detached.hpp
new file mode 100644
index 0000000..f22bf39
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__start_detached.hpp
@@ -0,0 +1,111 @@
+/*
+ * 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 "__cpo.hpp"
+#include "__env.hpp"
+#include "__execution_fwd.hpp"
+#include "__meta.hpp"
+#include "__receivers.hpp"
+#include "__senders.hpp"
+#include "__submit.hpp"
+#include "__transform_sender.hpp"
+#include "__type_traits.hpp"
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.consumer.start_detached]
+namespace __start_detached
+{
+template <class _EnvId>
+struct __detached_receiver
+{
+ using _Env = stdexec::__t<_EnvId>;
+
+ struct __t
+ {
+ using receiver_concept = receiver_t;
+ using __id = __detached_receiver;
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _Env __env_;
+
+ template <class... _As>
+ void set_value(_As&&...) noexcept
+ {}
+
+ template <class _Error>
+ [[noreturn]] void set_error(_Error&&) noexcept
+ {
+ std::terminate();
+ }
+
+ void set_stopped() noexcept {}
+
+ auto get_env() const noexcept -> const _Env&
+ {
+ // BUGBUG NOT TO SPEC
+ return __env_;
+ }
+ };
+};
+
+template <class _Env = empty_env>
+using __detached_receiver_t = __t<__detached_receiver<__id<__decay_t<_Env>>>>;
+
+struct start_detached_t
+{
+ template <sender_in<__root_env> _Sender>
+ requires __callable<apply_sender_t, __early_domain_of_t<_Sender>,
+ start_detached_t, _Sender>
+ void operator()(_Sender&& __sndr) const
+ {
+ auto __domain = __get_early_domain(__sndr);
+ stdexec::apply_sender(__domain, *this, static_cast<_Sender&&>(__sndr));
+ }
+
+ template <class _Env, sender_in<__as_root_env_t<_Env>> _Sender>
+ requires __callable<apply_sender_t,
+ __late_domain_of_t<_Sender, __as_root_env_t<_Env>>,
+ start_detached_t, _Sender, __as_root_env_t<_Env>>
+ void operator()(_Sender&& __sndr, _Env&& __env) const
+ {
+ auto __domain = __get_late_domain(__sndr, __env);
+ stdexec::apply_sender(__domain, *this, static_cast<_Sender&&>(__sndr),
+ __as_root_env(static_cast<_Env&&>(__env)));
+ }
+
+ using _Sender = __0;
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(start_detached_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(const _Sender&)),
+ _Sender),
+ tag_invoke_t(start_detached_t, _Sender)>;
+
+ template <class _Sender, class _Env = __root_env>
+ requires sender_to<_Sender, __detached_receiver_t<_Env>>
+ void apply_sender(_Sender&& __sndr, _Env&& __env = {}) const
+ {
+ __submit(static_cast<_Sender&&>(__sndr),
+ __detached_receiver_t<_Env>{static_cast<_Env&&>(__env)});
+ }
+};
+} // namespace __start_detached
+
+using __start_detached::start_detached_t;
+inline constexpr start_detached_t start_detached{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__start_on.hpp b/include/sdbusplus/async/stdexec/__detail/__start_on.hpp
new file mode 100644
index 0000000..7eb8425
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__start_on.hpp
@@ -0,0 +1,120 @@
+/*
+ * 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
+{
+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.start_on]
+namespace __start_on
+{
+struct start_on_t
+{
+ using _Sender = __1;
+ using _Scheduler = __0;
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(start_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<start_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) {
+ return let_value(
+ schedule(__data),
+ __detail::__always{static_cast<_Child&&>(__child)});
+ });
+ }
+};
+} // namespace __start_on
+
+using __start_on::start_on_t;
+inline constexpr start_on_t start_on{};
+
+using on_t = start_on_t;
+inline constexpr on_t on{};
+
+template <>
+struct __sexpr_impl<start_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/__stop_token.hpp b/include/sdbusplus/async/stdexec/__detail/__stop_token.hpp
new file mode 100644
index 0000000..7f5cacc
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__stop_token.hpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2021-2022 Facebook, Inc. and its affiliates
+ * 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 "__concepts.hpp"
+#include "__execution_fwd.hpp"
+
+namespace stdexec
+{
+namespace __stok
+{
+template <template <class> class>
+struct __check_type_alias_exists;
+} // namespace __stok
+
+template <class _Token, class _Callback>
+using stop_callback_for_t = typename _Token::template callback_type<_Callback>;
+
+template <class _Token>
+concept stoppable_token = __nothrow_copy_constructible<_Token> //
+ && __nothrow_move_constructible<_Token> //
+ && equality_comparable<_Token> //
+ && requires(const _Token& __token) {
+ {
+ __token.stop_requested()
+ } noexcept -> __boolean_testable_;
+ {
+ __token.stop_possible()
+ } noexcept -> __boolean_testable_;
+ // workaround ICE in appleclang 13.1
+#if !defined(__clang__)
+ typename __stok::__check_type_alias_exists<
+ _Token::template callback_type>;
+#endif
+ };
+
+template <class _Token, typename _Callback, typename _Initializer = _Callback>
+concept stoppable_token_for =
+ stoppable_token<_Token> //
+ && __callable<_Callback> //
+ && requires { typename stop_callback_for_t<_Token, _Callback>; } &&
+ constructible_from<_Callback, _Initializer> &&
+ constructible_from<stop_callback_for_t<_Token, _Callback>, const _Token&,
+ _Initializer>;
+
+template <class _Token>
+concept unstoppable_token = //
+ stoppable_token<_Token> && //
+ requires {
+ {
+ _Token::stop_possible()
+ } -> __boolean_testable_;
+ } && (!_Token::stop_possible());
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__stopped_as_error.hpp b/include/sdbusplus/async/stdexec/__detail/__stopped_as_error.hpp
new file mode 100644
index 0000000..7476f29
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__stopped_as_error.hpp
@@ -0,0 +1,57 @@
+/*
+ * 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 "__just.hpp"
+#include "__let.hpp"
+#include "__sender_adaptor_closure.hpp"
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.stopped_as_error]
+namespace __sae
+{
+struct stopped_as_error_t
+{
+ template <sender _Sender, __movable_value _Error>
+ auto operator()(_Sender&& __sndr, _Error __err) const
+ {
+ return let_stopped(
+ static_cast<_Sender&&>(__sndr),
+ [__err2 = static_cast<_Error&&>(__err)]() mutable noexcept(
+ __nothrow_move_constructible<_Error>) {
+ return just_error(static_cast<_Error&&>(__err2));
+ });
+ }
+
+ template <__movable_value _Error>
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto operator()(_Error __err) const
+ -> __binder_back<stopped_as_error_t, _Error>
+ {
+ return {{static_cast<_Error&&>(__err)}, {}, {}};
+ }
+};
+} // namespace __sae
+
+using __sae::stopped_as_error_t;
+inline constexpr stopped_as_error_t stopped_as_error{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__stopped_as_optional.hpp b/include/sdbusplus/async/stdexec/__detail/__stopped_as_optional.hpp
new file mode 100644
index 0000000..c84d59e
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__stopped_as_optional.hpp
@@ -0,0 +1,125 @@
+/*
+ * 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 "__completion_signatures.hpp"
+#include "__concepts.hpp"
+#include "__env.hpp"
+#include "__receivers.hpp"
+#include "__sender_adaptor_closure.hpp"
+#include "__senders_core.hpp"
+#include "__transform_completion_signatures.hpp"
+#include "__type_traits.hpp"
+
+#include <exception>
+#include <optional>
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.stopped_as_optional]
+namespace __sao
+{
+struct stopped_as_optional_t
+{
+ template <sender _Sender>
+ auto operator()(_Sender&& __sndr) const
+ {
+ return __make_sexpr<stopped_as_optional_t>(
+ __(), static_cast<_Sender&&>(__sndr));
+ }
+
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto operator()() const noexcept -> __binder_back<stopped_as_optional_t>
+ {
+ return {{}, {}, {}};
+ }
+};
+
+struct __stopped_as_optional_impl : __sexpr_defaults
+{
+ template <class... _Tys>
+ requires(sizeof...(_Tys) == 1)
+ using __set_value_t =
+ completion_signatures<set_value_t(std::optional<__decay_t<_Tys>>...)>;
+
+ template <class _Ty>
+ using __set_error_t = completion_signatures<set_error_t(_Ty)>;
+
+ static constexpr auto get_completion_signatures = //
+ []<class _Self, class... _Env>(_Self&&, _Env&&...) noexcept //
+ -> transform_completion_signatures<
+ __completion_signatures_of_t<__child_of<_Self>, _Env...>,
+ completion_signatures<set_error_t(std::exception_ptr)>,
+ __set_value_t, __set_error_t, completion_signatures<>> {
+ static_assert(sender_expr_for<_Self, stopped_as_optional_t>);
+ return {};
+ };
+
+ static constexpr auto get_state = //
+ []<class _Self, class _Receiver>(_Self&&, _Receiver&) noexcept
+ requires __single_value_sender<__child_of<_Self>, env_of_t<_Receiver>>
+ {
+ static_assert(sender_expr_for<_Self, stopped_as_optional_t>);
+ using _Value = __decay_t<
+ __single_sender_value_t<__child_of<_Self>, env_of_t<_Receiver>>>;
+ return __mtype<_Value>();
+ };
+
+ static constexpr auto complete = //
+ []<class _State, class _Receiver, class _Tag, class... _Args>(
+ __ignore, _State&, _Receiver& __rcvr, _Tag,
+ _Args&&... __args) noexcept -> void {
+ if constexpr (__same_as<_Tag, set_value_t>)
+ {
+ try
+ {
+ static_assert(constructible_from<__t<_State>, _Args...>);
+ stdexec::set_value(static_cast<_Receiver&&>(__rcvr),
+ std::optional<__t<_State>>{
+ static_cast<_Args&&>(__args)...});
+ }
+ catch (...)
+ {
+ stdexec::set_error(static_cast<_Receiver&&>(__rcvr),
+ std::current_exception());
+ }
+ }
+ else if constexpr (__same_as<_Tag, set_error_t>)
+ {
+ stdexec::set_error(static_cast<_Receiver&&>(__rcvr),
+ static_cast<_Args&&>(__args)...);
+ }
+ else
+ {
+ stdexec::set_value(static_cast<_Receiver&&>(__rcvr),
+ std::optional<__t<_State>>{std::nullopt});
+ }
+ };
+};
+} // namespace __sao
+
+using __sao::stopped_as_optional_t;
+inline constexpr stopped_as_optional_t stopped_as_optional{};
+
+template <>
+struct __sexpr_impl<stopped_as_optional_t> : __sao::__stopped_as_optional_impl
+{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__submit.hpp b/include/sdbusplus/async/stdexec/__detail/__submit.hpp
new file mode 100644
index 0000000..8f91ee9
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__submit.hpp
@@ -0,0 +1,167 @@
+/*
+ * 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 "__concepts.hpp"
+#include "__env.hpp"
+#include "__execution_fwd.hpp"
+#include "__meta.hpp"
+#include "__receivers.hpp"
+#include "__senders.hpp"
+#include "__type_traits.hpp"
+
+#include <memory>
+
+namespace stdexec
+{
+namespace
+{
+inline constexpr auto __ref = []<class _Ty>(_Ty& __ty) noexcept {
+ return [__ty = &__ty]() noexcept -> decltype(auto) { return (*__ty); };
+};
+} // namespace
+
+template <class _Ty>
+using __ref_t = decltype(__ref(__declval<_Ty&>()));
+
+/////////////////////////////////////////////////////////////////////////////
+// NOT TO SPEC: __submit
+namespace __submit_
+{
+template <class _OpRef>
+struct __receiver
+{
+ using receiver_concept = receiver_t;
+ using __t = __receiver;
+ using __id = __receiver;
+
+ using _Operation = __decay_t<__call_result_t<_OpRef>>;
+ using _Receiver = stdexec::__t<__mapply<__q<__msecond>, _Operation>>;
+
+ _OpRef __opref_;
+
+ void __delete_op() noexcept
+ {
+ _Operation* __op = &__opref_();
+ if constexpr (__callable<get_allocator_t, env_of_t<_Receiver>>)
+ {
+ auto&& __env = stdexec::get_env(__op->__rcvr_);
+ auto __alloc = stdexec::get_allocator(__env);
+ using _Alloc = decltype(__alloc);
+ using _OpAlloc = typename std::allocator_traits<
+ _Alloc>::template rebind_alloc<_Operation>;
+ _OpAlloc __op_alloc{__alloc};
+ std::allocator_traits<_OpAlloc>::destroy(__op_alloc, __op);
+ std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __op, 1);
+ }
+ else
+ {
+ delete __op;
+ }
+ }
+
+ // Forward all the receiver ops, and delete the operation state.
+ template <class... _As>
+ void set_value(_As&&... __as) noexcept
+ {
+ stdexec::set_value(static_cast<_Receiver&&>(__opref_().__rcvr_),
+ static_cast<_As&&>(__as)...);
+ __delete_op();
+ }
+
+ template <class _Error>
+ void set_error(_Error&& __err) noexcept
+ {
+ stdexec::set_error(static_cast<_Receiver&&>(__opref_().__rcvr_),
+ static_cast<_Error&&>(__err));
+ __delete_op();
+ }
+
+ void set_stopped() noexcept
+ {
+ stdexec::set_stopped(__opref_().__rcvr_);
+ __delete_op();
+ }
+
+ // Forward all receiever queries.
+ auto get_env() const noexcept -> env_of_t<_Receiver&>
+ {
+ return stdexec::get_env(__opref_().__rcvr_);
+ }
+};
+
+template <class _SenderId, class _ReceiverId>
+struct __operation
+{
+ using _Sender = stdexec::__t<_SenderId>;
+ using _Receiver = stdexec::__t<_ReceiverId>;
+ using __receiver_t = __receiver<__ref_t<__operation>>;
+
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _Receiver __rcvr_;
+ connect_result_t<_Sender, __receiver_t> __op_state_;
+
+ __operation(_Sender&& __sndr, _Receiver __rcvr) :
+ __rcvr_(static_cast<_Receiver&&>(__rcvr)),
+ __op_state_(
+ connect(static_cast<_Sender&&>(__sndr), __receiver_t{__ref(*this)}))
+ {}
+};
+
+struct __submit_t
+{
+ template <receiver _Receiver, sender_to<_Receiver> _Sender>
+ void operator()(_Sender&& __sndr, _Receiver __rcvr) const noexcept(false)
+ {
+ if constexpr (__callable<get_allocator_t, env_of_t<_Receiver>>)
+ {
+ auto&& __env = get_env(__rcvr);
+ auto __alloc = get_allocator(__env);
+ using _Alloc = decltype(__alloc);
+ using _Op = __operation<__id<_Sender>, __id<_Receiver>>;
+ using _OpAlloc = typename std::allocator_traits<
+ _Alloc>::template rebind_alloc<_Op>;
+ _OpAlloc __op_alloc{__alloc};
+ auto __op = std::allocator_traits<_OpAlloc>::allocate(__op_alloc,
+ 1);
+ try
+ {
+ std::allocator_traits<_OpAlloc>::construct(
+ __op_alloc, __op, static_cast<_Sender&&>(__sndr),
+ static_cast<_Receiver&&>(__rcvr));
+ stdexec::start(__op->__op_state_);
+ }
+ catch (...)
+ {
+ std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __op,
+ 1);
+ throw;
+ }
+ }
+ else
+ {
+ start((new __operation<__id<_Sender>, __id<_Receiver>>{
+ static_cast<_Sender&&>(__sndr),
+ static_cast<_Receiver&&>(__rcvr)})
+ ->__op_state_);
+ }
+ }
+};
+} // namespace __submit_
+
+using __submit_::__submit_t;
+inline constexpr __submit_t __submit{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__sync_wait.hpp b/include/sdbusplus/async/stdexec/__detail/__sync_wait.hpp
new file mode 100644
index 0000000..427594a
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__sync_wait.hpp
@@ -0,0 +1,392 @@
+/*
+ * 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 "__cpo.hpp"
+#include "__debug.hpp"
+#include "__diagnostics.hpp"
+#include "__domain.hpp"
+#include "__env.hpp"
+#include "__into_variant.hpp"
+#include "__meta.hpp"
+#include "__receivers.hpp"
+#include "__run_loop.hpp"
+#include "__senders.hpp"
+#include "__transform_completion_signatures.hpp"
+#include "__transform_sender.hpp"
+#include "__type_traits.hpp"
+
+#include <exception>
+#include <optional>
+#include <system_error>
+#include <tuple>
+#include <variant>
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.consumers.sync_wait]
+// [execution.senders.consumers.sync_wait_with_variant]
+namespace __sync_wait
+{
+struct __env
+{
+ run_loop* __loop_ = nullptr;
+
+ auto query(get_scheduler_t) const noexcept -> run_loop::__scheduler
+ {
+ return __loop_->get_scheduler();
+ }
+
+ auto query(get_delegatee_scheduler_t) const noexcept
+ -> run_loop::__scheduler
+ {
+ return __loop_->get_scheduler();
+ }
+
+ // static constexpr auto query(__debug::__is_debug_env_t) noexcept -> bool {
+ // return true;
+ // }
+};
+
+// What should sync_wait(just_stopped()) return?
+template <class _Sender, class _Continuation>
+using __sync_wait_result_impl = //
+ __value_types_of_t<_Sender, __env,
+ __mtransform<__q<__decay_t>, _Continuation>,
+ __q<__msingle>>;
+
+template <class _Sender>
+using __sync_wait_result_t =
+ __mtry_eval<__sync_wait_result_impl, _Sender, __qq<std::tuple>>;
+
+template <class _Sender>
+using __sync_wait_with_variant_result_t =
+ __mtry_eval<__sync_wait_result_impl, __result_of<into_variant, _Sender>,
+ __q<__midentity>>;
+
+struct __state
+{
+ std::exception_ptr __eptr_;
+ run_loop __loop_;
+};
+
+template <class... _Values>
+struct __receiver
+{
+ struct __t
+ {
+ using receiver_concept = receiver_t;
+ using __id = __receiver;
+ __state* __state_;
+ std::optional<std::tuple<_Values...>>* __values_;
+
+ template <class... _As>
+ requires constructible_from<std::tuple<_Values...>, _As...>
+ void set_value(_As&&... __as) noexcept
+ {
+ try
+ {
+ __values_->emplace(static_cast<_As&&>(__as)...);
+ }
+ catch (...)
+ {
+ __state_->__eptr_ = std::current_exception();
+ }
+ __state_->__loop_.finish();
+ }
+
+ template <class _Error>
+ void set_error(_Error __err) noexcept
+ {
+ if constexpr (__same_as<_Error, std::exception_ptr>)
+ {
+ STDEXEC_ASSERT(__err !=
+ nullptr); // std::exception_ptr must not be null.
+ __state_->__eptr_ = static_cast<_Error&&>(__err);
+ }
+ else if constexpr (__same_as<_Error, std::error_code>)
+ {
+ __state_->__eptr_ =
+ std::make_exception_ptr(std::system_error(__err));
+ }
+ else
+ {
+ __state_->__eptr_ =
+ std::make_exception_ptr(static_cast<_Error&&>(__err));
+ }
+ __state_->__loop_.finish();
+ }
+
+ void set_stopped() noexcept
+ {
+ __state_->__loop_.finish();
+ }
+
+ auto get_env() const noexcept -> __env
+ {
+ return __env{&__state_->__loop_};
+ }
+ };
+};
+
+template <class _Sender>
+using __receiver_t = __t<__sync_wait_result_impl<_Sender, __q<__receiver>>>;
+
+// These are for hiding the metaprogramming in diagnostics
+template <class _Sender>
+struct __sync_receiver_for
+{
+ using __t = __receiver_t<_Sender>;
+};
+template <class _Sender>
+using __sync_receiver_for_t = __t<__sync_receiver_for<_Sender>>;
+
+template <class _Sender>
+struct __value_tuple_for
+{
+ using __t = __sync_wait_result_t<_Sender>;
+};
+template <class _Sender>
+using __value_tuple_for_t = __t<__value_tuple_for<_Sender>>;
+
+template <class _Sender>
+struct __variant_for
+{
+ using __t = __sync_wait_with_variant_result_t<_Sender>;
+};
+template <class _Sender>
+using __variant_for_t = __t<__variant_for<_Sender>>;
+
+inline constexpr __mstring __sync_wait_context_diag = //
+ "In stdexec::sync_wait()..."_mstr;
+inline constexpr __mstring __too_many_successful_completions_diag =
+ "The argument to stdexec::sync_wait() is a sender that can complete successfully in more "
+ "than one way. Use stdexec::sync_wait_with_variant() instead."_mstr;
+
+template <__mstring _Context, __mstring _Diagnostic>
+struct _INVALID_ARGUMENT_TO_SYNC_WAIT_;
+
+template <__mstring _Diagnostic>
+using __invalid_argument_to_sync_wait =
+ _INVALID_ARGUMENT_TO_SYNC_WAIT_<__sync_wait_context_diag, _Diagnostic>;
+
+template <__mstring _Diagnostic, class _Sender, class _Env = __env>
+using __sync_wait_error =
+ __mexception<__invalid_argument_to_sync_wait<_Diagnostic>,
+ _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
+
+template <class _Sender, class>
+using __too_many_successful_completions_error =
+ __sync_wait_error<__too_many_successful_completions_diag, _Sender>;
+
+template <class _Sender>
+concept __valid_sync_wait_argument =
+ __ok<__minvoke<__mtry_catch_q<__single_value_variant_sender_t,
+ __q<__too_many_successful_completions_error>>,
+ _Sender, __env>>;
+
+#if STDEXEC_NVHPC()
+// It requires some hoop-jumping to get the NVHPC compiler to report a
+// meaningful diagnostic for SFINAE failures.
+template <class _Sender>
+auto __diagnose_error()
+{
+ if constexpr (!sender_in<_Sender, __env>)
+ {
+ using _Completions = __completion_signatures_of_t<_Sender, __env>;
+ if constexpr (__merror<_Completions>)
+ {
+ return _Completions();
+ }
+ else
+ {
+ constexpr __mstring __diag =
+ "The stdexec::sender_in<Sender, Environment> concept check has failed."_mstr;
+ return __sync_wait_error<__diag, _Sender>();
+ }
+ }
+ else if constexpr (!__valid_sync_wait_argument<_Sender>)
+ {
+ return __sync_wait_error<__too_many_successful_completions_diag,
+ _Sender>();
+ }
+ else if constexpr (!sender_to<_Sender, __sync_receiver_for_t<_Sender>>)
+ {
+ constexpr __mstring __diag =
+ "Failed to connect the given sender to sync_wait's internal receiver. "
+ "The stdexec::connect(Sender, Receiver) expression is ill-formed."_mstr;
+ return __sync_wait_error<__diag, _Sender>();
+ }
+ else
+ {
+ constexpr __mstring __diag = "Unknown concept check failure."_mstr;
+ return __sync_wait_error<__diag, _Sender>();
+ }
+}
+
+template <class _Sender>
+using __error_description_t =
+ decltype(__sync_wait::__diagnose_error<_Sender>());
+#endif
+
+////////////////////////////////////////////////////////////////////////////
+// [execution.senders.consumers.sync_wait]
+struct sync_wait_t
+{
+ template <sender_in<__env> _Sender>
+ requires __valid_sync_wait_argument<_Sender> &&
+ __has_implementation_for<sync_wait_t,
+ __early_domain_of_t<_Sender>, _Sender>
+ auto operator()(_Sender&& __sndr) const
+ -> std::optional<__value_tuple_for_t<_Sender>>
+ {
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::apply_sender(__domain, *this,
+ static_cast<_Sender&&>(__sndr));
+ }
+
+#if STDEXEC_NVHPC()
+ // 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
+ -> std::optional<std::tuple<int>> = delete;
+#endif
+
+ using _Sender = __0;
+ using __legacy_customizations_t = __types<
+ // For legacy reasons:
+ tag_invoke_t(
+ sync_wait_t,
+ get_completion_scheduler_t<set_value_t>(get_env_t(const _Sender&)),
+ _Sender),
+ tag_invoke_t(sync_wait_t, _Sender)>;
+
+ // clang-format off
+ /// @brief Synchronously wait for the result of a sender, blocking the
+ /// current thread.
+ ///
+ /// `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
+ /// receiver's environment.
+ ///
+ /// @pre The sender must have a exactly one value completion signature. That
+ /// is, it can only complete successfully in one way, with a single
+ /// set of values.
+ ///
+ /// @retval success Returns an engaged `std::optional` containing the result
+ /// values in a `std::tuple`.
+ /// @retval canceled Returns an empty `std::optional`.
+ /// @retval error Throws the error.
+ ///
+ /// @throws std::rethrow_exception(error) if the error has type
+ /// `std::exception_ptr`.
+ /// @throws std::system_error(error) if the error has type
+ /// `std::error_code`.
+ /// @throws error otherwise
+ // clang-format on
+ template <sender_in<__env> _Sender>
+ auto apply_sender(_Sender&& __sndr) const
+ -> std::optional<__sync_wait_result_t<_Sender>>
+ {
+ __state __local{};
+ 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});
+ stdexec::start(__op_state);
+
+ // Wait for the variant to be filled in.
+ __local.__loop_.run();
+
+ if (__local.__eptr_)
+ {
+ std::rethrow_exception(
+ static_cast<std::exception_ptr&&>(__local.__eptr_));
+ }
+
+ return __result;
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////
+// [execution.senders.consumers.sync_wait_with_variant]
+struct sync_wait_with_variant_t
+{
+ struct __impl;
+
+ template <sender_in<__env> _Sender>
+ requires __callable<apply_sender_t, __early_domain_of_t<_Sender>,
+ sync_wait_with_variant_t, _Sender>
+ auto operator()(_Sender&& __sndr) const -> decltype(auto)
+ {
+ using __result_t =
+ __call_result_t<apply_sender_t, __early_domain_of_t<_Sender>,
+ sync_wait_with_variant_t, _Sender>;
+ static_assert(__is_instance_of<__result_t, std::optional>);
+ using __variant_t = typename __result_t::value_type;
+ static_assert(__is_instance_of<__variant_t, std::variant>);
+
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::apply_sender(__domain, *this,
+ static_cast<_Sender&&>(__sndr));
+ }
+
+#if STDEXEC_NVHPC()
+ template <class _Sender, class _Error = __error_description_t<
+ __result_of<into_variant, _Sender>>>
+ auto operator()(_Sender&&, [[maybe_unused]] _Error __diagnostic = {}) const
+ -> std::optional<std::tuple<std::variant<std::tuple<>>>> = delete;
+#endif
+
+ using _Sender = __0;
+ using __legacy_customizations_t = __types<
+ // For legacy reasons:
+ tag_invoke_t(
+ sync_wait_with_variant_t,
+ get_completion_scheduler_t<set_value_t>(get_env_t(const _Sender&)),
+ _Sender),
+ tag_invoke_t(sync_wait_with_variant_t, _Sender)>;
+
+ template <class _Sender>
+ requires __callable<sync_wait_t, __result_of<into_variant, _Sender>>
+ auto apply_sender(_Sender&& __sndr) const
+ -> std::optional<__variant_for_t<_Sender>>
+ {
+ if (auto __opt_values =
+ sync_wait_t()(into_variant(static_cast<_Sender&&>(__sndr))))
+ {
+ return std::move(std::get<0>(*__opt_values));
+ }
+ return std::nullopt;
+ }
+};
+} // namespace __sync_wait
+
+using __sync_wait::sync_wait_t;
+inline constexpr sync_wait_t sync_wait{};
+
+using __sync_wait::sync_wait_with_variant_t;
+inline constexpr sync_wait_with_variant_t sync_wait_with_variant{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__tag_invoke.hpp b/include/sdbusplus/async/stdexec/__detail/__tag_invoke.hpp
new file mode 100644
index 0000000..0f78273
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__tag_invoke.hpp
@@ -0,0 +1,141 @@
+/*
+ * 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 "__concepts.hpp"
+#include "__meta.hpp"
+
+namespace stdexec::__std_concepts
+{
+#if STDEXEC_HAS_STD_CONCEPTS_HEADER()
+using std::invocable;
+#else
+template <class _Fun, class... _As>
+concept invocable = //
+ requires(_Fun&& __f, _As&&... __as) {
+ std::invoke(static_cast<_Fun&&>(__f), static_cast<_As&&>(__as)...);
+ };
+#endif
+} // namespace stdexec::__std_concepts
+
+namespace std
+{
+using namespace stdexec::__std_concepts;
+} // namespace std
+
+namespace stdexec
+{
+// [func.tag_invoke], tag_invoke
+namespace __tag_invoke
+{
+void tag_invoke();
+
+// For handling queryables with a static constexpr query member function:
+template <class _Tag, class _Env>
+ requires true // so this overload is preferred over the one below
+STDEXEC_ATTRIBUTE((always_inline)) constexpr auto tag_invoke(
+ _Tag, const _Env&) noexcept -> __mconstant<_Env::query(_Tag())>
+{
+ return {};
+}
+
+// For handling queryables with a query member function:
+template <class _Tag, class _Env>
+STDEXEC_ATTRIBUTE((always_inline))
+constexpr auto tag_invoke(_Tag, const _Env& __env) noexcept(
+ noexcept(__env.query(_Tag()))) -> decltype(__env.query(_Tag()))
+{
+ return __env.query(_Tag());
+}
+
+// NOT TO SPEC: Don't require tag_invocable to subsume invocable.
+// std::invoke is more expensive at compile time than necessary,
+// and results in diagnostics that are more verbose than necessary.
+template <class _Tag, class... _Args>
+concept tag_invocable = //
+ requires(_Tag __tag, _Args&&... __args) {
+ tag_invoke(static_cast<_Tag&&>(__tag), static_cast<_Args&&>(__args)...);
+ };
+
+template <class _Ret, class _Tag, class... _Args>
+concept __tag_invocable_r = //
+ requires(_Tag __tag, _Args&&... __args) {
+ {
+ static_cast<_Ret>(tag_invoke(static_cast<_Tag&&>(__tag),
+ static_cast<_Args&&>(__args)...))
+ };
+ };
+
+// NOT TO SPEC: nothrow_tag_invocable subsumes tag_invocable
+template <class _Tag, class... _Args>
+concept nothrow_tag_invocable =
+ tag_invocable<_Tag, _Args...> && //
+ requires(_Tag __tag, _Args&&... __args) {
+ {
+ tag_invoke(static_cast<_Tag&&>(__tag),
+ static_cast<_Args&&>(__args)...)
+ } noexcept;
+ };
+
+template <class _Tag, class... _Args>
+using tag_invoke_result_t =
+ decltype(tag_invoke(__declval<_Tag>(), __declval<_Args>()...));
+
+template <class _Tag, class... _Args>
+struct tag_invoke_result
+{};
+
+template <class _Tag, class... _Args>
+ requires tag_invocable<_Tag, _Args...>
+struct tag_invoke_result<_Tag, _Args...>
+{
+ using type = tag_invoke_result_t<_Tag, _Args...>;
+};
+
+struct tag_invoke_t
+{
+ template <class _Tag, class... _Args>
+ requires tag_invocable<_Tag, _Args...>
+ STDEXEC_ATTRIBUTE((always_inline)) constexpr auto
+ operator()(_Tag __tag, _Args&&... __args) const
+ noexcept(nothrow_tag_invocable<_Tag, _Args...>)
+ -> tag_invoke_result_t<_Tag, _Args...>
+ {
+ return tag_invoke(static_cast<_Tag&&>(__tag),
+ static_cast<_Args&&>(__args)...);
+ }
+};
+
+} // namespace __tag_invoke
+
+using __tag_invoke::tag_invoke_t;
+
+namespace __ti
+{
+inline constexpr tag_invoke_t tag_invoke{};
+} // namespace __ti
+
+using namespace __ti;
+
+template <auto& _Tag>
+using tag_t = __decay_t<decltype(_Tag)>;
+
+using __tag_invoke::__tag_invocable_r;
+using __tag_invoke::nothrow_tag_invocable;
+using __tag_invoke::tag_invocable;
+using __tag_invoke::tag_invoke_result;
+using __tag_invoke::tag_invoke_result_t;
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__then.hpp b/include/sdbusplus/async/stdexec/__detail/__then.hpp
new file mode 100644
index 0000000..946fcd9
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__then.hpp
@@ -0,0 +1,118 @@
+/*
+ * 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 "__basic_sender.hpp"
+#include "__diagnostics.hpp"
+#include "__domain.hpp"
+#include "__execution_fwd.hpp"
+#include "__meta.hpp"
+#include "__sender_adaptor_closure.hpp"
+#include "__senders.hpp"
+#include "__senders_core.hpp"
+#include "__transform_completion_signatures.hpp"
+#include "__transform_sender.hpp"
+
+// include these after __execution_fwd.hpp
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.then]
+namespace __then
+{
+inline constexpr __mstring __then_context =
+ "In stdexec::then(Sender, Function)..."_mstr;
+using __on_not_callable = __callable_error<__then_context>;
+
+template <class _Fun, class _CvrefSender, class... _Env>
+using __completions_t = //
+ transform_completion_signatures<
+ __completion_signatures_of_t<_CvrefSender, _Env...>,
+ __with_error_invoke_t<__on_not_callable, set_value_t, _Fun,
+ _CvrefSender, _Env...>,
+ __mbind_front<__mtry_catch_q<__set_value_invoke_t, __on_not_callable>,
+ _Fun>::template __f>;
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+struct then_t
+{
+ template <sender _Sender, __movable_value _Fun>
+ auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender
+ auto
+ {
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<then_t>(static_cast<_Fun&&>(__fun),
+ static_cast<_Sender&&>(__sndr)));
+ }
+
+ template <__movable_value _Fun>
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto operator()(_Fun __fun) const -> __binder_back<then_t, _Fun>
+ {
+ return {{static_cast<_Fun&&>(__fun)}, {}, {}};
+ }
+
+ using _Sender = __1;
+ using _Fun = __0;
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(then_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(_Sender&)),
+ _Sender, _Fun),
+ tag_invoke_t(then_t, _Sender, _Fun)>;
+};
+
+struct __then_impl : __sexpr_defaults
+{
+ static constexpr auto get_completion_signatures = //
+ []<class _Sender, class... _Env>(_Sender&&, _Env&&...) noexcept
+ -> __completions_t<__decay_t<__data_of<_Sender>>, __child_of<_Sender>,
+ _Env...> {
+ static_assert(sender_expr_for<_Sender, then_t>);
+ return {};
+ };
+
+ static constexpr auto complete = //
+ []<class _Tag, class _State, class _Receiver, class... _Args>(
+ __ignore, _State& __state, _Receiver& __rcvr, _Tag,
+ _Args&&... __args) noexcept -> void {
+ if constexpr (__same_as<_Tag, set_value_t>)
+ {
+ stdexec::__set_value_invoke(static_cast<_Receiver&&>(__rcvr),
+ static_cast<_State&&>(__state),
+ static_cast<_Args&&>(__args)...);
+ }
+ else
+ {
+ _Tag()(static_cast<_Receiver&&>(__rcvr),
+ static_cast<_Args&&>(__args)...);
+ }
+ };
+};
+} // namespace __then
+
+using __then::then_t;
+
+/// @brief The then sender adaptor, which invokes a function with the result of
+/// a sender, making the result available to the next receiver.
+/// @hideinitializer
+inline constexpr then_t then{};
+
+template <>
+struct __sexpr_impl<then_t> : __then::__then_impl
+{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__transfer_just.hpp b/include/sdbusplus/async/stdexec/__detail/__transfer_just.hpp
new file mode 100644
index 0000000..3566bc6
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__transfer_just.hpp
@@ -0,0 +1,140 @@
+/*
+ * 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 "__continue_on.hpp"
+#include "__domain.hpp"
+#include "__env.hpp"
+#include "__just.hpp"
+#include "__meta.hpp"
+#include "__schedule_from.hpp"
+#include "__schedulers.hpp"
+#include "__sender_introspection.hpp"
+#include "__tag_invoke.hpp"
+#include "__transform_sender.hpp"
+#include "__tuple.hpp"
+
+STDEXEC_PRAGMA_PUSH()
+STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.transfer_just]
+namespace __transfer_just
+{
+// This is a helper for finding legacy cusutomizations of transfer_just.
+inline auto __transfer_just_tag_invoke()
+{
+ return []<class... _Ts>(
+ _Ts&&... __ts) -> tag_invoke_result_t<transfer_just_t, _Ts...> {
+ return tag_invoke(transfer_just, static_cast<_Ts&&>(__ts)...);
+ };
+}
+
+template <class _Env>
+auto __make_transform_fn(const _Env&)
+{
+ return [&]<class _Scheduler, class... _Values>(_Scheduler&& __sched,
+ _Values&&... __vals) {
+ return continue_on(just(static_cast<_Values&&>(__vals)...),
+ static_cast<_Scheduler&&>(__sched));
+ };
+}
+
+template <class _Env>
+auto __transform_sender_fn(const _Env& __env)
+{
+ return [&]<class _Data>(__ignore, _Data&& __data) {
+ return __data.apply(__make_transform_fn(__env),
+ static_cast<_Data&&>(__data));
+ };
+}
+
+struct __legacy_customization_fn
+{
+ template <class _Data>
+ auto operator()(_Data&& __data) const
+ -> decltype(__data.apply(__transfer_just_tag_invoke(),
+ static_cast<_Data&&>(__data)))
+ {
+ return __data.apply(__transfer_just_tag_invoke(),
+ static_cast<_Data&&>(__data));
+ }
+};
+
+struct transfer_just_t
+{
+ using _Data = __0;
+ using __legacy_customizations_t = //
+ __types<__legacy_customization_fn(_Data)>;
+
+ template <scheduler _Scheduler, __movable_value... _Values>
+ auto operator()(_Scheduler&& __sched, _Values&&... __vals) const
+ -> __well_formed_sender auto
+ {
+ auto __domain = query_or(get_domain, __sched, default_domain());
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<transfer_just_t>(
+ __tuple{static_cast<_Scheduler&&>(__sched),
+ static_cast<_Values&&>(__vals)...}));
+ }
+
+ 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));
+ }
+};
+
+inline auto __make_env_fn() noexcept
+{
+ return []<class _Scheduler>(const _Scheduler& __sched,
+ const auto&...) noexcept {
+ using _Env = __t<__schfr::__environ<__id<_Scheduler>>>;
+ return _Env{__sched};
+ };
+}
+
+struct __transfer_just_impl : __sexpr_defaults
+{
+ static constexpr auto get_attrs = //
+ []<class _Data>(const _Data& __data) noexcept {
+ return __data.apply(__make_env_fn(), __data);
+ };
+
+ static constexpr auto get_completion_signatures = //
+ []<class _Sender>(_Sender&&) noexcept //
+ -> __completion_signatures_of_t< //
+ transform_sender_result_t<default_domain, _Sender, empty_env>> {};
+};
+} // namespace __transfer_just
+
+using __transfer_just::transfer_just_t;
+inline constexpr transfer_just_t transfer_just{};
+
+template <>
+struct __sexpr_impl<transfer_just_t> : __transfer_just::__transfer_just_impl
+{};
+} // namespace stdexec
+
+STDEXEC_PRAGMA_POP()
diff --git a/include/sdbusplus/async/stdexec/__detail/__transform_completion_signatures.hpp b/include/sdbusplus/async/stdexec/__detail/__transform_completion_signatures.hpp
new file mode 100644
index 0000000..631a9c9
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__transform_completion_signatures.hpp
@@ -0,0 +1,478 @@
+/*
+ * 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 "__completion_signatures.hpp"
+#include "__concepts.hpp"
+#include "__debug.hpp"
+#include "__diagnostics.hpp"
+#include "__meta.hpp"
+#include "__senders_core.hpp"
+
+#include <tuple>
+#include <variant>
+
+namespace stdexec
+{
+#if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING()
+// __checked_completion_signatures is for catching logic bugs in a sender's
+// metadata. If sender<S> and sender_in<S, Ctx> are both true, then they had
+// better report the same metadata. This completion signatures wrapper enforces
+// that at compile time.
+template <class _Sender, class... _Env>
+auto __checked_completion_signatures(_Sender&& __sndr, _Env&&... __env) noexcept
+{
+ using __completions_t = __completion_signatures_of_t<_Sender, _Env...>;
+ stdexec::__debug_sender(static_cast<_Sender&&>(__sndr), __env...);
+ return __completions_t{};
+}
+
+template <class _Sender, class... _Env>
+ requires sender_in<_Sender, _Env...>
+using completion_signatures_of_t =
+ decltype(stdexec::__checked_completion_signatures(__declval<_Sender>(),
+ __declval<_Env>()...));
+#else
+template <class _Sender, class... _Env>
+ requires sender_in<_Sender, _Env...>
+using completion_signatures_of_t =
+ __completion_signatures_of_t<_Sender, _Env...>;
+#endif
+
+struct __not_a_variant
+{
+ __not_a_variant() = delete;
+};
+
+template <class... _Ts>
+using __std_variant = //
+ __minvoke_if_c<sizeof...(_Ts) != 0,
+ __mtransform<__q1<__decay_t>, __munique<__qq<std::variant>>>,
+ __mconst<__not_a_variant>, _Ts...>;
+
+template <class... _Ts>
+using __nullable_std_variant =
+ __mcall<__munique<__mbind_front<__qq<std::variant>, std::monostate>>,
+ __decay_t<_Ts>...>;
+
+template <class... _Ts>
+using __decayed_std_tuple = __meval<std::tuple, __decay_t<_Ts>...>;
+
+namespace __sigs
+{
+// The following code is used to normalize completion signatures.
+// "Normalization" means that that rvalue-references are stripped from the types
+// in the completion signatures. For example, the completion signature
+// `set_value_t(int &&)` would be normalized to `set_value_t(int)`, but
+// `set_value_t(int)` and `set_value_t(int &)` would remain unchanged.
+template <class _Tag, class... _Args>
+auto __normalize_sig_impl(_Args&&...) -> _Tag (*)(_Args...);
+
+template <class _Tag, class... _Args>
+auto __normalize_sig(_Tag (*)(_Args...))
+ -> decltype(__sigs::__normalize_sig_impl<_Tag>(__declval<_Args>()...));
+
+template <class... _Sigs>
+auto __repack_completions(_Sigs*...) -> completion_signatures<_Sigs...>;
+
+template <class... _Sigs>
+auto __normalize_completions(completion_signatures<_Sigs...>*)
+ -> decltype(__sigs::__repack_completions(
+ __sigs::__normalize_sig(static_cast<_Sigs*>(nullptr))...));
+
+template <class _Completions>
+using __normalize_completions_t = decltype(__sigs::__normalize_completions(
+ static_cast<_Completions*>(nullptr)));
+} // namespace __sigs
+
+template <class... _SigPtrs>
+using __completion_signature_ptrs = //
+ decltype(__sigs::__repack_completions(static_cast<_SigPtrs>(nullptr)...));
+
+template <class... _Sigs>
+using __concat_completion_signatures = //
+ __mconcat<__qq<completion_signatures>>::__f<
+ __mconcat<__qq<__mmake_set>>::__f<_Sigs...>>;
+
+namespace __sigs
+{
+//////////////////////////////////////////////////////////////////////////////////////////////////
+template <template <class...> class _Tuple, class _Tag, class... _Args>
+auto __for_each_sig(_Tag (*)(_Args...)) -> _Tuple<_Tag, _Args...>;
+
+template <class _Sig, template <class...> class _Tuple>
+using __for_each_sig_t =
+ decltype(__sigs::__for_each_sig<_Tuple>(static_cast<_Sig*>(nullptr)));
+
+template <template <class...> class _Tuple, template <class...> class _Variant,
+ class... _More, class _What, class... _With>
+auto __for_each_completion_signature_fn(_ERROR_<_What, _With...>**)
+ -> _ERROR_<_What, _With...>;
+
+template <template <class...> class _Tuple, template <class...> class _Variant,
+ class... _More, class... _Sigs>
+auto __for_each_completion_signature_fn(completion_signatures<_Sigs...>**)
+ -> _Variant<__for_each_sig_t<_Sigs, _Tuple>..., _More...>;
+} // namespace __sigs
+
+template <class _Sigs, template <class...> class _Tuple,
+ template <class...> class _Variant, class... _More>
+using __for_each_completion_signature =
+ decltype(__sigs::__for_each_completion_signature_fn<_Tuple, _Variant,
+ _More...>(
+ static_cast<_Sigs**>(nullptr)));
+
+namespace __sigs
+{
+////////////////////////////////////////////////////////////////////////////////////////////////
+template <template <class...> class _SetVal, template <class...> class _SetErr,
+ class _SetStp, class... _Values>
+auto __transform_sig(set_value_t (*)(_Values...)) -> _SetVal<_Values...>;
+
+template <template <class...> class _SetVal, template <class...> class _SetErr,
+ class _SetStp, class _Error>
+auto __transform_sig(set_error_t (*)(_Error)) -> _SetErr<_Error>;
+
+template <template <class...> class _SetVal, template <class...> class _SetErr,
+ class _SetStp>
+auto __transform_sig(set_stopped_t (*)()) -> _SetStp;
+
+template <class _Sig, template <class...> class _SetVal,
+ template <class...> class _SetErr, class _SetStp>
+using __transform_sig_t =
+ decltype(__sigs::__transform_sig<_SetVal, _SetErr, _SetStp>(
+ static_cast<_Sig*>(nullptr)));
+
+template <template <class...> class _SetVal, template <class...> class _SetErr,
+ class _SetStp, template <class...> class _Variant, class... _More,
+ class _What, class... _With>
+auto __transform_sigs_fn(_ERROR_<_What, _With...>**)
+ -> _ERROR_<_What, _With...>;
+
+template <template <class...> class _SetVal, template <class...> class _SetErr,
+ class _SetStp, template <class...> class _Variant, class... _More,
+ class... _Sigs>
+auto __transform_sigs_fn(completion_signatures<_Sigs...>**) //
+ -> _Variant<__transform_sig_t<_Sigs, _SetVal, _SetErr, _SetStp>...,
+ _More...>;
+} // namespace __sigs
+
+template <class _Sigs, template <class...> class _SetVal,
+ template <class...> class _SetErr, class _SetStp,
+ template <class...> class _Variant,
+ class... _More>
+using __transform_completion_signatures = //
+ decltype(__sigs::__transform_sigs_fn<_SetVal, _SetErr, _SetStp, _Variant,
+ _More...>(
+ static_cast<_Sigs**>(nullptr)));
+
+namespace __sigs
+{
+////////////////////////////////////////////////////////////////////////////////////////////////
+template <class _WantedTag>
+struct __gather_sigs_fn;
+
+template <>
+struct __gather_sigs_fn<set_value_t>
+{
+ template <class _Sigs, template <class...> class _Then,
+ template <class...> class _Else,
+ template <class...> class _Variant, class... _More>
+ using __f = __transform_completion_signatures<
+ _Sigs, _Then, __mbind_front_q<_Else, set_error_t>::template __f,
+ _Else<set_stopped_t>, _Variant, _More...>;
+};
+
+template <>
+struct __gather_sigs_fn<set_error_t>
+{
+ template <class _Sigs, template <class...> class _Then,
+ template <class...> class _Else,
+ template <class...> class _Variant, class... _More>
+ using __f = __transform_completion_signatures<
+ _Sigs, __mbind_front_q<_Else, set_value_t>::template __f, _Then,
+ _Else<set_stopped_t>, _Variant, _More...>;
+};
+
+template <>
+struct __gather_sigs_fn<set_stopped_t>
+{
+ template <class _Sigs, template <class...> class _Then,
+ template <class...> class _Else,
+ template <class...> class _Variant, class... _More>
+ using __f = __transform_completion_signatures<
+ _Sigs, __mbind_front_q<_Else, set_value_t>::template __f,
+ __mbind_front_q<_Else, set_error_t>::template __f, _Then<>, _Variant,
+ _More...>;
+};
+
+template <class... _Values>
+using __default_set_value = completion_signatures<set_value_t(_Values...)>;
+
+template <class... _Error>
+using __default_set_error = completion_signatures<set_error_t(_Error...)>;
+
+template <class _Tag, class... _Args>
+using __default_completion = completion_signatures<_Tag(_Args...)>;
+} // namespace __sigs
+
+template <class _Sigs, class _WantedTag, template <class...> class _Then,
+ template <class...> class _Else, template <class...> class _Variant,
+ class... _More>
+using __gather_completion_signatures = typename __sigs::__gather_sigs_fn<
+ _WantedTag>::template __f<_Sigs, _Then, _Else, _Variant, _More...>;
+
+/////////////////////////////////////////////////////////////////////////////
+// transform_completion_signatures
+// ==========================
+//
+// `transform_completion_signatures` takes a sender, and environment, and a
+// bunch of other template arguments for munging the completion signatures of a
+// sender in interesting ways.
+//
+// ```c++
+// template <class... Args>
+// using __default_set_value = completion_signatures<set_value_t(Args...)>;
+//
+// template <class Err>
+// using __default_set_error = completion_signatures<set_error_t(Err)>;
+//
+// template <
+// class Completions,
+// class AdditionalSigs = completion_signatures<>,
+// template <class...> class SetValue = __default_set_value,
+// template <class> class SetError = __default_set_error,
+// class SetStopped = completion_signatures<set_stopped_t()>>
+// using transform_completion_signatures =
+// completion_signatures< ... >;
+// ```
+//
+// * `SetValue` : an alias template that accepts a set of value types and
+// returns an instance of
+// `completion_signatures`.
+//
+// * `SetError` : an alias template that accepts an error types and returns a
+// an instance of
+// `completion_signatures`.
+//
+// * `SetStopped` : an instantiation of `completion_signatures` with a list of
+// completion
+// signatures `Sigs...` to the added to the list if the sender can complete
+// with a stopped signal.
+//
+// * `AdditionalSigs` : an instantiation of `completion_signatures` with a list
+// of completion
+// signatures `Sigs...` to the added to the list unconditionally.
+//
+// `transform_completion_signatures` does the following:
+//
+// * Let `VCs...` be a pack of the `completion_signatures` types in the
+// `__typelist` named by
+// `value_types_of_t<Sndr, Env, SetValue, __typelist>`, and let `Vs...` be
+// the concatenation of the packs that are template arguments to each
+// `completion_signature` in `VCs...`.
+//
+// * Let `ECs...` be a pack of the `completion_signatures` types in the
+// `__typelist` named by
+// `error_types_of_t<Sndr, Env, __errorlist>`, where `__errorlist` is an
+// alias template such that `__errorlist<Ts...>` names
+// `__typelist<SetError<Ts>...>`, and let `Es...` be the concatenation of the
+// packs that are the template arguments to each `completion_signature` in
+// `ECs...`.
+//
+// * Let `Ss...` be an empty pack if `sends_stopped<Sndr, Env>` is `false`;
+// otherwise, a pack
+// containing the template arguments of the `completion_signatures`
+// instantiation named by `SetStopped`.
+//
+// * Let `MoreSigs...` be a pack of the template arguments of the
+// `completion_signatures`
+// instantiation named by `AdditionalSigs`.
+//
+// Then `transform_completion_signatures<Completions, AdditionalSigs, SetValue,
+// SetError, SendsStopped>` names the type `completion_signatures< Sigs... >`
+// where `Sigs...` is the unique set of types in `[Vs..., Es..., Ss...,
+// MoreSigs...]`.
+//
+// If any of the above type computations are ill-formed,
+// `transform_completion_signatures<Sndr, Env, AdditionalSigs, SetValue,
+// SetError, SendsStopped>` is ill-formed.
+template <
+ class _Sigs, class _MoreSigs = completion_signatures<>,
+ template <class...> class _ValueTransform = __sigs::__default_set_value,
+ template <class...> class _ErrorTransform = __sigs::__default_set_error,
+ class _StoppedSigs = completion_signatures<set_stopped_t()>>
+using transform_completion_signatures = //
+ __transform_completion_signatures<
+ _Sigs, _ValueTransform, _ErrorTransform, _StoppedSigs,
+ __mtry_q<__concat_completion_signatures>::__f, _MoreSigs>;
+
+template <
+ class _Sndr, class _Env = empty_env,
+ class _MoreSigs = completion_signatures<>,
+ template <class...> class _ValueTransform = __sigs::__default_set_value,
+ template <class...> class _ErrorTransform = __sigs::__default_set_error,
+ class _StoppedSigs = completion_signatures<set_stopped_t()>>
+using transform_completion_signatures_of = //
+ transform_completion_signatures<completion_signatures_of_t<_Sndr, _Env>,
+ _MoreSigs, _ValueTransform, _ErrorTransform,
+ _StoppedSigs>;
+
+using __eptr_completion =
+ completion_signatures<set_error_t(std::exception_ptr)>;
+
+template <class _NoExcept>
+using __eptr_completion_if_t =
+ __if<_NoExcept, completion_signatures<>, __eptr_completion>;
+
+template <bool _NoExcept>
+using __eptr_completion_if = __eptr_completion_if_t<__mbool<_NoExcept>>;
+
+template < //
+ class _Sender, //
+ class _Env = empty_env, //
+ class _More = completion_signatures<>, //
+ class _SetValue = __qq<__sigs::__default_set_value>, //
+ class _SetError = __qq<__sigs::__default_set_error>, //
+ class _SetStopped = completion_signatures<set_stopped_t()>> //
+using __try_make_completion_signatures = //
+ __transform_completion_signatures<
+ __completion_signatures_of_t<_Sender, _Env>, _SetValue::template __f,
+ _SetError::template __f, _SetStopped,
+ __mtry_q<__concat_completion_signatures>::__f, _More>;
+
+template <class _SetTag, class _Completions, class _Tuple,
+ class _Variant>
+using __gather_completions = //
+ __gather_completion_signatures<
+ _Completions, _SetTag,
+ __mcompose_q<__types, _Tuple::template __f>::template __f,
+ __mconst<__types<>>::__f, __mconcat<_Variant>::template __f>;
+
+template <class _SetTag, class _Sender, class _Env, class _Tuple,
+ class _Variant>
+using __gather_completions_of = //
+ __gather_completions<_SetTag, __completion_signatures_of_t<_Sender, _Env>,
+ _Tuple, _Variant>;
+
+template < //
+ class _Sender, //
+ class _Env = empty_env, //
+ class _Sigs = completion_signatures<>, //
+ template <class...> class _SetValue = __sigs::__default_set_value, //
+ template <class...> class _SetError = __sigs::__default_set_error, //
+ class _SetStopped = completion_signatures<set_stopped_t()>>
+using make_completion_signatures =
+ transform_completion_signatures_of<_Sender, _Env, _Sigs, _SetValue,
+ _SetError, _SetStopped>;
+
+template < //
+ class _Sigs, //
+ class _Tuple = __q<__decayed_std_tuple>, //
+ class _Variant = __q<__std_variant>>
+using __value_types_t = //
+ __gather_completions<set_value_t, _Sigs, _Tuple, _Variant>;
+
+template < //
+ class _Sender, //
+ class _Env = empty_env, //
+ class _Tuple = __q<__decayed_std_tuple>, //
+ class _Variant = __q<__std_variant>>
+using __value_types_of_t = //
+ __value_types_t<__completion_signatures_of_t<_Sender, _Env>, _Tuple,
+ _Variant>;
+
+template <class _Sigs, class _Variant = __q<__std_variant>>
+using __error_types_t =
+ __gather_completions<set_error_t, _Sigs, __q<__midentity>, _Variant>;
+
+template <class _Sender, class _Env = empty_env,
+ class _Variant = __q<__std_variant>>
+using __error_types_of_t =
+ __error_types_t<__completion_signatures_of_t<_Sender, _Env>, _Variant>;
+
+template < //
+ class _Sender, //
+ class _Env = empty_env, //
+ template <class...> class _Tuple = __decayed_std_tuple, //
+ template <class...> class _Variant = __std_variant>
+using value_types_of_t =
+ __value_types_of_t<_Sender, _Env, __q<_Tuple>, __q<_Variant>>;
+
+template <class _Sender, class _Env = empty_env,
+ template <class...> class _Variant = __std_variant>
+using error_types_of_t = __error_types_of_t<_Sender, _Env, __q<_Variant>>;
+
+template <class _Tag, class _Sender, class... _Env>
+using __count_of = //
+ __gather_completion_signatures<
+ __completion_signatures_of_t<_Sender, _Env...>, _Tag,
+ __mconst<__msize_t<1>>::__f, __mconst<__msize_t<0>>::__f, __mplus_t>;
+
+template <class _Tag, class _Sender, class... _Env>
+ requires sender_in<_Sender, _Env...>
+inline constexpr bool __sends = //
+ __v<__gather_completion_signatures<
+ __completion_signatures_of_t<_Sender, _Env...>, _Tag,
+ __mconst<__mtrue>::__f, __mconst<__mfalse>::__f, __mor_t>>;
+
+template <class _Sender, class... _Env>
+concept sends_stopped = //
+ sender_in<_Sender, _Env...> && __sends<set_stopped_t, _Sender, _Env...>;
+
+template <class _Sender, class... _Env>
+using __single_sender_value_t =
+ __value_types_t<__completion_signatures_of_t<_Sender, _Env...>,
+ __msingle_or<void>, __q<__msingle>>;
+
+template <class _Sender, class... _Env>
+concept __single_value_sender = //
+ sender_in<_Sender, _Env...> && //
+ requires { typename __single_sender_value_t<_Sender, _Env...>; };
+
+template <class _Sender, class... _Env>
+using __single_value_variant_sender_t =
+ __value_types_t<__completion_signatures_of_t<_Sender, _Env...>,
+ __qq<__types>, __q<__msingle>>;
+
+template <class _Sender, class... _Env>
+concept __single_value_variant_sender = //
+ sender_in<_Sender, _Env...> && //
+ requires { typename __single_value_variant_sender_t<_Sender, _Env...>; };
+
+// The following utilities are needed fairly often:
+template <class _Fun, class... _Args>
+ requires __invocable<_Fun, _Args...>
+using __nothrow_invocable_t = __mbool<__nothrow_invocable<_Fun, _Args...>>;
+
+template <class _Catch, class _Tag, class _Fun, class _Sender, class... _Env>
+using __with_error_invoke_t = //
+ __if<__gather_completion_signatures<
+ __completion_signatures_of_t<_Sender, _Env...>, _Tag,
+ __mbind_front<__mtry_catch_q<__nothrow_invocable_t, _Catch>,
+ _Fun>::template __f,
+ __mconst<__mbool<true>>::__f, __mand>,
+ completion_signatures<>, __eptr_completion>;
+
+template <class _Fun, class... _Args>
+ requires __invocable<_Fun, _Args...>
+using __set_value_invoke_t = //
+ completion_signatures<__minvoke<__mremove<void, __qf<set_value_t>>,
+ __invoke_result_t<_Fun, _Args...>>>;
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__transform_sender.hpp b/include/sdbusplus/async/stdexec/__detail/__transform_sender.hpp
new file mode 100644
index 0000000..9de95ba
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__transform_sender.hpp
@@ -0,0 +1,311 @@
+/*
+ * 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 "__diagnostics.hpp"
+#include "__domain.hpp"
+#include "__env.hpp"
+#include "__meta.hpp"
+#include "__sender_introspection.hpp"
+#include "__type_traits.hpp"
+
+STDEXEC_PRAGMA_PUSH()
+STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference)
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// dependent_domain
+struct dependent_domain
+{
+ template <class _Sender, class _Env>
+ static constexpr auto __is_nothrow_transform_sender() noexcept -> bool;
+
+ template <sender_expr _Sender, class _Env>
+ requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
+ STDEXEC_ATTRIBUTE((always_inline)) decltype(auto)
+ transform_sender(_Sender&& __sndr, const _Env& __env) const
+ noexcept(__is_nothrow_transform_sender<_Sender, _Env>());
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// [execution.transform_sender]
+namespace __domain
+{
+struct __transform_env
+{
+ template <class _Domain, class _Sender, class _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ /*constexpr*/
+ decltype(auto) operator()(_Domain __dom, _Sender&& __sndr,
+ _Env&& __env) const noexcept
+ {
+ if constexpr (__domain::__has_transform_env<_Domain, _Sender, _Env>)
+ {
+ return __dom.transform_env(static_cast<_Sender&&>(__sndr),
+ static_cast<_Env&&>(__env));
+ }
+ else
+ {
+ return default_domain().transform_env(
+ static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env));
+ }
+ }
+};
+
+struct __transform_sender_1
+{
+ template <class _Domain, class _Sender, class... _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ static constexpr bool __is_nothrow() noexcept
+ {
+ if constexpr (__domain::__has_transform_sender<_Domain, _Sender,
+ _Env...>)
+ {
+ return noexcept(__declval<_Domain&>().transform_sender(
+ __declval<_Sender>(), __declval<const _Env&>()...));
+ }
+ else
+ {
+ return //
+ noexcept(default_domain().transform_sender(
+ __declval<_Sender>(), __declval<const _Env&>()...));
+ }
+ }
+
+ template <class _Domain, class _Sender, class... _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ /*constexpr*/
+ decltype(auto) operator()(_Domain __dom, _Sender&& __sndr,
+ const _Env&... __env) const
+ noexcept(__is_nothrow<_Domain, _Sender, const _Env&...>())
+ {
+ if constexpr (__domain::__has_transform_sender<_Domain, _Sender,
+ _Env...>)
+ {
+ return __dom.transform_sender(static_cast<_Sender&&>(__sndr),
+ __env...);
+ }
+ else
+ {
+ return default_domain().transform_sender(
+ static_cast<_Sender&&>(__sndr), __env...);
+ }
+ }
+};
+
+template <class _Ty, class _Uy>
+concept __decay_same_as = same_as<__decay_t<_Ty>, __decay_t<_Uy>>;
+
+struct __transform_sender
+{
+ template <class _Self = __transform_sender, class _Domain, class _Sender,
+ class... _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ /*constexpr*/
+ decltype(auto) operator()(_Domain __dom, _Sender&& __sndr,
+ const _Env&... __env) const
+ noexcept(__nothrow_callable<__transform_sender_1, _Domain, _Sender,
+ const _Env&...>)
+ {
+ using _Sender2 = __call_result_t<__transform_sender_1, _Domain, _Sender,
+ const _Env&...>;
+ // If the transformation doesn't change the sender's type, then do not
+ // apply the transform recursively.
+ if constexpr (__decay_same_as<_Sender, _Sender2>)
+ {
+ return __transform_sender_1()(__dom, static_cast<_Sender&&>(__sndr),
+ __env...);
+ }
+ else
+ {
+ // We transformed the sender and got back a different sender.
+ // Transform that one too.
+ return _Self()(__dom,
+ __transform_sender_1()(
+ __dom, static_cast<_Sender&&>(__sndr), __env...),
+ __env...);
+ }
+ }
+};
+
+struct __transform_dependent_sender
+{
+ // If we are doing a lazy customization of a type whose domain is
+ // value-dependent (e.g., let_value), first transform the sender to
+ // determine the domain. Then continue transforming the sender with the
+ // requested domain.
+ template <class _Domain, sender_expr _Sender, class _Env>
+ requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
+ /*constexpr*/ auto operator()(_Domain __dom, _Sender&& __sndr,
+ const _Env& __env) const
+ noexcept(noexcept(__transform_sender()(
+ __dom,
+ dependent_domain().transform_sender(static_cast<_Sender&&>(__sndr),
+ __env),
+ __env))) -> decltype(auto)
+ {
+ static_assert(__none_of<_Domain, dependent_domain>);
+ return __transform_sender()(__dom,
+ dependent_domain().transform_sender(
+ static_cast<_Sender&&>(__sndr), __env),
+ __env);
+ }
+};
+} // namespace __domain
+
+/////////////////////////////////////////////////////////////////////////////
+// [execution.transform_sender]
+inline constexpr struct transform_sender_t :
+ __domain::__transform_sender,
+ __domain::__transform_dependent_sender
+{
+ using __domain::__transform_sender::operator();
+ using __domain::__transform_dependent_sender::operator();
+} transform_sender{};
+
+template <class _Domain, class _Sender, class... _Env>
+using transform_sender_result_t =
+ __call_result_t<transform_sender_t, _Domain, _Sender, _Env...>;
+
+inline constexpr __domain::__transform_env transform_env{};
+
+struct _CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_
+{};
+
+template <class _Sender, class _Env>
+constexpr auto dependent_domain::__is_nothrow_transform_sender() noexcept
+ -> bool
+{
+ using _Env2 = __call_result_t<__domain::__transform_env, dependent_domain&,
+ _Sender, _Env>;
+ return __v<decltype(__sexpr_apply(
+ __declval<_Sender>(), []<class _Tag, class _Data, class... _Childs>(
+ _Tag, _Data&&, _Childs&&...) {
+ constexpr bool __first_transform_is_nothrow =
+ noexcept(__make_sexpr<_Tag>(
+ __declval<_Data>(),
+ __domain::__transform_sender()(__declval<dependent_domain&>(),
+ __declval<_Childs>(),
+ __declval<const _Env2&>())...));
+ using _Sender2 = decltype(__make_sexpr<_Tag>(
+ __declval<_Data>(),
+ __domain::__transform_sender()(__declval<dependent_domain&>(),
+ __declval<_Childs>(),
+ __declval<const _Env2&>())...));
+ using _Domain2 = decltype(__sexpr_apply(
+ __declval<_Sender2&>(), __domain::__common_domain_fn()));
+ constexpr bool __second_transform_is_nothrow =
+ noexcept(__domain::__transform_sender()(__declval<_Domain2&>(),
+ __declval<_Sender2>(),
+ __declval<const _Env&>()));
+ return __mbool < __first_transform_is_nothrow &&
+ __second_transform_is_nothrow > ();
+ }))>;
+}
+
+template <sender_expr _Sender, class _Env>
+ requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
+auto dependent_domain::transform_sender(_Sender&& __sndr,
+ const _Env& __env) const
+ noexcept(__is_nothrow_transform_sender<_Sender, _Env>()) -> decltype(auto)
+{
+ // apply any algorithm-specific transformation to the environment
+ const auto& __env2 = transform_env(*this, static_cast<_Sender&&>(__sndr),
+ __env);
+
+ // recursively transform the sender to determine the domain
+ return __sexpr_apply(static_cast<_Sender&&>(__sndr),
+ [&]<class _Tag, class _Data, class... _Childs>(
+ _Tag, _Data&& __data, _Childs&&... __childs) {
+ // TODO: propagate meta-exceptions here:
+ auto __sndr2 = __make_sexpr<_Tag>(
+ static_cast<_Data&&>(__data),
+ __domain::__transform_sender()(
+ *this, static_cast<_Childs&&>(__childs), __env2)...);
+ using _Sender2 = decltype(__sndr2);
+
+ auto __domain2 = __sexpr_apply(__sndr2, __domain::__common_domain_fn());
+ using _Domain2 = decltype(__domain2);
+
+ if constexpr (same_as<_Domain2, __none_such>)
+ {
+ return __mexception<_CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_,
+ _WITH_SENDER_<_Sender2>>();
+ }
+ else
+ {
+ return __domain::__transform_sender()(__domain2, std::move(__sndr2),
+ __env);
+ }
+ });
+}
+
+/////////////////////////////////////////////////////////////////////////////
+template <class _Tag, class _Domain, class _Sender, class... _Args>
+concept __has_implementation_for =
+ __domain::__has_apply_sender<_Domain, _Tag, _Sender, _Args...> ||
+ __domain::__has_apply_sender<default_domain, _Tag, _Sender, _Args...>;
+
+/////////////////////////////////////////////////////////////////////////////
+// [execution.apply_sender]
+inline constexpr struct apply_sender_t
+{
+ template <class _Domain, class _Tag, class _Sender, class... _Args>
+ requires __has_implementation_for<_Tag, _Domain, _Sender, _Args...>
+ STDEXEC_ATTRIBUTE((always_inline))
+ /*constexpr*/
+ decltype(auto)
+ operator()(_Domain __dom, _Tag, _Sender&& __sndr,
+ _Args&&... __args) const
+ {
+ if constexpr (__domain::__has_apply_sender<_Domain, _Tag, _Sender,
+ _Args...>)
+ {
+ return __dom.apply_sender(_Tag(), static_cast<_Sender&&>(__sndr),
+ static_cast<_Args&&>(__args)...);
+ }
+ else
+ {
+ return default_domain().apply_sender(
+ _Tag(), static_cast<_Sender&&>(__sndr),
+ static_cast<_Args&&>(__args)...);
+ }
+ }
+} apply_sender{};
+
+template <class _Domain, class _Tag, class _Sender, class... _Args>
+using apply_sender_result_t =
+ __call_result_t<apply_sender_t, _Domain, _Tag, _Sender, _Args...>;
+
+/////////////////////////////////////////////////////////////////////////////
+template <class _Sender, class _Scheduler, class _Tag = set_value_t>
+concept __completes_on = __decays_to<
+ __call_result_t<get_completion_scheduler_t<_Tag>, env_of_t<_Sender>>,
+ _Scheduler>;
+
+/////////////////////////////////////////////////////////////////////////////
+template <class _Sender, class _Scheduler, class _Env>
+concept __starts_on =
+ __decays_to<__call_result_t<get_scheduler_t, _Env>, _Scheduler>;
+} // namespace stdexec
+
+STDEXEC_PRAGMA_POP()
diff --git a/include/sdbusplus/async/stdexec/__detail/__tuple.hpp b/include/sdbusplus/async/stdexec/__detail/__tuple.hpp
index b1239a7..902a6e5 100644
--- a/include/sdbusplus/async/stdexec/__detail/__tuple.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__tuple.hpp
@@ -29,98 +29,169 @@
template <class _Ty, std::size_t _Idx>
struct __box
{
- STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Ty __value;
+ // See https://github.com/llvm/llvm-project/issues/93563
+ // STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
+ _Ty __value;
};
-template <class _Idx, class... _Ts>
+template <class _Ty>
+concept __empty = //
+ STDEXEC_IS_EMPTY(_Ty) && STDEXEC_IS_TRIVIALLY_CONSTRUCTIBLE(_Ty);
+
+template <__empty _Ty>
+inline _Ty __value{};
+
+// A specialization for empty types so that they don't take up space.
+template <__empty _Ty, std::size_t _Idx>
+struct __box<_Ty, _Idx>
+{
+ __box() = default;
+
+ constexpr __box(__not_decays_to<__box> auto&&) noexcept {}
+
+ static constexpr _Ty& __value = __tup::__value<_Ty>;
+};
+
+template <auto _Idx, class... _Ts>
struct __tuple;
-template <std::size_t... _Idx, class... _Ts>
-struct __tuple<__indices<_Idx...>, _Ts...> : __box<_Ts, _Idx>...
-{};
+template <std::size_t... _Is, __indices<_Is...> _Idx, class... _Ts>
+struct __tuple<_Idx, _Ts...> : __box<_Ts, _Is>...
+{
+ template <class _Fn, class _Self, class... _Us>
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ static auto apply(_Fn&& __fn, _Self&& __self, _Us&&... __us) //
+ noexcept(noexcept(static_cast<_Fn&&>(__fn)(
+ static_cast<_Us&&>(__us)...,
+ static_cast<_Self&&>(__self).__box<_Ts, _Is>::__value...)))
+ -> decltype(static_cast<_Fn&&>(__fn)(
+ static_cast<_Us&&>(__us)...,
+ static_cast<_Self&&>(__self).__box<_Ts, _Is>::__value...))
+ {
+ return static_cast<_Fn&&>(__fn)(
+ static_cast<_Us&&>(__us)...,
+ static_cast<_Self&&>(__self).__box<_Ts, _Is>::__value...);
+ }
+
+ template <class _Fn, class _Self, class... _Us>
+ requires(__callable<_Fn, _Us..., __copy_cvref_t<_Self, _Ts>> && ...)
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) static auto for_each(
+ _Fn&& __fn, _Self&& __self, _Us&&... __us) //
+ noexcept((__nothrow_callable<_Fn, _Us..., __copy_cvref_t<_Self, _Ts>> &&
+ ...)) -> void
+ {
+ return (static_cast<_Fn&&>(__fn)(
+ static_cast<_Us&&>(__us)...,
+ static_cast<_Self&&>(__self).__box<_Ts, _Is>::__value),
+ ...);
+ }
+};
template <class... _Ts>
STDEXEC_ATTRIBUTE((host, device))
-__tuple(_Ts...) -> __tuple<__indices_for<_Ts...>, _Ts...>;
+__tuple(_Ts...) -> __tuple<__indices_for<_Ts...>{}, _Ts...>;
+
+template <class _Fn, class _Tuple, class... _Us>
+using __apply_result_t = //
+ decltype(__declval<_Tuple>().apply(__declval<_Fn>(), __declval<_Tuple>(),
+ __declval<_Us>()...));
+
+template <class _Fn, class _Tuple, class... _Us>
+concept __applicable =
+ requires { typename __apply_result_t<_Fn, _Tuple, _Us...>; };
+
+template <class _Fn, class _Tuple, class... _Us>
+concept __nothrow_applicable =
+ __applicable<_Fn, _Tuple, _Us...>&& noexcept(__declval<_Tuple>().apply(
+ __declval<_Fn>(), __declval<_Tuple>(), __declval<_Us>()...));
#if STDEXEC_GCC()
template <class... _Ts>
struct __mk_tuple
{
- using __t = __tuple<__indices_for<_Ts...>, _Ts...>;
+ using __t = __tuple<__indices_for<_Ts...>{}, _Ts...>;
};
-template <class... _Ts>
-using __tuple_for = __t<__mk_tuple<_Ts...>>;
-#else
-template <class... _Ts>
-using __tuple_for = __tuple<__indices_for<_Ts...>, _Ts...>;
#endif
template <std::size_t _Idx, class _Ty>
-STDEXEC_ATTRIBUTE((always_inline))
-constexpr _Ty&& __get(__box<_Ty, _Idx>&& __self) noexcept
+STDEXEC_ATTRIBUTE((host, device, always_inline))
+constexpr _Ty&& get(__box<_Ty, _Idx>&& __self) noexcept
{
return static_cast<_Ty&&>(__self.__value);
}
template <std::size_t _Idx, class _Ty>
-STDEXEC_ATTRIBUTE((always_inline))
-constexpr _Ty& __get(__box<_Ty, _Idx>& __self) noexcept
+STDEXEC_ATTRIBUTE((host, device, always_inline))
+constexpr _Ty& get(__box<_Ty, _Idx>& __self) noexcept
{
return __self.__value;
}
template <std::size_t _Idx, class _Ty>
-STDEXEC_ATTRIBUTE((always_inline))
-constexpr const _Ty& __get(const __box<_Ty, _Idx>& __self) noexcept
+STDEXEC_ATTRIBUTE((host, device, always_inline))
+constexpr const _Ty& get(const __box<_Ty, _Idx>& __self) noexcept
{
return __self.__value;
}
-template <std::size_t... _Idx, class... _Ts>
-void __tuple_like_(const __tuple<__indices<_Idx...>, _Ts...>&);
-
-template <class _Tup>
-concept __tuple_like = requires(_Tup& __tup) { __tup::__tuple_like_(__tup); };
-
-struct __apply_
+template <class _Fn, class _Tuple>
+STDEXEC_ATTRIBUTE((host, device, always_inline))
+auto operator<<(_Tuple&& __tup,
+ _Fn __fn) noexcept(__nothrow_move_constructible<_Fn>)
{
- template <class _Fun, class _Tuple, std::size_t... _Idx, class... _Ts>
- requires __callable<_Fun, __copy_cvref_t<_Tuple, _Ts>...>
- constexpr auto operator()(
- _Fun&& __fun, _Tuple&& __tup,
- const __tuple<
- __indices<_Idx...>,
- _Ts...>*) noexcept(__nothrow_callable<_Fun, __copy_cvref_t<_Tuple,
- _Ts>...>)
- -> __call_result_t<_Fun, __copy_cvref_t<_Tuple, _Ts>...>
- {
- return static_cast<_Fun&&>(__fun)(
- static_cast<__copy_cvref_t<_Tuple, __box<_Ts, _Idx>>&&>(__tup)
- .__value...);
- }
-};
-
-template <class _Fun, __tuple_like _Tuple>
-STDEXEC_ATTRIBUTE((always_inline))
-constexpr auto __apply(_Fun&& __fun, _Tuple&& __tup) noexcept(
- noexcept(__apply_()(static_cast<_Fun&&>(__fun),
- static_cast<_Tuple&&>(__tup), &__tup)))
- -> decltype(__apply_()(static_cast<_Fun&&>(__fun),
- static_cast<_Tuple&&>(__tup), &__tup))
-{
- return __apply_()(static_cast<_Fun&&>(__fun), static_cast<_Tuple&&>(__tup),
- &__tup);
+ return [&__tup, __fn]<class... _Us>(_Us&&... __us) //
+ noexcept(__nothrow_applicable<_Fn, _Tuple, _Us...>)
+ -> __apply_result_t<_Fn, _Tuple, _Us...> {
+ return __tup.apply(__fn, static_cast<_Tuple&&>(__tup),
+ static_cast<_Us&&>(__us)...);
+ };
}
+
+template <class _Fn, class... _Tuples>
+auto __cat_apply(_Fn __fn, _Tuples&&... __tups) //
+ noexcept(noexcept((static_cast<_Tuples&&>(__tups) << ... << __fn)())) //
+ -> decltype((static_cast<_Tuples&&>(__tups) << ... << __fn)())
+{
+ return (static_cast<_Tuples&&>(__tups) << ... << __fn)();
+}
+
+STDEXEC_PRAGMA_PUSH()
+STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
+
+inline constexpr struct __mktuple_t
+{
+ template <class... _Ts>
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ auto operator()(_Ts&&... __ts) const
+ noexcept(noexcept(__tuple{static_cast<_Ts&&>(__ts)...}))
+ -> decltype(__tuple{static_cast<_Ts&&>(__ts)...})
+ {
+ return __tuple{static_cast<_Ts&&>(__ts)...};
+ }
+} __mktuple{};
+
+STDEXEC_PRAGMA_POP()
+
} // namespace __tup
using __tup::__tuple;
-// So we can use __tuple as a typelist and ignore the first template parameter
-template <class _Fn, class _Idx, class... _Ts>
- requires __minvocable<_Fn, _Ts...>
-struct __uncurry_<_Fn, __tuple<_Idx, _Ts...>>
+#if STDEXEC_GCC()
+template <class... _Ts>
+using __tuple_for = __t<__tup::__mk_tuple<_Ts...>>;
+#else
+template <class... _Ts>
+using __tuple_for = __tuple<__indices_for<_Ts...>{}, _Ts...>;
+#endif
+
+template <class... _Ts>
+using __decayed_tuple = __tuple_for<__decay_t<_Ts>...>;
+
+// So we can use __tuple as a typelist
+template <auto _Idx, class... _Ts>
+struct __muncurry_<__tuple<_Idx, _Ts...>>
{
- using __t = __minvoke<_Fn, _Ts...>;
+ template <class _Fn>
+ using __f = __minvoke<_Fn, _Ts...>;
};
} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__type_traits.hpp b/include/sdbusplus/async/stdexec/__detail/__type_traits.hpp
index 8b134ed..41a17c7 100644
--- a/include/sdbusplus/async/stdexec/__detail/__type_traits.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__type_traits.hpp
@@ -17,13 +17,15 @@
#include "__config.hpp"
+#include <type_traits>
+
namespace stdexec
{
//////////////////////////////////////////////////////////////////////////////////////////////////
// A very simple std::declval replacement that doesn't handle void
template <class _Tp>
-auto __declval() noexcept -> _Tp&&;
+extern auto (*__declval)() noexcept -> _Tp&&;
//////////////////////////////////////////////////////////////////////////////////////////////////
// __decay_t: An efficient implementation for std::decay
@@ -179,12 +181,10 @@
template <class _From, class _To>
using __copy_cvref_t = typename __copy_cvref_fn<_From>::template __f<_To>;
-#if !STDEXEC_HAS_BUILTIN(__is_const)
template <class>
-inline constexpr bool __is_const = false;
+inline constexpr bool __is_const_ = false;
template <class _Up>
-inline constexpr bool __is_const<const _Up> = true;
-#endif
+inline constexpr bool __is_const_<const _Up> = true;
namespace __tt
{
@@ -196,4 +196,13 @@
using __remove_rvalue_reference_t =
decltype(__tt::__remove_rvalue_reference_fn(__declval<_Ty>()));
+// Implemented as a class instead of a free function
+// because of a bizarre nvc++ compiler bug:
+struct __cref_fn
+{
+ template <class _Ty>
+ auto operator()(const _Ty&) -> const _Ty&;
+};
+template <class _Ty>
+using __cref_t = decltype(__cref_fn{}(__declval<_Ty>()));
} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__upon_error.hpp b/include/sdbusplus/async/stdexec/__detail/__upon_error.hpp
new file mode 100644
index 0000000..eb71faa
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__upon_error.hpp
@@ -0,0 +1,116 @@
+/*
+ * 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 "__basic_sender.hpp"
+#include "__diagnostics.hpp"
+#include "__domain.hpp"
+#include "__execution_fwd.hpp"
+#include "__meta.hpp"
+#include "__sender_adaptor_closure.hpp"
+#include "__senders.hpp"
+#include "__senders_core.hpp"
+#include "__transform_completion_signatures.hpp"
+#include "__transform_sender.hpp"
+
+// include these after __execution_fwd.hpp
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.upon_error]
+namespace __upon_error
+{
+inline constexpr __mstring __upon_error_context =
+ "In stdexec::upon_error(Sender, Function)..."_mstr;
+using __on_not_callable = __callable_error<__upon_error_context>;
+
+template <class _Fun, class _CvrefSender, class... _Env>
+using __completion_signatures_t = //
+ transform_completion_signatures<
+ __completion_signatures_of_t<_CvrefSender, _Env...>,
+ __with_error_invoke_t<__on_not_callable, set_error_t, _Fun,
+ _CvrefSender, _Env...>,
+ __sigs::__default_set_value,
+ __mbind_front<__mtry_catch_q<__set_value_invoke_t, __on_not_callable>,
+ _Fun>::template __f>;
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+struct upon_error_t
+{
+ template <sender _Sender, __movable_value _Fun>
+ auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender
+ auto
+ {
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<upon_error_t>(static_cast<_Fun&&>(__fun),
+ static_cast<_Sender&&>(__sndr)));
+ }
+
+ template <__movable_value _Fun>
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto operator()(_Fun __fun) const -> __binder_back<upon_error_t, _Fun>
+ {
+ return {{static_cast<_Fun&&>(__fun)}, {}, {}};
+ }
+
+ using _Sender = __1;
+ using _Fun = __0;
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(upon_error_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(_Sender&)),
+ _Sender, _Fun),
+ tag_invoke_t(upon_error_t, _Sender, _Fun)>;
+};
+
+struct __upon_error_impl : __sexpr_defaults
+{
+ static constexpr auto get_completion_signatures = //
+ []<class _Sender, class... _Env>(_Sender&&, _Env&&...) noexcept
+ -> __completion_signatures_t<__decay_t<__data_of<_Sender>>,
+ __child_of<_Sender>, _Env...> {
+ static_assert(sender_expr_for<_Sender, upon_error_t>);
+ return {};
+ };
+
+ static constexpr auto complete = //
+ []<class _Tag, class _State, class _Receiver, class... _Args>(
+ __ignore, _State& __state, _Receiver& __rcvr, _Tag,
+ _Args&&... __args) noexcept -> void {
+ if constexpr (__same_as<_Tag, set_error_t>)
+ {
+ stdexec::__set_value_invoke(static_cast<_Receiver&&>(__rcvr),
+ static_cast<_State&&>(__state),
+ static_cast<_Args&&>(__args)...);
+ }
+ else
+ {
+ _Tag()(static_cast<_Receiver&&>(__rcvr),
+ static_cast<_Args&&>(__args)...);
+ }
+ };
+};
+} // namespace __upon_error
+
+using __upon_error::upon_error_t;
+inline constexpr upon_error_t upon_error{};
+
+template <>
+struct __sexpr_impl<upon_error_t> : __upon_error::__upon_error_impl
+{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__upon_stopped.hpp b/include/sdbusplus/async/stdexec/__detail/__upon_stopped.hpp
new file mode 100644
index 0000000..bb588da
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__upon_stopped.hpp
@@ -0,0 +1,118 @@
+/*
+ * 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 "__basic_sender.hpp"
+#include "__diagnostics.hpp"
+#include "__domain.hpp"
+#include "__execution_fwd.hpp"
+#include "__meta.hpp"
+#include "__sender_adaptor_closure.hpp"
+#include "__senders.hpp"
+#include "__senders_core.hpp"
+#include "__transform_completion_signatures.hpp"
+#include "__transform_sender.hpp"
+
+// include these after __execution_fwd.hpp
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.upon_stopped]
+namespace __upon_stopped
+{
+inline constexpr __mstring __upon_stopped_context =
+ "In stdexec::upon_stopped(Sender, Function)..."_mstr;
+using __on_not_callable = __callable_error<__upon_stopped_context>;
+
+template <class _Fun, class _CvrefSender, class... _Env>
+using __completion_signatures_t = //
+ transform_completion_signatures<
+ __completion_signatures_of_t<_CvrefSender, _Env...>,
+ __with_error_invoke_t<__on_not_callable, set_stopped_t, _Fun,
+ _CvrefSender, _Env...>,
+ __sigs::__default_set_value, __sigs::__default_set_error,
+ __set_value_invoke_t<_Fun>>;
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+struct upon_stopped_t
+{
+ template <sender _Sender, __movable_value _Fun>
+ requires __callable<_Fun>
+ auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender
+ auto
+ {
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<upon_stopped_t>(static_cast<_Fun&&>(__fun),
+ static_cast<_Sender&&>(__sndr)));
+ }
+
+ template <__movable_value _Fun>
+ requires __callable<_Fun>
+ STDEXEC_ATTRIBUTE((always_inline)) auto operator()(_Fun __fun) const
+ -> __binder_back<upon_stopped_t, _Fun>
+ {
+ return {{static_cast<_Fun&&>(__fun)}, {}, {}};
+ }
+
+ using _Sender = __1;
+ using _Fun = __0;
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(upon_stopped_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(_Sender&)),
+ _Sender, _Fun),
+ tag_invoke_t(upon_stopped_t, _Sender, _Fun)>;
+};
+
+struct __upon_stopped_impl : __sexpr_defaults
+{
+ static constexpr auto get_completion_signatures = //
+ []<class _Sender, class... _Env>(_Sender&&, _Env&&...) noexcept
+ -> __completion_signatures_t<__decay_t<__data_of<_Sender>>,
+ __child_of<_Sender>, _Env...> {
+ static_assert(sender_expr_for<_Sender, upon_stopped_t>);
+ return {};
+ };
+
+ static constexpr auto complete = //
+ []<class _Tag, class _State, class _Receiver, class... _Args>(
+ __ignore, _State& __state, _Receiver& __rcvr, _Tag,
+ _Args&&... __args) noexcept -> void {
+ if constexpr (__same_as<_Tag, set_stopped_t>)
+ {
+ stdexec::__set_value_invoke(static_cast<_Receiver&&>(__rcvr),
+ static_cast<_State&&>(__state),
+ static_cast<_Args&&>(__args)...);
+ }
+ else
+ {
+ _Tag()(static_cast<_Receiver&&>(__rcvr),
+ static_cast<_Args&&>(__args)...);
+ }
+ };
+};
+} // namespace __upon_stopped
+
+using __upon_stopped::upon_stopped_t;
+inline constexpr upon_stopped_t upon_stopped{};
+
+template <>
+struct __sexpr_impl<upon_stopped_t> : __upon_stopped::__upon_stopped_impl
+{};
+
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__utility.hpp b/include/sdbusplus/async/stdexec/__detail/__utility.hpp
index 5f3f68a..3af21b7 100644
--- a/include/sdbusplus/async/stdexec/__detail/__utility.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__utility.hpp
@@ -15,13 +15,110 @@
*/
#pragma once
+#include "__concepts.hpp"
#include "__config.hpp"
#include "__type_traits.hpp"
+#include <initializer_list>
+#include <memory> // for addressof
#include <type_traits>
namespace stdexec
{
+constexpr std::size_t __npos = ~0UL;
+
+template <class...>
+struct __undefined;
+
+struct __
+{};
+
+struct __ignore
+{
+ __ignore() = default;
+
+ STDEXEC_ATTRIBUTE((always_inline))
+ constexpr __ignore(auto&&...) noexcept {}
+};
+
+#if STDEXEC_MSVC()
+// MSVCBUG
+// https://developercommunity.visualstudio.com/t/Incorrect-function-template-argument-sub/10437827
+
+template <std::size_t>
+struct __ignore_t
+{
+ __ignore_t() = default;
+
+ constexpr __ignore_t(auto&&...) noexcept {}
+};
+#else
+template <std::size_t>
+using __ignore_t = __ignore;
+#endif
+
+struct __none_such
+{};
+
+namespace
+{
+struct __anon
+{};
+} // namespace
+
+struct __immovable
+{
+ __immovable() = default;
+
+ private:
+ STDEXEC_IMMOVABLE(__immovable);
+};
+
+struct __move_only
+{
+ __move_only() = default;
+
+ __move_only(__move_only&&) noexcept = default;
+ auto operator=(__move_only&&) noexcept -> __move_only& = default;
+
+ __move_only(const __move_only&) = delete;
+ auto operator=(const __move_only&) -> __move_only& = delete;
+};
+
+inline constexpr std::size_t
+ __umax(std::initializer_list<std::size_t> __il) noexcept
+{
+ std::size_t __m = 0;
+ for (std::size_t __i : __il)
+ {
+ if (__i > __m)
+ {
+ __m = __i;
+ }
+ }
+ return __m;
+}
+
+inline constexpr std::size_t __pos_of(const bool* const __first,
+ const bool* const __last) noexcept
+{
+ for (const bool* __where = __first; __where != __last; ++__where)
+ {
+ if (*__where)
+ {
+ return static_cast<std::size_t>(__where - __first);
+ }
+ }
+ return __npos;
+}
+
+template <class _Ty, class... _Ts>
+inline constexpr std::size_t __index_of() noexcept
+{
+ constexpr bool __same[] = {STDEXEC_IS_SAME(_Ty, _Ts)..., false};
+ return __pos_of(__same, __same + sizeof...(_Ts));
+}
+
namespace __detail
{
template <class _Cpcvref>
@@ -35,4 +132,68 @@
template <class _Ty>
inline constexpr const auto& __forward_like =
__detail::__forward_like<__copy_cvref_fn<_Ty&&>>;
+
+STDEXEC_PRAGMA_PUSH()
+STDEXEC_PRAGMA_IGNORE_GNU("-Wold-style-cast")
+
+// A derived-to-base cast that works even when the base is not accessible from
+// derived.
+template <class _Tp, class _Up>
+STDEXEC_ATTRIBUTE((host, device))
+auto __c_upcast(_Up&& u) noexcept -> __copy_cvref_t<_Up&&, _Tp>
+ requires __decays_to<_Tp, _Tp>
+{
+ static_assert(STDEXEC_IS_BASE_OF(_Tp, __decay_t<_Up>));
+ return (__copy_cvref_t<_Up&&, _Tp>)static_cast<_Up&&>(u);
+}
+
+// A base-to-derived cast that works even when the base is not accessible from
+// derived.
+template <class _Tp, class _Up>
+STDEXEC_ATTRIBUTE((host, device))
+auto __c_downcast(_Up&& u) noexcept -> __copy_cvref_t<_Up&&, _Tp>
+ requires __decays_to<_Tp, _Tp>
+{
+ static_assert(STDEXEC_IS_BASE_OF(__decay_t<_Up>, _Tp));
+ return (__copy_cvref_t<_Up&&, _Tp>)static_cast<_Up&&>(u);
+}
+
+STDEXEC_PRAGMA_POP()
+
+template <class _Ty>
+_Ty __decay_copy(_Ty) noexcept;
+
+template <class _Ty>
+struct __indestructible
+{
+ template <class... _Us>
+ constexpr __indestructible(_Us&&... __us) noexcept(
+ __nothrow_constructible_from<_Ty, _Us...>) :
+ __value(static_cast<_Us&&>(__us)...)
+ {}
+
+ constexpr ~__indestructible() {}
+
+ _Ty& get() noexcept
+ {
+ return __value;
+ }
+
+ const _Ty& get() const noexcept
+ {
+ return __value;
+ }
+
+ union
+ {
+ _Ty __value;
+ };
+};
} // namespace stdexec
+
+#if defined(__cpp_auto_cast) && (__cpp_auto_cast >= 202110UL)
+#define STDEXEC_DECAY_COPY(...) auto(__VA_ARGS__)
+#else
+#define STDEXEC_DECAY_COPY(...) \
+ (true ? (__VA_ARGS__) : stdexec::__decay_copy(__VA_ARGS__))
+#endif
diff --git a/include/sdbusplus/async/stdexec/__detail/__variant.hpp b/include/sdbusplus/async/stdexec/__detail/__variant.hpp
new file mode 100644
index 0000000..a3ea4bd
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__variant.hpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 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 "__meta.hpp"
+#include "__type_traits.hpp"
+#include "__utility.hpp"
+
+#include <cstddef>
+#include <memory>
+#include <new>
+#include <type_traits>
+
+/********************************************************************************/
+/* NB: The variant type implemented here default-constructs into the valueless
+ */
+/* state. This is different from std::variant which default-constructs into the
+ */
+/* first alternative. This is done to simplify the implementation and to avoid
+ */
+/* the need for a default constructor for each alternative type. */
+/********************************************************************************/
+
+STDEXEC_PRAGMA_PUSH()
+STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
+
+namespace stdexec
+{
+inline constexpr std::size_t __variant_npos = ~0UL;
+
+struct __monostate
+{};
+
+namespace __var
+{
+template <auto _Idx, class... _Ts>
+class __variant;
+
+template <>
+class __variant<__indices<>{}>
+{
+ public:
+ template <class _Fn, class... _Us>
+ STDEXEC_ATTRIBUTE((host, device))
+ void visit(_Fn&&, _Us&&...) const noexcept
+ {
+ STDEXEC_ASSERT(false);
+ }
+
+ STDEXEC_ATTRIBUTE((host, device))
+ static constexpr std::size_t index() noexcept
+ {
+ return __variant_npos;
+ }
+
+ STDEXEC_ATTRIBUTE((host, device))
+ static constexpr bool is_valueless() noexcept
+ {
+ return true;
+ }
+};
+
+template <std::size_t... _Is, __indices<_Is...> _Idx, class... _Ts>
+class __variant<_Idx, _Ts...>
+{
+ static constexpr std::size_t __max_size = stdexec::__umax({sizeof(_Ts)...});
+ static_assert(__max_size != 0);
+ std::size_t __index_{__variant_npos};
+ alignas(_Ts...) unsigned char __storage_[__max_size];
+
+ STDEXEC_ATTRIBUTE((host, device))
+ void __destroy() noexcept
+ {
+ auto __index = std::exchange(__index_, __variant_npos);
+ if (__variant_npos != __index)
+ {
+ ((_Is == __index ? std::destroy_at(static_cast<_Ts*>(__get_ptr()))
+ : void(0)),
+ ...);
+ }
+ }
+
+ template <std::size_t _Ny>
+ using __at = __m_at_c<_Ny, _Ts...>;
+
+ public:
+ // immovable:
+ __variant(__variant&&) = delete;
+
+ STDEXEC_ATTRIBUTE((host, device))
+ __variant() noexcept {}
+
+ STDEXEC_ATTRIBUTE((host, device))
+ ~__variant()
+ {
+ __destroy();
+ }
+
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ void* __get_ptr() noexcept
+ {
+ return __storage_;
+ }
+
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ std::size_t index() const noexcept
+ {
+ return __index_;
+ }
+
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ bool is_valueless() const noexcept
+ {
+ return __index_ == __variant_npos;
+ }
+
+ template <class _Ty, class... _As>
+ STDEXEC_ATTRIBUTE((host, device))
+ _Ty& emplace(_As&&... __as) //
+ noexcept(__nothrow_constructible_from<_Ty, _As...>)
+ {
+ constexpr std::size_t __new_index = stdexec::__index_of<_Ty, _Ts...>();
+ static_assert(__new_index != __variant_npos, "Type not in variant");
+
+ __destroy();
+ ::new (__storage_) _Ty{static_cast<_As&&>(__as)...};
+ __index_ = __new_index;
+ return *reinterpret_cast<_Ty*>(__storage_);
+ }
+
+ template <std::size_t _Ny, class... _As>
+ STDEXEC_ATTRIBUTE((host, device))
+ __at<_Ny>& emplace(_As&&... __as) //
+ noexcept(__nothrow_constructible_from<__at<_Ny>, _As...>)
+ {
+ static_assert(_Ny < sizeof...(_Ts), "variant index is too large");
+
+ __destroy();
+ ::new (__storage_) __at<_Ny>{static_cast<_As&&>(__as)...};
+ __index_ = _Ny;
+ return *reinterpret_cast<__at<_Ny>*>(__storage_);
+ }
+
+ template <class _Fn, class... _As>
+ STDEXEC_ATTRIBUTE((host, device))
+ auto emplace_from(_Fn&& __fn, _As&&... __as) //
+ noexcept(__nothrow_callable<_Fn, _As...>)
+ -> __call_result_t<_Fn, _As...>&
+ {
+ using __result_t = __call_result_t<_Fn, _As...>;
+ 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_);
+ }
+
+ template <class _Fn, class _Self, class... _As>
+ STDEXEC_ATTRIBUTE((host, device))
+ static void visit(_Fn&& __fn, _Self&& __self, _As&&... __as) //
+ noexcept((__nothrow_callable<_Fn, _As..., __copy_cvref_t<_Self, _Ts>> &&
+ ...))
+ {
+ STDEXEC_ASSERT(__self.__index_ != __variant_npos);
+ auto __index = __self.__index_; // make it local so we don't access it
+ // after it's deleted.
+ ((_Is == __index ? static_cast<_Fn&&>(__fn)(
+ static_cast<_As&&>(__as)...,
+ static_cast<_Self&&>(__self).template get<_Is>())
+ : void()),
+ ...);
+ }
+
+ template <std::size_t _Ny>
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ decltype(auto) get() && noexcept
+ {
+ STDEXEC_ASSERT(_Ny == __index_);
+ return static_cast<__at<_Ny>&&>(
+ *reinterpret_cast<__at<_Ny>*>(__storage_));
+ }
+
+ template <std::size_t _Ny>
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ decltype(auto) get() & noexcept
+ {
+ STDEXEC_ASSERT(_Ny == __index_);
+ return *reinterpret_cast<__at<_Ny>*>(__storage_);
+ }
+
+ template <std::size_t _Ny>
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ decltype(auto) get() const& noexcept
+ {
+ STDEXEC_ASSERT(_Ny == __index_);
+ return *reinterpret_cast<const __at<_Ny>*>(__storage_);
+ }
+};
+} // namespace __var
+
+using __var::__variant;
+
+template <class... _Ts>
+using __variant_for = __variant<__indices_for<_Ts...>{}, _Ts...>;
+
+template <class... Ts>
+using __uniqued_variant_for =
+ __mcall<__munique<__qq<__variant_for>>, __decay_t<Ts>...>;
+
+// So we can use __variant as a typelist
+template <auto _Idx, class... _Ts>
+struct __muncurry_<__variant<_Idx, _Ts...>>
+{
+ template <class _Fn>
+ using __f = __minvoke<_Fn, _Ts...>;
+};
+} // namespace stdexec
+
+STDEXEC_PRAGMA_POP()
diff --git a/include/sdbusplus/async/stdexec/__detail/__when_all.hpp b/include/sdbusplus/async/stdexec/__detail/__when_all.hpp
new file mode 100644
index 0000000..d3a778b
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__when_all.hpp
@@ -0,0 +1,640 @@
+/*
+ * 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 "../stop_token.hpp"
+#include "__basic_sender.hpp"
+#include "__concepts.hpp"
+#include "__continue_on.hpp"
+#include "__diagnostics.hpp"
+#include "__domain.hpp"
+#include "__env.hpp"
+#include "__into_variant.hpp"
+#include "__meta.hpp"
+#include "__optional.hpp"
+#include "__schedulers.hpp"
+#include "__senders.hpp"
+#include "__transform_completion_signatures.hpp"
+#include "__transform_sender.hpp"
+#include "__tuple.hpp"
+#include "__type_traits.hpp"
+#include "__utility.hpp"
+#include "__variant.hpp"
+
+#include <atomic>
+#include <exception>
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.when_all]
+// [execution.senders.adaptors.when_all_with_variant]
+namespace __when_all
+{
+enum __state_t
+{
+ __started,
+ __error,
+ __stopped
+};
+
+struct __on_stop_request
+{
+ inplace_stop_source& __stop_source_;
+
+ void operator()() noexcept
+ {
+ __stop_source_.request_stop();
+ }
+};
+
+template <class _Env>
+auto __mkenv(_Env&& __env, const inplace_stop_source& __stop_source) noexcept
+{
+ return __env::__join(prop{get_stop_token, __stop_source.get_token()},
+ static_cast<_Env&&>(__env));
+}
+
+template <class _Env>
+using __env_t = //
+ decltype(__when_all::__mkenv(__declval<_Env>(),
+ __declval<inplace_stop_source&>()));
+
+template <class _Sender, class _Env>
+concept __max1_sender = sender_in<_Sender, _Env> &&
+ __mvalid<__value_types_of_t, _Sender, _Env,
+ __mconst<int>, __msingle_or<void>>;
+
+template <
+ __mstring _Context = "In stdexec::when_all()..."_mstr,
+ __mstring _Diagnostic =
+ "The given sender can complete successfully in more that one way. "
+ "Use stdexec::when_all_with_variant() instead."_mstr>
+struct _INVALID_WHEN_ALL_ARGUMENT_;
+
+template <class _Sender, class... _Env>
+using __too_many_value_completions_error =
+ __mexception<_INVALID_WHEN_ALL_ARGUMENT_<>, _WITH_SENDER_<_Sender>,
+ _WITH_ENVIRONMENT_<_Env>...>;
+
+template <class... _Args>
+using __all_nothrow_decay_copyable =
+ __mbool<(__nothrow_decay_copyable<_Args> && ...)>;
+
+template <class _Error>
+using __set_error_t = completion_signatures<set_error_t(__decay_t<_Error>)>;
+
+template <class _Sender, class... _Env>
+using __nothrow_decay_copyable_results = //
+ __for_each_completion_signature<
+ __completion_signatures_of_t<_Sender, _Env...>,
+ __all_nothrow_decay_copyable, __mand_t>;
+
+template <class... _Env>
+struct __completions_t
+{
+ template <class... _Senders>
+ using __all_nothrow_decay_copyable_results = //
+ __mand<__nothrow_decay_copyable_results<_Senders, _Env...>...>;
+
+ template <class _Sender, class _ValueTuple, class... _Rest>
+ using __value_tuple_t =
+ __minvoke<__if_c<(0 == sizeof...(_Rest)), __mconst<_ValueTuple>,
+ __q<__too_many_value_completions_error>>,
+ _Sender, _Env...>;
+
+ template <class _Sender>
+ using __single_values_of_t = //
+ __value_types_t<__completion_signatures_of_t<_Sender, _Env...>,
+ __mtransform<__q<__decay_t>, __q<__types>>,
+ __mbind_front_q<__value_tuple_t, _Sender>>;
+
+ template <class... _Senders>
+ using __set_values_sig_t = //
+ __meval<completion_signatures,
+ __minvoke<__mconcat<__qf<set_value_t>>,
+ __single_values_of_t<_Senders>...>>;
+
+ template <class... _Senders>
+ using __f = //
+ __meval< //
+ __concat_completion_signatures,
+ __meval<__eptr_completion_if_t,
+ __all_nothrow_decay_copyable_results<_Senders...>>,
+ completion_signatures<set_stopped_t()>,
+ __minvoke<__with_default<__qq<__set_values_sig_t>,
+ completion_signatures<>>,
+ _Senders...>,
+ __transform_completion_signatures<
+ __completion_signatures_of_t<_Senders, _Env...>,
+ __mconst<completion_signatures<>>::__f, __set_error_t,
+ completion_signatures<>, __concat_completion_signatures>...>;
+};
+
+template <class _Tag, class _Receiver>
+auto __complete_fn(_Tag, _Receiver& __rcvr) noexcept
+{
+ return [&]<class... _Ts>(_Ts&... __ts) noexcept {
+ _Tag()(static_cast<_Receiver&&>(__rcvr), static_cast<_Ts&&>(__ts)...);
+ };
+}
+
+template <class _Receiver, class _ValuesTuple>
+void __set_values(_Receiver& __rcvr, _ValuesTuple& __values) noexcept
+{
+ __values.apply(
+ [&](auto&... __opt_vals) noexcept -> void {
+ __tup::__cat_apply(__when_all::__complete_fn(set_value, __rcvr),
+ *__opt_vals...);
+ },
+ __values);
+}
+
+template <class _Env, class _Sender>
+using __values_opt_tuple_t = //
+ value_types_of_t<_Sender, __env_t<_Env>, __decayed_tuple, __optional>;
+
+template <class _Env, __max1_sender<__env_t<_Env>>... _Senders>
+struct __traits
+{
+ // tuple<optional<tuple<Vs1...>>, optional<tuple<Vs2...>>, ...>
+ using __values_tuple = //
+ __minvoke<__with_default<
+ __mtransform<__mbind_front_q<__values_opt_tuple_t, _Env>,
+ __q<__tuple_for>>,
+ __ignore>,
+ _Senders...>;
+
+ using __collect_errors = __mbind_front_q<__mset_insert, __mset<>>;
+
+ using __errors_list = //
+ __minvoke<
+ __mconcat<>,
+ __if<__mand<__nothrow_decay_copyable_results<_Senders, _Env>...>,
+ __types<>, __types<std::exception_ptr>>,
+ __error_types_of_t<_Senders, __env_t<_Env>, __q<__types>>...>;
+
+ using __errors_variant =
+ __mapply<__q<__uniqued_variant_for>, __errors_list>;
+};
+
+struct _INVALID_ARGUMENTS_TO_WHEN_ALL_
+{};
+
+template <class _ErrorsVariant, class _ValuesTuple, class _StopToken>
+struct __when_all_state
+{
+ using __stop_callback_t =
+ stop_callback_for_t<_StopToken, __on_stop_request>;
+
+ template <class _Receiver>
+ void __arrive(_Receiver& __rcvr) noexcept
+ {
+ if (0 == --__count_)
+ {
+ __complete(__rcvr);
+ }
+ }
+
+ template <class _Receiver>
+ void __complete(_Receiver& __rcvr) noexcept
+ {
+ // Stop callback is no longer needed. Destroy it.
+ __on_stop_.reset();
+ // All child operations have completed and arrived at the barrier.
+ switch (__state_.load(std::memory_order_relaxed))
+ {
+ case __started:
+ if constexpr (!same_as<_ValuesTuple, __ignore>)
+ {
+ // All child operations completed successfully:
+ __when_all::__set_values(__rcvr, __values_);
+ }
+ break;
+ case __error:
+ if constexpr (!__same_as<_ErrorsVariant, __variant_for<>>)
+ {
+ // One or more child operations completed with an error:
+ __errors_.visit(__complete_fn(set_error, __rcvr),
+ __errors_);
+ }
+ break;
+ case __stopped:
+ stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr));
+ break;
+ default:;
+ }
+ }
+
+ std::atomic<std::size_t> __count_;
+ inplace_stop_source __stop_source_{};
+ // Could be non-atomic here and atomic_ref everywhere except __completion_fn
+ std::atomic<__state_t> __state_{__started};
+ _ErrorsVariant __errors_{};
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _ValuesTuple __values_{};
+ __optional<__stop_callback_t> __on_stop_{};
+};
+
+template <class _Env>
+static auto __mk_state_fn(const _Env&) noexcept
+{
+ return []<__max1_sender<__env_t<_Env>>... _Child>(__ignore, __ignore,
+ _Child&&...) {
+ using _Traits = __traits<_Env, _Child...>;
+ using _ErrorsVariant = typename _Traits::__errors_variant;
+ using _ValuesTuple = typename _Traits::__values_tuple;
+ using _State = __when_all_state<_ErrorsVariant, _ValuesTuple,
+ stop_token_of_t<_Env>>;
+ return _State{sizeof...(_Child)};
+ };
+}
+
+template <class _Env>
+using __mk_state_fn_t = decltype(__when_all::__mk_state_fn(__declval<_Env>()));
+
+struct when_all_t
+{
+ // Used by the default_domain to find legacy customizations:
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(when_all_t, _Sender...)>;
+
+ template <sender... _Senders>
+ requires __domain::__has_common_domain<_Senders...>
+ auto operator()(_Senders&&... __sndrs) const -> __well_formed_sender auto
+ {
+ auto __domain = __domain::__common_domain_t<_Senders...>();
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<when_all_t>(
+ __(), static_cast<_Senders&&>(__sndrs)...));
+ }
+};
+
+struct __when_all_impl : __sexpr_defaults
+{
+ template <class _Self, class _Env>
+ using __error_t = __mexception<_INVALID_ARGUMENTS_TO_WHEN_ALL_,
+ __children_of<_Self, __q<_WITH_SENDERS_>>,
+ _WITH_ENVIRONMENT_<_Env>>;
+
+ template <class _Self, class... _Env>
+ using __completions =
+ __children_of<_Self, __completions_t<__env_t<_Env>...>>;
+
+ static constexpr auto get_attrs = //
+ []<class... _Child>(__ignore, const _Child&...) noexcept {
+ using _Domain = __domain::__common_domain_t<_Child...>;
+ if constexpr (__same_as<_Domain, default_domain>)
+ {
+ return env();
+ }
+ else
+ {
+ return prop{get_domain, _Domain()};
+ }
+ };
+
+ static constexpr auto get_completion_signatures = //
+ []<class _Self, class... _Env>(_Self&&, _Env&&...) noexcept {
+ static_assert(sender_expr_for<_Self, when_all_t>);
+ return __minvoke<__mtry_catch<__q<__completions>, __q<__error_t>>,
+ _Self, _Env...>();
+ };
+
+ static constexpr auto get_env = //
+ []<class _State, class _Receiver>(__ignore, _State& __state,
+ const _Receiver& __rcvr) noexcept //
+ -> __env_t<env_of_t<const _Receiver&>> {
+ return __mkenv(stdexec::get_env(__rcvr), __state.__stop_source_);
+ };
+
+ static constexpr auto get_state = //
+ []<class _Self, class _Receiver>(_Self&& __self, _Receiver& __rcvr)
+ -> __sexpr_apply_result_t<_Self, __mk_state_fn_t<env_of_t<_Receiver>>> {
+ return __sexpr_apply(
+ static_cast<_Self&&>(__self),
+ __when_all::__mk_state_fn(stdexec::get_env(__rcvr)));
+ };
+
+ static constexpr auto start = //
+ []<class _State, class _Receiver, class... _Operations>(
+ _State& __state, _Receiver& __rcvr,
+ _Operations&... __child_ops) noexcept -> void {
+ // register stop callback:
+ __state.__on_stop_.emplace(get_stop_token(stdexec::get_env(__rcvr)),
+ __on_stop_request{__state.__stop_source_});
+ if (__state.__stop_source_.stop_requested())
+ {
+ // Stop has already been requested. Don't bother starting
+ // the child operations.
+ stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr));
+ }
+ else
+ {
+ (stdexec::start(__child_ops), ...);
+ if constexpr (sizeof...(__child_ops) == 0)
+ {
+ __state.__complete(__rcvr);
+ }
+ }
+ };
+
+ template <class _State, class _Receiver, class _Error>
+ static void __set_error(_State& __state, _Receiver&,
+ _Error&& __err) noexcept
+ {
+ // TODO: What memory orderings are actually needed here?
+ if (__error != __state.__state_.exchange(__error))
+ {
+ __state.__stop_source_.request_stop();
+ // We won the race, free to write the error into the operation
+ // state without worry.
+ if constexpr (__nothrow_decay_copyable<_Error>)
+ {
+ __state.__errors_.template emplace<__decay_t<_Error>>(
+ static_cast<_Error&&>(__err));
+ }
+ else
+ {
+ try
+ {
+ __state.__errors_.template emplace<__decay_t<_Error>>(
+ static_cast<_Error&&>(__err));
+ }
+ catch (...)
+ {
+ __state.__errors_.template emplace<std::exception_ptr>(
+ std::current_exception());
+ }
+ }
+ }
+ }
+
+ static constexpr auto complete = //
+ []<class _Index, class _State, class _Receiver, class _Set,
+ class... _Args>(_Index, _State& __state, _Receiver& __rcvr, _Set,
+ _Args&&... __args) noexcept -> void {
+ if constexpr (__same_as<_Set, set_error_t>)
+ {
+ __set_error(__state, __rcvr, static_cast<_Args&&>(__args)...);
+ }
+ else if constexpr (__same_as<_Set, set_stopped_t>)
+ {
+ __state_t __expected = __started;
+ // Transition to the "stopped" state if and only if we're in the
+ // "started" state. (If this fails, it's because we're in an
+ // error state, which trumps cancellation.)
+ if (__state.__state_.compare_exchange_strong(__expected, __stopped))
+ {
+ __state.__stop_source_.request_stop();
+ }
+ }
+ else if constexpr (!__same_as<decltype(_State::__values_), __ignore>)
+ {
+ // We only need to bother recording the completion values
+ // if we're not already in the "error" or "stopped" state.
+ if (__state.__state_ == __started)
+ {
+ auto& __opt_values = __tup::get<__v<_Index>>(__state.__values_);
+ using _Tuple = __decayed_tuple<_Args...>;
+ static_assert(
+ __same_as<decltype(*__opt_values), _Tuple&>,
+ "One of the senders in this when_all() is fibbing about what types it sends");
+ if constexpr ((__nothrow_decay_copyable<_Args> && ...))
+ {
+ __opt_values.emplace(
+ _Tuple{{static_cast<_Args&&>(__args)}...});
+ }
+ else
+ {
+ try
+ {
+ __opt_values.emplace(
+ _Tuple{{static_cast<_Args&&>(__args)}...});
+ }
+ catch (...)
+ {
+ __set_error(__state, __rcvr, std::current_exception());
+ }
+ }
+ }
+ }
+
+ __state.__arrive(__rcvr);
+ };
+};
+
+struct when_all_with_variant_t
+{
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(when_all_with_variant_t, _Sender...)>;
+
+ template <sender... _Senders>
+ requires __domain::__has_common_domain<_Senders...>
+ auto operator()(_Senders&&... __sndrs) const -> __well_formed_sender auto
+ {
+ auto __domain = __domain::__common_domain_t<_Senders...>();
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<when_all_with_variant_t>(
+ __(), static_cast<_Senders&&>(__sndrs)...));
+ }
+
+ template <class _Sender, class _Env>
+ static auto transform_sender(_Sender&& __sndr, const _Env&)
+ {
+ // transform the when_all_with_variant into a regular when_all (looking
+ // for early when_all customizations), then transform it again to look
+ // for late customizations.
+ return __sexpr_apply(
+ static_cast<_Sender&&>(__sndr),
+ [&]<class... _Child>(__ignore, __ignore, _Child&&... __child) {
+ return when_all_t()(
+ into_variant(static_cast<_Child&&>(__child))...);
+ });
+ }
+};
+
+struct __when_all_with_variant_impl : __sexpr_defaults
+{
+ static constexpr auto get_attrs = //
+ []<class... _Child>(__ignore, const _Child&...) noexcept {
+ using _Domain = __domain::__common_domain_t<_Child...>;
+ if constexpr (same_as<_Domain, default_domain>)
+ {
+ return env();
+ }
+ else
+ {
+ return prop{get_domain, _Domain()};
+ }
+ };
+
+ static constexpr auto get_completion_signatures = //
+ []<class _Sender>(_Sender&&) noexcept //
+ -> __completion_signatures_of_t< //
+ transform_sender_result_t<default_domain, _Sender, empty_env>> {
+ return {};
+ };
+};
+
+struct transfer_when_all_t
+{
+ using _Env = __0;
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(
+ transfer_when_all_t,
+ get_completion_scheduler_t<set_value_t>(const _Env&), _Sender...)>;
+
+ template <scheduler _Scheduler, sender... _Senders>
+ requires __domain::__has_common_domain<_Senders...>
+ auto operator()(_Scheduler&& __sched, _Senders&&... __sndrs) const
+ -> __well_formed_sender auto
+ {
+ using _Env = __t<__schfr::__environ<__id<__decay_t<_Scheduler>>>>;
+ auto __domain = query_or(get_domain, __sched, default_domain());
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<transfer_when_all_t>(
+ _Env{static_cast<_Scheduler&&>(__sched)},
+ static_cast<_Senders&&>(__sndrs)...));
+ }
+
+ template <class _Sender, class _Env>
+ static auto transform_sender(_Sender&& __sndr, const _Env&)
+ {
+ // transform the transfer_when_all into a regular transform | when_all
+ // (looking for early customizations), then transform it again to look
+ // for late customizations.
+ return __sexpr_apply(
+ static_cast<_Sender&&>(__sndr),
+ [&]<class _Data, class... _Child>(__ignore, _Data&& __data,
+ _Child&&... __child) {
+ return continue_on(when_all_t()(static_cast<_Child&&>(__child)...),
+ get_completion_scheduler<set_value_t>(__data));
+ });
+ }
+};
+
+struct __transfer_when_all_impl : __sexpr_defaults
+{
+ static constexpr auto get_attrs = //
+ []<class _Data>(const _Data& __data,
+ const auto&...) noexcept -> const _Data& {
+ return __data;
+ };
+
+ static constexpr auto get_completion_signatures = //
+ []<class _Sender>(_Sender&&) noexcept //
+ -> __completion_signatures_of_t< //
+ transform_sender_result_t<default_domain, _Sender, empty_env>> {
+ return {};
+ };
+};
+
+struct transfer_when_all_with_variant_t
+{
+ using _Env = __0;
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(
+ transfer_when_all_with_variant_t,
+ get_completion_scheduler_t<set_value_t>(const _Env&), _Sender...)>;
+
+ template <scheduler _Scheduler, sender... _Senders>
+ requires __domain::__has_common_domain<_Senders...>
+ auto operator()(_Scheduler&& __sched, _Senders&&... __sndrs) const
+ -> __well_formed_sender auto
+ {
+ using _Env = __t<__schfr::__environ<__id<__decay_t<_Scheduler>>>>;
+ auto __domain = query_or(get_domain, __sched, default_domain());
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<transfer_when_all_with_variant_t>(
+ _Env{{static_cast<_Scheduler&&>(__sched)}},
+ static_cast<_Senders&&>(__sndrs)...));
+ }
+
+ template <class _Sender, class _Env>
+ static auto transform_sender(_Sender&& __sndr, const _Env&)
+ {
+ // transform the transfer_when_all_with_variant into regular
+ // transform_when_all and into_variant calls/ (looking for early
+ // customizations), then transform it again to look for late
+ // customizations.
+ return __sexpr_apply(
+ static_cast<_Sender&&>(__sndr),
+ [&]<class _Data, class... _Child>(__ignore, _Data&& __data,
+ _Child&&... __child) {
+ return transfer_when_all_t()(
+ get_completion_scheduler<set_value_t>(
+ static_cast<_Data&&>(__data)),
+ into_variant(static_cast<_Child&&>(__child))...);
+ });
+ }
+};
+
+struct __transfer_when_all_with_variant_impl : __sexpr_defaults
+{
+ static constexpr auto get_attrs = //
+ []<class _Data>(const _Data& __data,
+ const auto&...) noexcept -> const _Data& {
+ return __data;
+ };
+
+ 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 __when_all
+
+using __when_all::when_all_t;
+inline constexpr when_all_t when_all{};
+
+using __when_all::when_all_with_variant_t;
+inline constexpr when_all_with_variant_t when_all_with_variant{};
+
+using __when_all::transfer_when_all_t;
+inline constexpr transfer_when_all_t transfer_when_all{};
+
+using __when_all::transfer_when_all_with_variant_t;
+inline constexpr transfer_when_all_with_variant_t
+ transfer_when_all_with_variant{};
+
+template <>
+struct __sexpr_impl<when_all_t> : __when_all::__when_all_impl
+{};
+
+template <>
+struct __sexpr_impl<when_all_with_variant_t> :
+ __when_all::__when_all_with_variant_impl
+{};
+
+template <>
+struct __sexpr_impl<transfer_when_all_t> : __when_all::__transfer_when_all_impl
+{};
+
+template <>
+struct __sexpr_impl<transfer_when_all_with_variant_t> :
+ __when_all::__transfer_when_all_with_variant_impl
+{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__with_awaitable_senders.hpp b/include/sdbusplus/async/stdexec/__detail/__with_awaitable_senders.hpp
new file mode 100644
index 0000000..c522394
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__with_awaitable_senders.hpp
@@ -0,0 +1,153 @@
+/*
+ * 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 "__as_awaitable.hpp"
+#include "__concepts.hpp"
+#include "__execution_fwd.hpp"
+
+#include <exception>
+
+namespace stdexec
+{
+#if !STDEXEC_STD_NO_COROUTINES()
+namespace __was
+{
+template <class _Promise = void>
+class __continuation_handle;
+
+template <>
+class __continuation_handle<void>
+{
+ public:
+ __continuation_handle() = default;
+
+ template <class _Promise>
+ __continuation_handle(__coro::coroutine_handle<_Promise> __coro) noexcept :
+ __coro_(__coro)
+ {
+ if constexpr (requires(_Promise& __promise) {
+ __promise.unhandled_stopped();
+ })
+ {
+ __stopped_callback_ =
+ [](void* __address) noexcept -> __coro::coroutine_handle<> {
+ // This causes the rest of the coroutine (the part after the
+ // co_await of the sender) to be skipped and invokes the calling
+ // coroutine's stopped handler.
+ return __coro::coroutine_handle<_Promise>::from_address(
+ __address)
+ .promise()
+ .unhandled_stopped();
+ };
+ }
+ // If _Promise doesn't implement unhandled_stopped(), then if a
+ // "stopped" unwind reaches this point, it's considered an unhandled
+ // exception and terminate() is called.
+ }
+
+ [[nodiscard]] auto handle() const noexcept -> __coro::coroutine_handle<>
+ {
+ return __coro_;
+ }
+
+ [[nodiscard]] auto unhandled_stopped() const noexcept
+ -> __coro::coroutine_handle<>
+ {
+ return __stopped_callback_(__coro_.address());
+ }
+
+ private:
+ using __stopped_callback_t = __coro::coroutine_handle<> (*)(void*) noexcept;
+
+ __coro::coroutine_handle<> __coro_{};
+ __stopped_callback_t __stopped_callback_ =
+ [](void*) noexcept -> __coro::coroutine_handle<> { std::terminate(); };
+};
+
+template <class _Promise>
+class __continuation_handle
+{
+ public:
+ __continuation_handle() = default;
+
+ __continuation_handle(__coro::coroutine_handle<_Promise> __coro) noexcept :
+ __continuation_{__coro}
+ {}
+
+ auto handle() const noexcept -> __coro::coroutine_handle<_Promise>
+ {
+ return __coro::coroutine_handle<_Promise>::from_address(
+ __continuation_.handle().address());
+ }
+
+ [[nodiscard]] auto unhandled_stopped() const noexcept
+ -> __coro::coroutine_handle<>
+ {
+ return __continuation_.unhandled_stopped();
+ }
+
+ private:
+ __continuation_handle<> __continuation_{};
+};
+
+struct __with_awaitable_senders_base
+{
+ template <class _OtherPromise>
+ void set_continuation(
+ __coro::coroutine_handle<_OtherPromise> __hcoro) noexcept
+ {
+ static_assert(!__same_as<_OtherPromise, void>);
+ __continuation_ = __hcoro;
+ }
+
+ void set_continuation(__continuation_handle<> __continuation) noexcept
+ {
+ __continuation_ = __continuation;
+ }
+
+ [[nodiscard]] auto continuation() const noexcept -> __continuation_handle<>
+ {
+ return __continuation_;
+ }
+
+ auto unhandled_stopped() noexcept -> __coro::coroutine_handle<>
+ {
+ return __continuation_.unhandled_stopped();
+ }
+
+ private:
+ __continuation_handle<> __continuation_{};
+};
+
+template <class _Promise>
+struct with_awaitable_senders : __with_awaitable_senders_base
+{
+ template <class _Value>
+ auto await_transform(_Value&& __val)
+ -> __call_result_t<as_awaitable_t, _Value, _Promise&>
+ {
+ static_assert(derived_from<_Promise, with_awaitable_senders>);
+ return as_awaitable(static_cast<_Value&&>(__val),
+ static_cast<_Promise&>(*this));
+ }
+};
+} // namespace __was
+
+using __was::__continuation_handle;
+using __was::with_awaitable_senders;
+#endif
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__write_env.hpp b/include/sdbusplus/async/stdexec/__detail/__write_env.hpp
new file mode 100644
index 0000000..3afb894
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__write_env.hpp
@@ -0,0 +1,93 @@
+/*
+ * 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 "__env.hpp"
+#include "__sender_adaptor_closure.hpp"
+#include "__senders.hpp"
+#include "__transform_sender.hpp"
+#include "__utility.hpp"
+
+namespace stdexec
+{
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// __write adaptor
+namespace __write_
+{
+struct __write_env_t
+{
+ template <sender _Sender, class _Env>
+ auto operator()(_Sender&& __sndr, _Env __env) const
+ {
+ return __make_sexpr<__write_env_t>(static_cast<_Env&&>(__env),
+ static_cast<_Sender&&>(__sndr));
+ }
+
+ template <class _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ auto operator()(_Env __env) const -> __binder_back<__write_env_t, _Env>
+ {
+ return {{static_cast<_Env&&>(__env)}, {}, {}};
+ }
+
+ template <class _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ static auto __transform_env_fn(_Env&& __env) noexcept
+ {
+ return [&](__ignore, const auto& __state, __ignore) noexcept {
+ return __env::__join(__state, static_cast<_Env&&>(__env));
+ };
+ }
+
+ template <sender_expr_for<__write_env_t> _Self, class _Env>
+ static auto transform_env(const _Self& __self, _Env&& __env) noexcept
+ {
+ return __sexpr_apply(__self,
+ __transform_env_fn(static_cast<_Env&&>(__env)));
+ }
+};
+
+struct __write_env_impl : __sexpr_defaults
+{
+ static constexpr auto get_env = //
+ [](__ignore, const auto& __state, const auto& __rcvr) noexcept {
+ return __env::__join(__state, stdexec::get_env(__rcvr));
+ };
+
+ static constexpr auto get_completion_signatures = //
+ []<class _Self, class... _Env>(_Self&&, _Env&&...) noexcept
+ -> __completion_signatures_of_t<
+ __child_of<_Self>,
+ __meval<__env::__join_t, const __decay_t<__data_of<_Self>>&,
+ _Env...>> {
+ static_assert(sender_expr_for<_Self, __write_env_t>);
+ return {};
+ };
+};
+} // namespace __write_
+
+using __write_::__write_env_t;
+inline constexpr __write_env_t __write{};
+inline constexpr __write_env_t __write_env{};
+
+template <>
+struct __sexpr_impl<__write_env_t> : __write_::__write_env_impl
+{};
+} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/any_sender_of.hpp b/include/sdbusplus/async/stdexec/any_sender_of.hpp
index e65118c..e7658a8 100644
--- a/include/sdbusplus/async/stdexec/any_sender_of.hpp
+++ b/include/sdbusplus/async/stdexec/any_sender_of.hpp
@@ -92,12 +92,11 @@
}
};
-template <class>
+template <class _Queryable, bool _IsEnvProvider = true>
struct __query_vfun_fn;
template <class _EnvProvider>
- requires __callable<get_env_t, const _EnvProvider&>
-struct __query_vfun_fn<_EnvProvider>
+struct __query_vfun_fn<_EnvProvider, true>
{
template <class _Tag, class _Ret, class... _As>
requires __callable<_Tag, env_of_t<const _EnvProvider&>, _As...>
@@ -105,9 +104,9 @@
const noexcept)(void*, _As...)
{
return +[](void* __env_provider, _As... __as) -> _Ret {
- return _Tag{}(
- get_env(*static_cast<const _EnvProvider*>(__env_provider)),
- static_cast<_As&&>(__as)...);
+ return _Tag{}(stdexec::get_env(*static_cast<const _EnvProvider*>(
+ __env_provider)),
+ static_cast<_As&&>(__as)...);
};
}
@@ -120,16 +119,15 @@
static_assert(
__nothrow_callable<_Tag, const env_of_t<_EnvProvider>&,
_As...>);
- return _Tag{}(
- get_env(*static_cast<const _EnvProvider*>(__env_provider)),
- static_cast<_As&&>(__as)...);
+ return _Tag{}(stdexec::get_env(*static_cast<const _EnvProvider*>(
+ __env_provider)),
+ static_cast<_As&&>(__as)...);
};
}
};
template <class _Queryable>
- requires(!__callable<get_env_t, const _Queryable&>)
-struct __query_vfun_fn<_Queryable>
+struct __query_vfun_fn<_Queryable, false>
{
template <class _Tag, class _Ret, class... _As>
requires __callable<_Tag, const _Queryable&, _As...>
@@ -227,12 +225,9 @@
template <class _Storage, class _Tp>
requires tag_invocable<__copy_construct_t, __mtype<_Tp>, _Storage&,
const _Storage&>
- void operator()(
- __mtype<_Tp>, _Storage& __self,
- const _Storage&
- __from) noexcept(nothrow_tag_invocable<__copy_construct_t,
- __mtype<_Tp>, _Storage&,
- const _Storage&>)
+ void operator()(__mtype<_Tp>, _Storage& __self, const _Storage& __from) //
+ noexcept(nothrow_tag_invocable<__copy_construct_t, __mtype<_Tp>,
+ _Storage&, const _Storage&>)
{
stdexec::tag_invoke(__copy_construct_t{}, __mtype<_Tp>{}, __self,
__from);
@@ -299,16 +294,16 @@
static_cast<_StorageCPOs*>(nullptr))}...};
template <class _Vtable, class _Allocator, bool _Copyable = false,
- std::size_t _Alignment = alignof(std::max_align_t),
- std::size_t _InlineSize = 3 * sizeof(void*)>
+ std::size_t _InlineSize = 3 * sizeof(void*),
+ std::size_t _Alignment = alignof(std::max_align_t)>
struct __storage
{
class __t;
};
template <class _Vtable, class _Allocator,
- std::size_t _Alignment = alignof(std::max_align_t),
- std::size_t _InlineSize = 3 * sizeof(void*)>
+ std::size_t _InlineSize = 3 * sizeof(void*),
+ std::size_t _Alignment = alignof(std::max_align_t)>
struct __immovable_storage
{
class __t : __immovable
@@ -430,7 +425,8 @@
}
template <class _Tp>
- friend void tag_invoke(__delete_t, __mtype<_Tp>, __t& __self) noexcept
+ STDEXEC_MEMFN_DECL(void __delete)
+ (this __mtype<_Tp>, __t& __self) noexcept
{
if (!__self.__object_pointer_)
{
@@ -460,8 +456,8 @@
};
template <class _Vtable, class _Allocator, bool _Copyable,
- std::size_t _Alignment, std::size_t _InlineSize>
-class __storage<_Vtable, _Allocator, _Copyable, _Alignment, _InlineSize>::__t :
+ std::size_t _InlineSize, std::size_t _Alignment>
+class __storage<_Vtable, _Allocator, _Copyable, _InlineSize, _Alignment>::__t :
__if_c<_Copyable, __, __move_only>
{
static_assert(STDEXEC_IS_CONVERTIBLE_TO(
@@ -625,7 +621,8 @@
}
template <class _Tp>
- friend void tag_invoke(__delete_t, __mtype<_Tp>, __t& __self) noexcept
+ STDEXEC_MEMFN_DECL(void __delete)
+ (this __mtype<_Tp>, __t& __self) noexcept
{
if (!__self.__object_pointer_)
{
@@ -644,8 +641,8 @@
}
template <class _Tp>
- friend void tag_invoke(__move_construct_t, __mtype<_Tp>, __t& __self,
- __t&& __other) noexcept
+ STDEXEC_MEMFN_DECL(void __move_construct)
+ (this __mtype<_Tp>, __t& __self, __t&& __other) noexcept
{
if (!__other.__object_pointer_)
{
@@ -674,8 +671,8 @@
template <class _Tp>
requires _Copyable
- friend void tag_invoke(__copy_construct_t, __mtype<_Tp>, __t& __self,
- const __t& __other)
+ STDEXEC_MEMFN_DECL(void __copy_construct)(this __mtype<_Tp>, __t& __self,
+ const __t& __other)
{
if (!__other.__object_pointer_)
{
@@ -705,8 +702,9 @@
struct __empty_vtable
{
template <class _Sender>
- friend auto tag_invoke(__create_vtable_t, __mtype<__empty_vtable>,
- __mtype<_Sender>) noexcept -> const __empty_vtable*
+ STDEXEC_MEMFN_DECL(auto __create_vtable)
+ (this __mtype<__empty_vtable>, __mtype<_Sender>) noexcept
+ -> const __empty_vtable*
{
static const __empty_vtable __vtable_{};
return &__vtable_;
@@ -720,8 +718,10 @@
template <class _VTable, class _Allocator = std::allocator<std::byte>>
using __unique_storage_t = __t<__storage<_VTable, _Allocator>>;
-template <class _VTable, class _Allocator = std::allocator<std::byte>>
-using __copyable_storage_t = __t<__storage<_VTable, _Allocator, true>>;
+template <class _VTable, std::size_t _InlineSize = 3 * sizeof(void*),
+ class _Allocator = std::allocator<std::byte>>
+using __copyable_storage_t =
+ __t<__storage<_VTable, _Allocator, true, _InlineSize>>;
template <class _Tag, class... _As>
auto __tag_type(_Tag (*)(_As...)) -> _Tag;
@@ -744,9 +744,6 @@
namespace __rec
{
-template <class _Sig>
-struct __rcvr_vfun;
-
template <class _Sigs, class... _Queries>
struct __vtable
{
@@ -756,30 +753,12 @@
template <class _Sigs, class... _Queries>
struct __ref;
-template <class _Tag, class... _As>
-struct __rcvr_vfun<_Tag(_As...)>
-{
- void (*__fn_)(void*, _As...) noexcept;
-};
-
-template <class _Rcvr>
-struct __rcvr_vfun_fn
-{
- template <class _Tag, class... _As>
- constexpr void (*operator()(_Tag (*)(_As...))
- const noexcept)(void*, _As...) noexcept
- {
- return +[](void* __rcvr, _As... __as) noexcept -> void {
- _Tag{}(static_cast<_Rcvr&&>(*static_cast<_Rcvr*>(__rcvr)),
- static_cast<_As&&>(__as)...);
- };
- }
-};
-
template <class... _Sigs, class... _Queries>
struct __vtable<completion_signatures<_Sigs...>, _Queries...>
{
- class __t : public __rcvr_vfun<_Sigs>..., public __query_vfun<_Queries>...
+ class __t :
+ public __any_::__rcvr_vfun<_Sigs>...,
+ public __query_vfun<_Queries>...
{
public:
using __query_vfun<_Queries>::operator()...;
@@ -788,11 +767,13 @@
template <class _Rcvr>
requires receiver_of<_Rcvr, completion_signatures<_Sigs...>> &&
(__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...)
- friend auto tag_invoke(__create_vtable_t, __mtype<__t>,
- __mtype<_Rcvr>) noexcept -> const __t*
+ STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__t>,
+ __mtype<_Rcvr>) noexcept
+ -> const __t*
{
static const __t __vtable_{
- {__rcvr_vfun_fn<_Rcvr>{}(static_cast<_Sigs*>(nullptr))}...,
+ {__any_::__rcvr_vfun_fn(static_cast<_Rcvr*>(nullptr),
+ static_cast<_Sigs*>(nullptr))}...,
{__query_vfun_fn<_Rcvr>{}(static_cast<_Queries>(nullptr))}...};
return &__vtable_;
}
@@ -816,23 +797,20 @@
{
const __vtable_t* __vtable_;
void* __rcvr_;
- in_place_stop_token __token_;
+ inplace_stop_token __token_;
template <class _Tag, class... _As>
requires __callable<const __vtable_t&, _Tag, void*, _As...>
- friend auto
- tag_invoke(_Tag, const __env_t& __self, _As&&... __as) noexcept(
- __nothrow_callable<const __vtable_t&, _Tag, void*, _As...>)
+ auto query(_Tag, _As&&... __as) const //
+ noexcept(__nothrow_callable<const __vtable_t&, _Tag, void*, _As...>)
-> __call_result_t<const __vtable_t&, _Tag, void*, _As...>
{
- return (*__self.__vtable_)(_Tag{}, __self.__rcvr_,
- static_cast<_As&&>(__as)...);
+ return (*__vtable_)(_Tag{}, __rcvr_, static_cast<_As&&>(__as)...);
}
- friend auto tag_invoke(get_stop_token_t, const __env_t& __self) noexcept
- -> in_place_stop_token
+ auto query(get_stop_token_t) const noexcept -> inplace_stop_token
{
- return __self.__token_;
+ return __token_;
}
} __env_;
@@ -849,20 +827,34 @@
&__rcvr, stdexec::get_stop_token(stdexec::get_env(__rcvr))}
{}
- template <__completion_tag _Tag, __decays_to<__ref> _Self, class... _As>
- requires __one_of<_Tag(_As...), _Sigs...>
- friend void tag_invoke(_Tag, _Self&& __self, _As&&... __as) noexcept
+ template <class... _As>
+ requires __one_of<set_value_t(_As...), _Sigs...>
+ void set_value(_As&&... __as) noexcept
{
- (*static_cast<const __rcvr_vfun<_Tag(_As...)>*>(__self.__env_.__vtable_)
- ->__fn_)(static_cast<_Self&&>(__self).__env_.__rcvr_,
- static_cast<_As&&>(__as)...);
+ const __any_::__rcvr_vfun<set_value_t(_As...)>* __vfun =
+ __env_.__vtable_;
+ (*__vfun->__complete_)(__env_.__rcvr_, static_cast<_As&&>(__as)...);
}
- template <std::same_as<__ref> Self>
- friend auto tag_invoke(get_env_t, const Self& __self) noexcept
- -> const __env_t&
+ template <class _Error>
+ requires __one_of<set_error_t(_Error), _Sigs...>
+ void set_error(_Error&& __err) noexcept
{
- return __self.__env_;
+ const __any_::__rcvr_vfun<set_error_t(_Error)>* __vfun =
+ __env_.__vtable_;
+ (*__vfun->__complete_)(__env_.__rcvr_, static_cast<_Error&&>(__err));
+ }
+
+ void set_stopped() noexcept
+ requires __one_of<set_stopped_t(), _Sigs...>
+ {
+ const __any_::__rcvr_vfun<set_stopped_t()>* __vfun = __env_.__vtable_;
+ (*__vfun->__complete_)(__env_.__rcvr_);
+ }
+
+ auto get_env() const noexcept -> const __env_t&
+ {
+ return __env_;
}
};
@@ -891,7 +883,7 @@
private:
#endif
using _FilteredQueries =
- __minvoke<__remove_if<__q<__is_never_stop_token_query>>, _Queries...>;
+ __minvoke<__mremove_if<__q<__is_never_stop_token_query>>, _Queries...>;
using __vtable_t = stdexec::__t<
__mapply<__mbind_front_q<__vtable, completion_signatures<_Sigs...>>,
_FilteredQueries>>;
@@ -903,13 +895,11 @@
template <class _Tag, class... _As>
requires __callable<const __vtable_t&, _Tag, void*, _As...>
- friend auto
- tag_invoke(_Tag, const __env_t& __self, _As&&... __as) noexcept(
- __nothrow_callable<const __vtable_t&, _Tag, void*, _As...>)
+ auto query(_Tag, _As&&... __as) const //
+ noexcept(__nothrow_callable<const __vtable_t&, _Tag, void*, _As...>)
-> __call_result_t<const __vtable_t&, _Tag, void*, _As...>
{
- return (*__self.__vtable_)(_Tag{}, __self.__rcvr_,
- static_cast<_As&&>(__as)...);
+ return (*__vtable_)(_Tag{}, __rcvr_, static_cast<_As&&>(__as)...);
}
} __env_;
@@ -926,20 +916,34 @@
&__rcvr}
{}
- template <__completion_tag _Tag, __decays_to<__ref> _Self, class... _As>
- requires __one_of<_Tag(_As...), _Sigs...>
- friend void tag_invoke(_Tag, _Self&& __self, _As&&... __as) noexcept
+ template <class... _As>
+ requires __one_of<set_value_t(_As...), _Sigs...>
+ void set_value(_As&&... __as) noexcept
{
- (*static_cast<const __rcvr_vfun<_Tag(_As...)>*>(__self.__env_.__vtable_)
- ->__fn_)(static_cast<_Self&&>(__self).__env_.__rcvr_,
- static_cast<_As&&>(__as)...);
+ const __any_::__rcvr_vfun<set_value_t(_As...)>* __vfun =
+ __env_.__vtable_;
+ (*__vfun->__complete_)(__env_.__rcvr_, static_cast<_As&&>(__as)...);
}
- template <std::same_as<__ref> Self>
- friend auto tag_invoke(get_env_t, const Self& __self) noexcept
- -> const __env_t&
+ template <class _Error>
+ requires __one_of<set_error_t(_Error), _Sigs...>
+ void set_error(_Error&& __err) noexcept
{
- return __self.__env_;
+ const __any_::__rcvr_vfun<set_error_t(_Error)>* __vfun =
+ __env_.__vtable_;
+ (*__vfun->__complete_)(__env_.__rcvr_, static_cast<_Error&&>(__err));
+ }
+
+ void set_stopped() noexcept
+ requires __one_of<set_stopped_t(), _Sigs...>
+ {
+ const __any_::__rcvr_vfun<set_stopped_t()>* __vfun = __env_.__vtable_;
+ (*__vfun->__complete_)(__env_.__rcvr_);
+ }
+
+ auto get_env() const noexcept -> const __env_t&
+ {
+ return __env_;
}
};
} // namespace __rec
@@ -951,15 +955,16 @@
private:
template <class _Op>
- friend auto tag_invoke(__create_vtable_t, __mtype<__operation_vtable>,
- __mtype<_Op>) noexcept -> const __operation_vtable*
+ STDEXEC_MEMFN_DECL(auto __create_vtable)
+ (this __mtype<__operation_vtable>, __mtype<_Op>) noexcept
+ -> const __operation_vtable*
{
static __operation_vtable __vtable{
[](void* __object_pointer) noexcept -> void {
STDEXEC_ASSERT(__object_pointer);
_Op& __op = *static_cast<_Op*>(__object_pointer);
static_assert(operation_state<_Op>);
- start(__op);
+ stdexec::start(__op);
}};
return &__vtable;
}
@@ -973,7 +978,7 @@
struct __on_stop_t
{
- stdexec::in_place_stop_source& __source_;
+ stdexec::inplace_stop_source& __source_;
void operator()() const noexcept
{
@@ -986,7 +991,7 @@
{
STDEXEC_ATTRIBUTE((no_unique_address))
_Receiver __rcvr_;
- stdexec::in_place_stop_source __stop_source_{};
+ stdexec::inplace_stop_source __stop_source_{};
using __stop_callback = typename stdexec::stop_token_of_t<
stdexec::env_of_t<_Receiver>>::template callback_type<__on_stop_t>;
std::optional<__stop_callback> __on_stop_{};
@@ -994,7 +999,7 @@
template <class _Env>
using __env_t =
- __env::__join_t<__env::__with<in_place_stop_token, get_stop_token_t>, _Env>;
+ __env::__join_t<prop<get_stop_token_t, inplace_stop_token>, _Env>;
template <class _ReceiverId>
struct __stoppable_receiver
@@ -1006,52 +1011,46 @@
using receiver_concept = stdexec::receiver_t;
__operation_base<_Receiver>* __op_;
- template <same_as<set_next_t> _SetNext, same_as<__t> _Self, class _Item>
- requires __callable<_SetNext, _Receiver&, _Item>
- friend auto tag_invoke(_SetNext, _Self& __self, _Item&& __item)
+ template <same_as<__t> _Self, class _Item>
+ requires __callable<set_next_t, _Receiver&, _Item>
+ STDEXEC_MEMFN_DECL(auto set_next)(this _Self& __self,
+ _Item&& __item) noexcept
+ -> __call_result_t<set_next_t, _Receiver&, _Item>
{
- return _SetNext{}(__self.__op_->__rcvr_,
- static_cast<_Item&&>(__item));
+ return exec::set_next(__self.__op_->__rcvr_,
+ static_cast<_Item&&>(__item));
}
- template <same_as<set_value_t> _SetValue, same_as<__t> _Self,
- class... _Args>
- requires __callable<_SetValue, _Receiver&&, _Args...>
- friend void tag_invoke(_SetValue, _Self&& __self,
- _Args&&... __args) noexcept
+ template <class... _Args>
+ requires __callable<set_value_t, _Receiver, _Args...>
+ void set_value(_Args&&... __args) noexcept
{
- __self.__op_->__on_stop_.reset();
- _SetValue{}(static_cast<_Receiver&&>(__self.__op_->__rcvr_),
- static_cast<_Args&&>(__args)...);
+ __op_->__on_stop_.reset();
+ stdexec::set_value(static_cast<_Receiver&&>(__op_->__rcvr_),
+ static_cast<_Args&&>(__args)...);
}
- template <same_as<set_error_t> _SetError, same_as<__t> _Self,
- class _Error>
- requires __callable<_SetError, _Receiver&&, _Error>
- friend void tag_invoke(_SetError, _Self&& __self,
- _Error&& __err) noexcept
+ template <class _Error>
+ requires __callable<set_error_t, _Receiver, _Error>
+ void set_error(_Error&& __err) noexcept
{
- __self.__op_->__on_stop_.reset();
- _SetError{}(static_cast<_Receiver&&>(__self.__op_->__rcvr_),
- static_cast<_Error&&>(__err));
+ __op_->__on_stop_.reset();
+ stdexec::set_error(static_cast<_Receiver&&>(__op_->__rcvr_),
+ static_cast<_Error&&>(__err));
}
- template <same_as<set_stopped_t> _SetStopped, same_as<__t> _Self>
- requires __callable<_SetStopped, _Receiver&&>
- friend void tag_invoke(_SetStopped, _Self&& __self) noexcept
+ void set_stopped() noexcept
+ requires __callable<set_stopped_t, _Receiver>
{
- __self.__op_->__on_stop_.reset();
- _SetStopped{}(static_cast<_Receiver&&>(__self.__op_->__rcvr_));
+ __op_->__on_stop_.reset();
+ stdexec::set_stopped(static_cast<_Receiver&&>(__op_->__rcvr_));
}
- template <same_as<get_env_t> _GetEnv, same_as<__t> _Self>
- friend auto tag_invoke(_GetEnv, const _Self& __self) noexcept
- -> __env_t<env_of_t<_Receiver>>
+ auto get_env() const noexcept -> __env_t<env_of_t<_Receiver>>
{
return __env::__join(
- __env::__with(__self.__op_->__stop_source_.get_token(),
- get_stop_token),
- get_env(__self.__op_->__rcvr_));
+ prop{get_stop_token, __op_->__stop_source_.get_token()},
+ stdexec::get_env(__op_->__rcvr_));
}
};
};
@@ -1075,19 +1074,19 @@
__rec_{this}, __storage_{__sender.__connect(__rec_)}
{}
+ void start() & noexcept
+ {
+ this->__on_stop_.emplace(
+ stdexec::get_stop_token(stdexec::get_env(this->__rcvr_)),
+ __on_stop_t{this->__stop_source_});
+ STDEXEC_ASSERT(__storage_.__get_vtable()->__start_);
+ __storage_.__get_vtable()->__start_(
+ __storage_.__get_object_pointer());
+ }
+
private:
__stoppable_receiver_t<_ReceiverId> __rec_;
__immovable_operation_storage __storage_{};
-
- friend void tag_invoke(start_t, __t& __self) noexcept
- {
- __self.__on_stop_.emplace(
- stdexec::get_stop_token(stdexec::get_env(__self.__rcvr_)),
- __on_stop_t{__self.__stop_source_});
- STDEXEC_ASSERT(__self.__storage_.__get_vtable()->__start_);
- __self.__storage_.__get_vtable()->__start_(
- __self.__storage_.__get_object_pointer());
- }
};
};
@@ -1107,38 +1106,43 @@
__storage_{__sender.__connect(__rec_)}
{}
+ void start() & noexcept
+ {
+ STDEXEC_ASSERT(__storage_.__get_vtable()->__start_);
+ __storage_.__get_vtable()->__start_(
+ __storage_.__get_object_pointer());
+ }
+
private:
STDEXEC_ATTRIBUTE((no_unique_address))
_Receiver __rec_;
__immovable_operation_storage __storage_{};
-
- friend void tag_invoke(start_t, __t& __self) noexcept
- {
- STDEXEC_ASSERT(__self.__storage_.__get_vtable()->__start_);
- __self.__storage_.__get_vtable()->__start_(
- __self.__storage_.__get_object_pointer());
- }
};
};
-template <class _Queries>
+template <class _Queries, bool _IsEnvProvider = true>
class __query_vtable;
-template <template <class...> class _List, typename... _Queries>
-class __query_vtable<_List<_Queries...>> : public __query_vfun<_Queries>...
+template <template <class...> class _List, class... _Queries,
+ bool _IsEnvProvider>
+class __query_vtable<_List<_Queries...>, _IsEnvProvider> :
+ public __query_vfun<_Queries>...
{
public:
using __query_vfun<_Queries>::operator()...;
private:
- template <class _EnvProvider>
- requires(__callable<__query_vfun_fn<_EnvProvider>, _Queries> && ...)
- friend auto tag_invoke(__create_vtable_t, __mtype<__query_vtable>,
- __mtype<_EnvProvider>) noexcept
+ template <class _Queryable>
+ requires(
+ __callable<__query_vfun_fn<_Queryable, _IsEnvProvider>, _Queries> &&
+ ...)
+ STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__query_vtable>,
+ __mtype<_Queryable>) noexcept
-> const __query_vtable*
{
- static const __query_vtable __vtable{{__query_vfun_fn<_EnvProvider>{}(
- static_cast<_Queries>(nullptr))}...};
+ static const __query_vtable __vtable{
+ {__query_vfun_fn<_Queryable, _IsEnvProvider>{}(
+ static_cast<_Queries>(nullptr))}...};
return &__vtable;
}
};
@@ -1148,7 +1152,7 @@
struct __sender
{
using __receiver_ref_t = __receiver_ref<_Sigs, _ReceiverQueries>;
- static constexpr bool __with_in_place_stop_token =
+ static constexpr bool __with_inplace_stop_token =
__v<__mapply<__mall_of<__q<__is_not_stop_token_query_v>>,
_ReceiverQueries>>;
@@ -1166,8 +1170,8 @@
private:
template <sender_to<__receiver_ref_t> _Sender>
- friend auto tag_invoke(__create_vtable_t, __mtype<__vtable>,
- __mtype<_Sender>) noexcept -> const __vtable*
+ STDEXEC_MEMFN_DECL(auto __create_vtable)
+ (this __mtype<__vtable>, __mtype<_Sender>) noexcept -> const __vtable*
{
static const __vtable __vtable_{
{*__create_vtable(__mtype<__query_vtable<_SenderQueries>>{},
@@ -1178,7 +1182,7 @@
using __op_state_t =
connect_result_t<_Sender, __receiver_ref_t>;
return __immovable_operation_storage{
- std::in_place_type<__op_state_t>, __conv{[&] {
+ std::in_place_type<__op_state_t>, __emplace_from{[&] {
return stdexec::connect(
static_cast<_Sender&&>(__sender),
static_cast<__receiver_ref_t&&>(__receiver));
@@ -1188,35 +1192,27 @@
}
};
- class __env_t
+ struct __env_t
{
- public:
- __env_t(const __vtable* __vtable, void* __sender) noexcept :
- __vtable_{__vtable}, __sender_{__sender}
- {}
-
- private:
const __vtable* __vtable_;
void* __sender_;
template <class _Tag, class... _As>
requires __callable<const __query_vtable<_SenderQueries>&, _Tag,
void*, _As...>
- friend auto
- tag_invoke(_Tag, const __env_t& __self, _As&&... __as) noexcept(
- __nothrow_callable<const __query_vtable<_SenderQueries>&, _Tag,
- void*, _As...>)
+ auto query(_Tag, _As&&... __as) const //
+ noexcept(__nothrow_callable<const __query_vtable<_SenderQueries>&,
+ _Tag, void*, _As...>)
-> __call_result_t<const __query_vtable<_SenderQueries>&, _Tag,
void*, _As...>
{
- return __self.__vtable_->__queries()(_Tag{}, __self.__sender_,
- static_cast<_As&&>(__as)...);
+ return __vtable_->__queries()(_Tag{}, __sender_,
+ static_cast<_As&&>(__as)...);
}
};
- class __t
+ struct __t
{
- public:
using __id = __sender;
using completion_signatures = _Sigs;
using sender_concept = stdexec::sender_t;
@@ -1245,62 +1241,102 @@
return __get_object_pointer(__storage_) != nullptr;
}
- private:
- __unique_storage_t<__vtable> __storage_;
+ auto get_env() const noexcept -> __env_t
+ {
+ return {__storage_.__get_vtable(),
+ __storage_.__get_object_pointer()};
+ }
template <receiver_of<_Sigs> _Rcvr>
- friend auto tag_invoke(connect_t, __t&& __self, _Rcvr&& __rcvr)
- -> stdexec::__t<__operation<stdexec::__id<__decay_t<_Rcvr>>,
- __with_in_place_stop_token>>
+ auto connect(_Rcvr __rcvr) && //
+ -> stdexec::__t<
+ __operation<stdexec::__id<_Rcvr>, __with_inplace_stop_token>>
{
- return {static_cast<__t&&>(__self), static_cast<_Rcvr&&>(__rcvr)};
+ return {static_cast<__t&&>(*this), static_cast<_Rcvr&&>(__rcvr)};
}
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept -> __env_t
- {
- return {__self.__storage_.__get_vtable(),
- __self.__storage_.__get_object_pointer()};
- }
+ private:
+ __unique_storage_t<__vtable> __storage_;
};
};
template <class _ScheduleSender, class _SchedulerQueries = __types<>>
class __scheduler
{
+ static constexpr std::size_t __buffer_size = 4 * sizeof(void*);
+ template <class _Ty>
+ static constexpr bool __is_small = sizeof(_Ty) <= __buffer_size &&
+ alignof(_Ty) <=
+ alignof(std::max_align_t);
+
public:
template <class _Scheduler>
requires(!__decays_to<_Scheduler, __scheduler>) && scheduler<_Scheduler>
__scheduler(_Scheduler&& __scheduler) :
__storage_{static_cast<_Scheduler&&>(__scheduler)}
- {}
+ {
+ static_assert(
+ __is_small<_Scheduler>,
+ "any_scheduler<> must have a nothrow copy constructor, so the scheduler object must be "
+ "small enough to be stored in the internal buffer to avoid dynamic allocation.");
+ }
+
+ __scheduler(__scheduler&&) noexcept = default;
+ __scheduler(const __scheduler&) noexcept = default;
+ __scheduler& operator=(__scheduler&&) noexcept = default;
+ __scheduler& operator=(const __scheduler&) noexcept = default;
using __sender_t = _ScheduleSender;
+ auto schedule() const noexcept -> __sender_t
+ {
+ STDEXEC_ASSERT(__storage_.__get_vtable()->__schedule_);
+ return __storage_.__get_vtable()->__schedule_(
+ __storage_.__get_object_pointer());
+ }
+
+ template <class _Tag, class... _As>
+ requires __callable<const __query_vtable<_SchedulerQueries, false>&,
+ _Tag, void*, _As...>
+ auto query(_Tag, _As&&... __as) const //
+ noexcept(
+ __nothrow_callable<const __query_vtable<_SchedulerQueries, false>&,
+ _Tag, void*, _As...>)
+ -> __call_result_t<const __query_vtable<_SchedulerQueries, false>&,
+ _Tag, void*, _As...>
+ {
+ return __storage_.__get_vtable()->__queries()(
+ _Tag{}, __storage_.__get_object_pointer(),
+ static_cast<_As&&>(__as)...);
+ }
+
private:
- class __vtable : public __query_vtable<_SchedulerQueries>
+ class __vtable : public __query_vtable<_SchedulerQueries, false>
{
public:
__sender_t (*__schedule_)(void*) noexcept;
bool (*__equal_to_)(const void*, const void* other) noexcept;
auto __queries() const noexcept
- -> const __query_vtable<_SchedulerQueries>&
+ -> const __query_vtable<_SchedulerQueries, false>&
{
return *this;
}
private:
template <scheduler _Scheduler>
- friend auto tag_invoke(__create_vtable_t, __mtype<__vtable>,
- __mtype<_Scheduler>) noexcept -> const __vtable*
+ STDEXEC_MEMFN_DECL(auto __create_vtable)
+ (this __mtype<__vtable>, __mtype<_Scheduler>) noexcept
+ -> const __vtable*
{
static const __vtable __vtable_{
- {*__create_vtable(__mtype<__query_vtable<_SchedulerQueries>>{},
- __mtype<_Scheduler>{})},
+ {*__create_vtable(
+ __mtype<__query_vtable<_SchedulerQueries, false>>{},
+ __mtype<_Scheduler>{})},
[](void* __object_pointer) noexcept -> __sender_t {
const _Scheduler& __scheduler =
*static_cast<const _Scheduler*>(__object_pointer);
- return __sender_t{schedule(__scheduler)};
+ return __sender_t{stdexec::schedule(__scheduler)};
},
[](const void* __self, const void* __other) noexcept -> bool {
static_assert(noexcept(__declval<const _Scheduler&>() ==
@@ -1316,29 +1352,6 @@
}
};
- template <same_as<__scheduler> _Self>
- friend auto tag_invoke(schedule_t, const _Self& __self) noexcept
- -> __sender_t
- {
- STDEXEC_ASSERT(__self.__storage_.__get_vtable()->__schedule_);
- return __self.__storage_.__get_vtable()->__schedule_(
- __self.__storage_.__get_object_pointer());
- }
-
- template <class _Tag, same_as<__scheduler> _Self, class... _As>
- requires __callable<const __query_vtable<_SchedulerQueries>&, _Tag,
- void*, _As...>
- friend auto tag_invoke(_Tag, const _Self& __self, _As&&... __as) noexcept(
- __nothrow_callable<const __query_vtable<_SchedulerQueries>&, _Tag,
- void*, _As...>)
- -> __call_result_t<const __query_vtable<_SchedulerQueries>&, _Tag,
- void*, _As...>
- {
- return __self.__storage_.__get_vtable()->__queries()(
- _Tag{}, __self.__storage_.__get_object_pointer(),
- static_cast<_As&&>(__as)...);
- }
-
friend auto operator==(const __scheduler& __self,
const __scheduler& __other) noexcept -> bool
{
@@ -1347,6 +1360,7 @@
{
return false;
}
+
void* __p = __self.__storage_.__get_object_pointer();
void* __o = __other.__storage_.__get_object_pointer();
// if both object pointers are not null, use the virtual equal_to
@@ -1363,7 +1377,7 @@
return !(__self == __other);
}
- __copyable_storage_t<__vtable> __storage_{};
+ __copyable_storage_t<__vtable, __buffer_size> __storage_{};
};
} // namespace __any
@@ -1378,19 +1392,6 @@
using __env_t = stdexec::env_of_t<__receiver_base>;
__receiver_base __receiver_;
- template <class _Tag, stdexec::__decays_to<any_receiver_ref> Self,
- class... _As>
- requires stdexec::tag_invocable<
- _Tag, stdexec::__copy_cvref_t<Self, __receiver_base>, _As...>
- friend auto tag_invoke(_Tag, Self&& __self, _As&&... __as) noexcept(
- std::is_nothrow_invocable_v<
- _Tag, stdexec::__copy_cvref_t<Self, __receiver_base>, _As...>)
- {
- return stdexec::tag_invoke(_Tag{},
- static_cast<Self&&>(__self).__receiver_,
- static_cast<_As&&>(__as)...);
- }
-
public:
using receiver_concept = stdexec::receiver_t;
using __t = any_receiver_ref;
@@ -1400,11 +1401,44 @@
__env_t, const __env_t>
_Receiver>
requires stdexec::receiver_of<_Receiver, _Completions>
- any_receiver_ref(_Receiver& __receiver) noexcept(
- stdexec::__nothrow_constructible_from<__receiver_base, _Receiver>) :
+ any_receiver_ref(_Receiver& __receiver) //
+ noexcept(
+ stdexec::__nothrow_constructible_from<__receiver_base, _Receiver>) :
__receiver_(__receiver)
{}
+ template <class... _As>
+ requires stdexec::tag_invocable<stdexec::set_value_t, __receiver_base,
+ _As...>
+ void set_value(_As&&... __as) noexcept
+ {
+ stdexec::tag_invoke(stdexec::set_value,
+ static_cast<__receiver_base&&>(__receiver_),
+ static_cast<_As&&>(__as)...);
+ }
+
+ template <class _Error>
+ requires stdexec::tag_invocable<stdexec::set_error_t, __receiver_base,
+ _Error>
+ void set_error(_Error&& __err) noexcept
+ {
+ stdexec::tag_invoke(stdexec::set_error,
+ static_cast<__receiver_base&&>(__receiver_),
+ static_cast<_Error&&>(__err));
+ }
+
+ void set_stopped() noexcept
+ requires stdexec::tag_invocable<stdexec::set_stopped_t, __receiver_base>
+ {
+ stdexec::tag_invoke(stdexec::set_stopped,
+ static_cast<__receiver_base&&>(__receiver_));
+ }
+
+ auto get_env() const noexcept -> stdexec::env_of_t<__receiver_base>
+ {
+ return stdexec::get_env(__receiver_);
+ }
+
template <auto... _SenderQueries>
class any_sender
{
@@ -1417,9 +1451,10 @@
class... _As>
requires stdexec::tag_invocable<
_Tag, stdexec::__copy_cvref_t<Self, __sender_base>, _As...>
- friend auto tag_invoke(_Tag, Self&& __self, _As&&... __as) noexcept(
- std::is_nothrow_invocable_v<
- _Tag, stdexec::__copy_cvref_t<Self, __sender_base>, _As...>)
+ friend auto tag_invoke(_Tag, Self&& __self, _As&&... __as) //
+ noexcept(
+ stdexec::nothrow_tag_invocable<
+ _Tag, stdexec::__copy_cvref_t<Self, __sender_base>, _As...>)
{
return stdexec::tag_invoke(_Tag{},
static_cast<Self&&>(__self).__sender_,
@@ -1433,16 +1468,25 @@
template <stdexec::__not_decays_to<any_sender> _Sender>
requires stdexec::sender_to<_Sender, __receiver_base>
- any_sender(_Sender&& __sender) noexcept(
- stdexec::__nothrow_constructible_from<__sender_base, _Sender>) :
+ any_sender(_Sender&& __sender) //
+ noexcept(
+ stdexec::__nothrow_constructible_from<__sender_base, _Sender>) :
__sender_(static_cast<_Sender&&>(__sender))
{}
+ template <stdexec::receiver_of<_Completions> _Receiver>
+ auto connect(_Receiver __rcvr) && -> stdexec::connect_result_t<
+ __sender_base, _Receiver>
+ {
+ return static_cast<__sender_base&&>(__sender_).connect(
+ static_cast<_Receiver&&>(__rcvr));
+ }
+
template <auto... _SchedulerQueries>
class any_scheduler
{
using __schedule_completions =
- stdexec::__concat_completion_signatures_t<
+ stdexec::__concat_completion_signatures<
_Completions,
stdexec::completion_signatures<stdexec::set_value_t()>>;
using __schedule_receiver =
@@ -1460,7 +1504,7 @@
};
using schedule_sender_queries = stdexec::__minvoke<
- stdexec::__remove_if<__ret_equals_to<
+ stdexec::__mremove_if<__ret_equals_to<
stdexec::get_completion_scheduler_t<stdexec::set_value_t>>>,
decltype(_SenderQueries)...>;
@@ -1493,31 +1537,31 @@
using __t = any_scheduler;
using __id = any_scheduler;
- template <class _Scheduler>
- requires(!stdexec::__decays_to<_Scheduler, any_scheduler> &&
- stdexec::scheduler<_Scheduler>)
- any_scheduler(_Scheduler&& __scheduler) :
+ template <stdexec::__none_of<any_scheduler> _Scheduler>
+ requires stdexec::scheduler<_Scheduler>
+ any_scheduler(_Scheduler __scheduler) :
__scheduler_{static_cast<_Scheduler&&>(__scheduler)}
{}
- private:
- template <class _Tag, stdexec::__decays_to<any_scheduler> Self,
- class... _As>
- requires stdexec::tag_invocable<
- _Tag, stdexec::__copy_cvref_t<Self, __scheduler_base>,
- _As...>
- friend auto tag_invoke(_Tag, Self&& __self, _As&&... __as) noexcept(
- std::is_nothrow_invocable_v<
- _Tag, stdexec::__copy_cvref_t<Self, __scheduler_base>,
- _As...>)
+ auto schedule() const noexcept -> __schedule_sender
{
- return stdexec::tag_invoke(
- _Tag{}, static_cast<Self&&>(__self).__scheduler_,
- static_cast<_As&&>(__as)...);
+ return __scheduler_.schedule();
}
- friend auto operator==(const any_scheduler& __self,
- const any_scheduler& __other) noexcept
+ template <class _Tag, class... _As>
+ requires stdexec::tag_invocable<_Tag, const __scheduler_base&,
+ _As...>
+ auto query(_Tag, _As&&... __as) const noexcept(
+ stdexec::nothrow_tag_invocable<_Tag, const __scheduler_base&,
+ _As...>)
+ -> stdexec::tag_invoke_result_t<_Tag, const __scheduler_base&,
+ _As...>
+ {
+ return stdexec::tag_invoke(_Tag(), __scheduler_,
+ static_cast<_As&&>(__as)...);
+ }
+
+ auto operator==(const any_scheduler&) const noexcept
-> bool = default;
};
};
diff --git a/include/sdbusplus/async/stdexec/async_scope.hpp b/include/sdbusplus/async/stdexec/async_scope.hpp
index 63f38d6..d48ef41 100644
--- a/include/sdbusplus/async/stdexec/async_scope.hpp
+++ b/include/sdbusplus/async/stdexec/async_scope.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
@@ -33,6 +33,13 @@
struct __impl;
struct async_scope;
+template <class _A>
+concept __async_scope = requires(_A& __a) {
+ {
+ __a.nest(stdexec::just())
+ } -> sender_of<stdexec::set_value_t()>;
+ };
+
struct __task : __immovable
{
const __impl* __scope_;
@@ -42,11 +49,11 @@
template <class _BaseEnv>
using __env_t =
- make_env_t<_BaseEnv, with_t<get_stop_token_t, in_place_stop_token>>;
+ make_env_t<_BaseEnv, prop<get_stop_token_t, inplace_stop_token>>;
struct __impl
{
- in_place_stop_source __stop_source_{};
+ inplace_stop_source __stop_source_{};
mutable std::mutex __lock_{};
mutable std::ptrdiff_t __active_ = 0;
mutable __intrusive_queue<&__task::__next_> __waiters_{};
@@ -78,13 +85,7 @@
static_cast<_Receiver&&>(__rcvr)))
{}
- private:
- static void __notify_waiter(__task* __self) noexcept
- {
- start(static_cast<__t*>(__self)->__op_);
- }
-
- void __start_() noexcept
+ void start() & noexcept
{
std::unique_lock __guard{this->__scope_->__lock_};
auto& __active = this->__scope_->__active_;
@@ -95,12 +96,13 @@
return;
}
__guard.unlock();
- start(this->__op_);
+ stdexec::start(this->__op_);
}
- friend void tag_invoke(start_t, __t& __self) noexcept
+ private:
+ static void __notify_waiter(__task* __self) noexcept
{
- return __self.__start_();
+ stdexec::start(static_cast<__t*>(__self)->__op_);
}
STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
@@ -125,8 +127,7 @@
template <__decays_to<__t> _Self, receiver _Receiver>
requires sender_to<__copy_cvref_t<_Self, _Constrained>, _Receiver>
- [[nodiscard]] friend auto tag_invoke(connect_t, _Self&& __self,
- _Receiver __rcvr)
+ [[nodiscard]] static auto connect(_Self&& __self, _Receiver __rcvr) //
-> __when_empty_op_t<_Self, _Receiver>
{
return __when_empty_op_t<_Self, _Receiver>{
@@ -134,15 +135,10 @@
static_cast<_Receiver&&>(__rcvr)};
}
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> completion_signatures_of_t<__copy_cvref_t<_Self, _Constrained>,
- __env_t<_Env>>
- {
- return {};
- }
-
- friend auto tag_invoke(get_env_t, const __t&) noexcept -> empty_env
+ template <__decays_to<__t> _Self, class... _Env>
+ static auto get_completion_signatures(_Self&&, _Env&&...)
+ -> __completion_signatures_of_t<__copy_cvref_t<_Self, _Constrained>,
+ __env_t<_Env>...>
{
return {};
}
@@ -198,25 +194,46 @@
}
}
- template <__completion_tag _Tag, class... _As>
- requires __callable<_Tag, _Receiver, _As...>
- friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept
+ template <class... _As>
+ requires __callable<set_value_t, _Receiver, _As...>
+ void set_value(_As&&... __as) noexcept
{
- auto __scope = __self.__op_->__scope_;
- _Tag{}(std::move(__self.__op_->__rcvr_),
- static_cast<_As&&>(__as)...);
+ auto __scope = __op_->__scope_;
+ stdexec::set_value(std::move(__op_->__rcvr_),
+ static_cast<_As&&>(__as)...);
// do not access __op_
// do not access this
__complete(__scope);
}
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> __env_t<env_of_t<_Receiver>>
+ template <class _Error>
+ requires __callable<set_error_t, _Receiver, _Error>
+ void set_error(_Error&& __err) noexcept
+ {
+ auto __scope = __op_->__scope_;
+ stdexec::set_error(std::move(__op_->__rcvr_),
+ static_cast<_Error&&>(__err));
+ // do not access __op_
+ // do not access this
+ __complete(__scope);
+ }
+
+ void set_stopped() noexcept
+ requires __callable<set_stopped_t, _Receiver>
+ {
+ auto __scope = __op_->__scope_;
+ stdexec::set_stopped(std::move(__op_->__rcvr_));
+ // do not access __op_
+ // do not access this
+ __complete(__scope);
+ }
+
+ auto get_env() const noexcept -> __env_t<env_of_t<_Receiver>>
{
return make_env(
- get_env(__self.__op_->__rcvr_),
- with(get_stop_token,
- __self.__op_->__scope_->__stop_source_.get_token()));
+ stdexec::get_env(__op_->__rcvr_),
+ stdexec::prop{get_stop_token,
+ __op_->__scope_->__stop_source_.get_token()});
}
};
};
@@ -243,20 +260,14 @@
__nest_rcvr_t{this}))
{}
- private:
- void __start_() noexcept
+ void start() & noexcept
{
STDEXEC_ASSERT(this->__scope_);
std::unique_lock __guard{this->__scope_->__lock_};
auto& __active = this->__scope_->__active_;
++__active;
__guard.unlock();
- start(__op_);
- }
-
- friend void tag_invoke(start_t, __t& __self) noexcept
- {
- return __self.__start_();
+ stdexec::start(__op_);
}
};
};
@@ -278,6 +289,7 @@
template <class _Receiver>
using __nest_operation_t =
stdexec::__t<__nest_op<_ConstrainedId, stdexec::__id<_Receiver>>>;
+
template <class _Receiver>
using __nest_receiver_t =
stdexec::__t<__nest_rcvr<stdexec::__id<_Receiver>>>;
@@ -285,8 +297,7 @@
template <__decays_to<__t> _Self, receiver _Receiver>
requires sender_to<__copy_cvref_t<_Self, _Constrained>,
__nest_receiver_t<_Receiver>>
- [[nodiscard]] friend auto tag_invoke(connect_t, _Self&& __self,
- _Receiver __rcvr)
+ [[nodiscard]] static auto connect(_Self&& __self, _Receiver __rcvr)
-> __nest_operation_t<_Receiver>
{
return __nest_operation_t<_Receiver>{
@@ -294,15 +305,10 @@
static_cast<_Receiver&&>(__rcvr)};
}
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> completion_signatures_of_t<__copy_cvref_t<_Self, _Constrained>,
- __env_t<_Env>>
- {
- return {};
- }
-
- friend auto tag_invoke(get_env_t, const __t&) noexcept -> empty_env
+ template <__decays_to<__t> _Self, class... _Env>
+ static auto get_completion_signatures(_Self&&, _Env&&...)
+ -> __completion_signatures_of_t<__copy_cvref_t<_Self, _Constrained>,
+ __env_t<_Env>...>
{
return {};
}
@@ -329,7 +335,7 @@
struct __forward_stopped
{
- in_place_stop_source* __stop_source_;
+ inplace_stop_source* __stop_source_;
void operator()() noexcept
{
@@ -361,11 +367,6 @@
using __forward_consumer = typename stop_token_of_t<
env_of_t<_Receiver>>::template callback_type<__forward_stopped>;
- friend void tag_invoke(start_t, __t& __self) noexcept
- {
- __self.__start_();
- }
-
void __complete_() noexcept
{
try
@@ -384,7 +385,7 @@
else if (get_stop_token(get_env(__rcvr_)).stop_requested())
{
__guard.unlock();
- set_stopped(static_cast<_Receiver&&>(__rcvr_));
+ stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr_));
__guard.lock();
}
else
@@ -413,33 +414,8 @@
}
catch (...)
{
- set_error(static_cast<_Receiver&&>(__rcvr_),
- std::current_exception());
- }
- }
-
- void __start_() noexcept
- {
- try
- {
- if (!!__state_)
- {
- std::unique_lock __guard{__state_->__mutex_};
- if (__state_->__data_.index() != 0)
- {
- __guard.unlock();
- __complete_();
- }
- else
- {
- __state_->__subscribers_.push_back(this);
- }
- }
- }
- catch (...)
- {
- set_error(static_cast<_Receiver&&>(__rcvr_),
- std::current_exception());
+ stdexec::set_error(static_cast<_Receiver&&>(__rcvr_),
+ std::current_exception());
}
}
@@ -482,6 +458,31 @@
__forward_consumer_(get_stop_token(get_env(__rcvr_)),
__forward_stopped{&__state_->__stop_source_})
{}
+
+ void start() & noexcept
+ {
+ try
+ {
+ if (!!__state_)
+ {
+ std::unique_lock __guard{__state_->__mutex_};
+ if (__state_->__data_.index() != 0)
+ {
+ __guard.unlock();
+ __complete_();
+ }
+ else
+ {
+ __state_->__subscribers_.push_back(this);
+ }
+ }
+ }
+ catch (...)
+ {
+ stdexec::set_error(static_cast<_Receiver&&>(__rcvr_),
+ std::current_exception());
+ }
+ }
};
};
@@ -490,7 +491,7 @@
struct __completion_as_tuple2_;
template <class _Tag, class... _Ts>
-struct __completion_as_tuple2_<_Tag(_Ts&&...)>
+struct __completion_as_tuple2_<_Tag(_Ts...)>
{
using __t = std::tuple<_Tag, _Ts...>;
};
@@ -500,31 +501,30 @@
#else
template <class _Tag, class... _Ts>
-auto __completion_as_tuple_(_Tag (*)(_Ts&&...)) -> std::tuple<_Tag, _Ts...>;
+auto __completion_as_tuple_(_Tag (*)(_Ts...)) -> std::tuple<_Tag, _Ts...>;
+
template <class _Fn>
using __completion_as_tuple_t =
decltype(__scope::__completion_as_tuple_(static_cast<_Fn*>(nullptr)));
#endif
template <class... _Ts>
-using __decay_values_t =
- completion_signatures<set_value_t(__decay_t<_Ts>&&...)>;
+using __decay_values_t = completion_signatures<set_value_t(__decay_t<_Ts>...)>;
template <class _Ty>
-using __decay_error_t = completion_signatures<set_error_t(__decay_t<_Ty>&&)>;
+using __decay_error_t = completion_signatures<set_error_t(__decay_t<_Ty>)>;
template <class _Sender, class _Env>
using __future_completions_t = //
- make_completion_signatures<
+ transform_completion_signatures_of<
_Sender, __env_t<_Env>,
- completion_signatures<set_stopped_t(),
- set_error_t(std::exception_ptr&&)>,
+ completion_signatures<set_stopped_t(), set_error_t(std::exception_ptr)>,
__decay_values_t, __decay_error_t>;
template <class _Completions>
using __completions_as_variant = //
- __mapply<__transform<__q<__completion_as_tuple_t>,
- __mbind_front_q<std::variant, std::monostate>>,
+ __mapply<__mtransform<__q<__completion_as_tuple_t>,
+ __mbind_front_q<std::variant, std::monostate>>,
_Completions>;
template <class _Ty>
@@ -560,9 +560,9 @@
__future_state_base(_Env __env, const __impl* __scope) :
__forward_scope_{std::in_place, __scope->__stop_source_.get_token(),
__forward_stopped{&__stop_source_}},
- __env_(
- make_env(static_cast<_Env&&>(__env),
- with(get_stop_token, __scope->__stop_source_.get_token())))
+ __env_(make_env(
+ static_cast<_Env&&>(__env),
+ stdexec::prop{get_stop_token, __scope->__stop_source_.get_token()}))
{}
~__future_state_base()
@@ -591,8 +591,8 @@
STDEXEC_ASSERT(actual == __from);
}
- in_place_stop_source __stop_source_;
- std::optional<in_place_stop_callback<__forward_stopped>> __forward_scope_;
+ inplace_stop_source __stop_source_;
+ std::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>>
@@ -638,31 +638,56 @@
}
}
- template <__completion_tag _Tag, __movable_value... _As>
- friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept
+ template <class _Tag, class... _As>
+ bool __save_completion(_Tag, _As&&... __as) noexcept
{
- auto& __state = *__self.__state_;
+ auto& __state = *__state_;
try
{
std::unique_lock __guard{__state.__mutex_};
- using _Tuple = __decayed_tuple<_Tag, _As...>;
+ using _Tuple = __decayed_std_tuple<_Tag, _As...>;
__state.__data_.template emplace<_Tuple>(
- _Tag{}, static_cast<_As&&>(__as)...);
- __guard.unlock();
- __self.__dispatch_result_();
+ _Tag(), static_cast<_As&&>(__as)...);
+ return true;
}
catch (...)
{
using _Tuple = std::tuple<set_error_t, std::exception_ptr>;
__state.__data_.template emplace<_Tuple>(
- set_error_t{}, std::current_exception());
+ 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_();
}
}
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> const __env_t<_Env>&
+ template <__movable_value _Error>
+ void set_error(_Error&& __err) noexcept
{
- return __self.__state_->__env_;
+ if (__save_completion(set_error_t(), static_cast<_Error&&>(__err)))
+ {
+ __dispatch_result_();
+ }
+ }
+
+ void set_stopped() noexcept
+ {
+ if (__save_completion(set_stopped_t()))
+ {
+ __dispatch_result_();
+ }
+ }
+
+ auto get_env() const noexcept -> const __env_t<_Env>&
+ {
+ return __state_->__env_;
}
};
};
@@ -694,8 +719,17 @@
using _Sender = stdexec::__t<_SenderId>;
using _Env = stdexec::__t<_EnvId>;
- struct __t
+ class __t
{
+ template <class _Self>
+ using __completions_t =
+ __future_completions_t<__mfront<_Sender, _Self>, _Env>;
+
+ template <class _Receiver>
+ using __future_op_t = stdexec::__t<
+ __future_op<_SenderId, _EnvId, stdexec::__id<_Receiver>>>;
+
+ public:
using __id = __future;
using sender_concept = stdexec::sender_t;
@@ -720,15 +754,25 @@
}
}
+ template <__decays_to<__t> _Self, receiver _Receiver>
+ requires receiver_of<_Receiver, __completions_t<_Self>>
+ static auto connect(_Self&& __self, _Receiver __rcvr)
+ -> __future_op_t<_Receiver>
+ {
+ return __future_op_t<_Receiver>{
+ static_cast<_Receiver&&>(__rcvr),
+ static_cast<_Self&&>(__self).__state_};
+ }
+
+ template <__decays_to<__t> _Self, class... _OtherEnv>
+ static auto get_completion_signatures(_Self&&, _OtherEnv&&...)
+ -> __completions_t<_Self>
+ {
+ return {};
+ }
+
private:
friend struct async_scope;
- template <class _Self>
- using __completions_t =
- __future_completions_t<__mfront<_Sender, _Self>, _Env>;
-
- template <class _Receiver>
- using __future_op_t = stdexec::__t<
- __future_op<_SenderId, _EnvId, stdexec::__id<_Receiver>>>;
explicit __t(
std::unique_ptr<__future_state<_Sender, _Env>> __state) noexcept :
@@ -739,27 +783,6 @@
__future_step::__future);
}
- template <__decays_to<__t> _Self, receiver _Receiver>
- requires receiver_of<_Receiver, __completions_t<_Self>>
- friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr)
- -> __future_op_t<_Receiver>
- {
- return __future_op_t<_Receiver>{static_cast<_Receiver&&>(__rcvr),
- std::move(__self.__state_)};
- }
-
- template <__decays_to<__t> _Self, class _OtherEnv>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&,
- _OtherEnv&&) -> __completions_t<_Self>
- {
- return {};
- }
-
- friend auto tag_invoke(get_env_t, const __t&) noexcept -> empty_env
- {
- return {};
- }
-
std::unique_ptr<__future_state<_Sender, _Env>> __state_;
};
};
@@ -770,10 +793,23 @@
////////////////////////////////////////////////////////////////////////////
// async_scope::spawn implementation
+struct __spawn_env_
+{
+ inplace_stop_token __token_;
+
+ auto query(get_stop_token_t) const noexcept -> inplace_stop_token
+ {
+ return __token_;
+ }
+
+ auto query(get_scheduler_t) const noexcept -> __inln::__scheduler
+ {
+ return {};
+ }
+};
+
template <class _Env>
-using __spawn_env_t =
- __env::__join_t<_Env, __env::__with<in_place_stop_token, get_stop_token_t>,
- __env::__with<__inln::__scheduler, get_scheduler_t>>;
+using __spawn_env_t = __env::__join_t<_Env, __spawn_env_>;
template <class _EnvId>
struct __spawn_op_base
@@ -794,24 +830,25 @@
using receiver_concept = stdexec::receiver_t;
__spawn_op_base<_EnvId>* __op_;
- template <__one_of<set_value_t, set_stopped_t> _Tag>
- friend void tag_invoke(_Tag, __t&& __self) noexcept
+ void set_value() noexcept
{
- __self.__op_->__delete_(__self.__op_);
+ __op_->__delete_(__op_);
}
// BUGBUG NOT TO SPEC spawn shouldn't accept senders that can fail.
- template <same_as<set_error_t> _Tag>
- [[noreturn]] friend void tag_invoke(_Tag, __t&&,
- std::exception_ptr __eptr) noexcept
+ [[noreturn]] void set_error(std::exception_ptr __eptr) noexcept
{
std::rethrow_exception(std::move(__eptr));
}
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> const __spawn_env_t<_Env>&
+ void set_stopped() noexcept
{
- return __self.__op_->__env_;
+ __op_->__delete_(__op_);
+ }
+
+ auto get_env() const noexcept -> const __spawn_env_t<_Env>&
+ {
+ return __op_->__env_;
}
};
};
@@ -832,9 +869,7 @@
__spawn_op_base<_EnvId>{
__env::__join(
static_cast<_Env&&>(__env),
- __env::__with(__scope->__stop_source_.get_token(),
- get_stop_token),
- __env::__with(__inln::__scheduler{}, get_scheduler)),
+ __spawn_env_{__scope->__stop_source_.get_token()}),
[](__spawn_op_base<_EnvId>* __op) {
delete static_cast<__t*>(__op);
}},
@@ -842,14 +877,9 @@
__spawn_receiver_t<_Env>{this}))
{}
- void __start_() noexcept
+ void start() & noexcept
{
- start(__op_);
- }
-
- friend void tag_invoke(start_t, __t& __self) noexcept
- {
- return __self.__start_();
+ stdexec::start(__op_);
}
connect_result_t<_Sender, __spawn_receiver_t<_Env>> __op_;
@@ -914,12 +944,12 @@
return __future_t<_Sender, _Env>{std::move(__state)};
}
- auto get_stop_source() noexcept -> in_place_stop_source&
+ auto get_stop_source() noexcept -> inplace_stop_source&
{
return __impl_.__stop_source_;
}
- auto get_stop_token() const noexcept -> in_place_stop_token
+ auto get_stop_token() const noexcept -> inplace_stop_token
{
return __impl_.__stop_source_.get_token();
}
@@ -935,4 +965,8 @@
} // namespace __scope
using __scope::async_scope;
+
+template <class _AsyncScope, class _Sender>
+using nest_result_t = decltype(stdexec::__declval<_AsyncScope&>().nest(
+ stdexec::__declval<_Sender&&>()));
} // namespace exec
diff --git a/include/sdbusplus/async/stdexec/at_coroutine_exit.hpp b/include/sdbusplus/async/stdexec/at_coroutine_exit.hpp
index 6bb5318..a9ad470 100644
--- a/include/sdbusplus/async/stdexec/at_coroutine_exit.hpp
+++ b/include/sdbusplus/async/stdexec/at_coroutine_exit.hpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
@@ -48,39 +48,44 @@
using __id = __receiver_id;
_Receiver __receiver_;
- template <__one_of<set_value_t, set_error_t> _Tag,
- __decays_to<__t> _Self, class... _Args>
- requires __callable<_Tag, _Receiver, _Args...>
- friend void tag_invoke(_Tag, _Self&& __self,
- _Args&&... __args) noexcept
+ template <class... _Args>
+ requires __callable<set_value_t, _Receiver, _Args...>
+ void set_value(_Args&&... __args) noexcept
{
- _Tag{}(static_cast<_Receiver&&>(__self.__receiver_),
- static_cast<_Args&&>(__args)...);
+ stdexec::set_value(static_cast<_Receiver&&>(__receiver_),
+ static_cast<_Args&&>(__args)...);
}
- template <same_as<set_stopped_t> _Tag>
- [[noreturn]] friend void tag_invoke(_Tag, __t&&) noexcept
+ template <class _Error>
+ requires __callable<set_error_t, _Receiver, _Error>
+ void set_error(_Error&& __err) noexcept
+ {
+ stdexec::set_error(static_cast<_Receiver&&>(__receiver_),
+ static_cast<_Error&&>(__err));
+ }
+
+ [[noreturn]] void set_stopped() noexcept
{
std::terminate();
}
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> env_of_t<_Receiver>
+ auto get_env() const noexcept -> env_of_t<_Receiver>
{
- return get_env(__self.__receiver_);
+ return stdexec::get_env(__receiver_);
}
};
};
+
template <class _Rec>
using __receiver = __t<__receiver_id<_Rec>>;
template <class _Sender>
struct __sender_id
{
- template <class _Env>
+ template <class... _Env>
using __completion_signatures = //
- __mapply<__remove<set_stopped_t(), __q<completion_signatures>>,
- completion_signatures_of_t<_Sender, _Env>>;
+ __mapply<__mremove<set_stopped_t(), __q<completion_signatures>>,
+ __completion_signatures_of_t<_Sender, _Env...>>;
struct __t
{
@@ -91,26 +96,24 @@
template <receiver _Receiver>
requires sender_to<_Sender, __receiver<_Receiver>>
- friend auto tag_invoke(connect_t, __t&& __self,
- _Receiver&& __rcvr) noexcept
+ auto connect(_Receiver __rcvr) && noexcept
-> connect_result_t<_Sender, __receiver<_Receiver>>
{
return stdexec::connect(
- static_cast<_Sender&&>(__self.__sender_),
+ static_cast<_Sender&&>(__sender_),
__receiver<_Receiver>{static_cast<_Receiver&&>(__rcvr)});
}
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> __completion_signatures<_Env>
+ template <class... _Env>
+ auto get_completion_signatures(_Env&&...)
+ -> __completion_signatures<_Env...>
{
return {};
}
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> env_of_t<_Sender>
+ auto get_env() const noexcept -> env_of_t<_Sender>
{
- return get_env(__self.__sender_);
+ return stdexec::get_env(__sender_);
}
};
};
@@ -207,10 +210,9 @@
{
const __promise& __promise_;
- friend auto tag_invoke(get_scheduler_t, __env __self) noexcept
- -> __any_scheduler
+ auto query(get_scheduler_t) const noexcept -> __any_scheduler
{
- return __self.__promise_.__scheduler_;
+ return __promise_.__scheduler_;
}
};
@@ -257,10 +259,9 @@
__die_on_stop(static_cast<_Awaitable&&>(__awaitable)), *this);
}
- friend auto tag_invoke(get_env_t, const __promise& __self) noexcept
- -> __env
+ auto get_env() const noexcept -> __env
{
- return {__self};
+ return {*this};
}
bool __is_unhandled_stopped_{false};
diff --git a/include/sdbusplus/async/stdexec/commit.info b/include/sdbusplus/async/stdexec/commit.info
index 68da718..e3aca22 100644
--- a/include/sdbusplus/async/stdexec/commit.info
+++ b/include/sdbusplus/async/stdexec/commit.info
@@ -1 +1 @@
-af0efa96811682e26c123175746c408fa582dbb1
+439a29175f45aecdec6b124e75dc3c7bc5d8b778
diff --git a/include/sdbusplus/async/stdexec/concepts.hpp b/include/sdbusplus/async/stdexec/concepts.hpp
index 5963932..3ece504 100644
--- a/include/sdbusplus/async/stdexec/concepts.hpp
+++ b/include/sdbusplus/async/stdexec/concepts.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
@@ -15,271 +15,4 @@
*/
#pragma once
-#if __cpp_concepts < 201907L
-#error This library requires support for C++20 concepts
-#endif
-
#include "__detail/__concepts.hpp"
-#include "__detail/__config.hpp"
-#include "__detail/__meta.hpp"
-#include "__detail/__type_traits.hpp"
-
-#include <version>
-
-// Perhaps the stdlib lacks support for concepts though:
-#if __has_include(<concepts>) && __cpp_lib_concepts >= 202002
-#define STDEXEC_HAS_STD_CONCEPTS_HEADER() 1
-#else
-#define STDEXEC_HAS_STD_CONCEPTS_HEADER() 0
-#endif
-
-#if STDEXEC_HAS_STD_CONCEPTS_HEADER()
-#include <concepts>
-#else
-#include <type_traits>
-#endif
-
-namespace stdexec::__std_concepts
-{
-// Make sure we're using a same_as concept that doesn't instantiate std::is_same
-template <class _Ap, class _Bp>
-concept same_as = __same_as<_Ap, _Bp> && __same_as<_Bp, _Ap>;
-
-#if STDEXEC_HAS_STD_CONCEPTS_HEADER()
-
-using std::convertible_to;
-using std::derived_from;
-using std::equality_comparable;
-using std::integral;
-
-#else
-
-template <class T>
-concept integral = std::is_integral_v<T>;
-
-template <class _Ap, class _Bp>
-concept derived_from = //
- STDEXEC_IS_BASE_OF(_Bp, _Ap) && //
- STDEXEC_IS_CONVERTIBLE_TO(const volatile _Ap*, const volatile _Bp*);
-
-template <class _From, class _To>
-concept convertible_to = //
- STDEXEC_IS_CONVERTIBLE_TO(_From, _To) && //
- requires(_From (&__fun)()) { static_cast<_To>(__fun()); };
-
-template <class _Ty>
-concept equality_comparable = //
- requires(__cref_t<_Ty> __t) {
- {
- __t == __t
- } -> convertible_to<bool>;
- {
- __t != __t
- } -> convertible_to<bool>;
- };
-#endif
-} // namespace stdexec::__std_concepts
-
-namespace stdexec
-{
-using namespace __std_concepts;
-
-// Avoid using libstdc++'s object concepts because they instantiate a
-// lot of templates.
-#if STDEXEC_HAS_BUILTIN(__is_nothrow_destructible)
-template <class _Ty>
-concept destructible = __is_nothrow_destructible(_Ty);
-#else
-template <class _Ty>
-inline constexpr bool __destructible_ = //
- requires {
- {
- (static_cast < _Ty && (*)() noexcept > (nullptr))().~_Ty()
- } noexcept;
- };
-template <class _Ty>
-inline constexpr bool __destructible_<_Ty&> = true;
-template <class _Ty>
-inline constexpr bool __destructible_<_Ty&&> = true;
-template <class _Ty, std::size_t _Np>
-inline constexpr bool __destructible_<_Ty[_Np]> = __destructible_<_Ty>;
-
-template <class T>
-concept destructible = __destructible_<T>;
-#endif
-
-#if STDEXEC_HAS_BUILTIN(__is_constructible)
-template <class _Ty, class... _As>
-concept constructible_from = //
- destructible<_Ty> && //
- __is_constructible(_Ty, _As...);
-#else
-template <class _Ty, class... _As>
-concept constructible_from = //
- destructible<_Ty> && //
- std::is_constructible_v<_Ty, _As...>;
-#endif
-
-template <class _Ty>
-concept default_initializable = //
- constructible_from<_Ty> && //
- requires { _Ty{}; } && //
- requires { ::new _Ty; };
-
-template <class _Ty>
-concept move_constructible = //
- constructible_from<_Ty, _Ty>;
-
-template <class _Ty>
-concept copy_constructible = //
- move_constructible<_Ty> //
- && constructible_from<_Ty, const _Ty&>;
-
-template <class _LHS, class _RHS>
-concept assignable_from = //
- same_as<_LHS, _LHS&> && //
- // std::common_reference_with<
- // const std::remove_reference_t<_LHS>&,
- // const std::remove_reference_t<_RHS>&> &&
- requires(_LHS __lhs, _RHS&& __rhs) {
- {
- __lhs = static_cast<_RHS&&>(__rhs)
- } -> same_as<_LHS>;
- };
-
-namespace __swap
-{
-using std::swap;
-
-template <class _Ty, class _Uy>
-concept swappable_with = //
- requires(_Ty&& __t, _Uy&& __u) { //
- swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u));
- };
-
-inline constexpr const auto __fn = //
- []<class _Ty, swappable_with<_Ty> _Uy>(_Ty&& __t, _Uy&& __u) noexcept(
- noexcept(swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u)))) {
- swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u));
-};
-} // namespace __swap
-
-using __swap::swappable_with;
-inline constexpr const auto& swap = __swap::__fn;
-
-template <class _Ty>
-concept swappable = //
- swappable_with<_Ty, _Ty>;
-
-template <class _Ty>
-concept movable = //
- std::is_object_v<_Ty> && //
- move_constructible<_Ty> && //
- assignable_from<_Ty&, _Ty> && //
- swappable<_Ty>;
-
-template <class _Ty>
-concept copyable = //
- copy_constructible<_Ty> && //
- movable<_Ty> && //
- assignable_from<_Ty&, _Ty&> && //
- assignable_from<_Ty&, const _Ty&> && //
- assignable_from<_Ty&, const _Ty>;
-
-template <class _Ty>
-concept semiregular = //
- copyable<_Ty> && //
- default_initializable<_Ty>;
-
-template <class _Ty>
-concept regular = //
- semiregular<_Ty> && //
- equality_comparable<_Ty>;
-
-// Not exactly right, but close.
-template <class _Ty>
-concept __boolean_testable_ = convertible_to<_Ty, bool>;
-
-template <class T, class U>
-concept __partially_ordered_with = //
- requires(__cref_t<T> t, __cref_t<U> u) {
- {
- t < u
- } -> __boolean_testable_;
- {
- t > u
- } -> __boolean_testable_;
- {
- t <= u
- } -> __boolean_testable_;
- {
- t >= u
- } -> __boolean_testable_;
- {
- u < t
- } -> __boolean_testable_;
- {
- u > t
- } -> __boolean_testable_;
- {
- u <= t
- } -> __boolean_testable_;
- {
- u >= t
- } -> __boolean_testable_;
- };
-
-template <class _Ty>
-concept totally_ordered = //
- equality_comparable<_Ty> && //
- __partially_ordered_with<_Ty, _Ty>;
-
-template <class _Ty>
-concept __movable_value = //
- move_constructible<__decay_t<_Ty>> && //
- constructible_from<__decay_t<_Ty>, _Ty>;
-
-template <class _Ty>
-concept __nothrow_movable_value = //
- __movable_value<_Ty> && //
- requires(_Ty&& __t) {
- {
- __decay_t<_Ty>{__decay_t<_Ty>{static_cast<_Ty&&>(__t)}}
- } noexcept;
- };
-
-#if STDEXEC_HAS_BUILTIN(__is_nothrow_constructible)
-template <class _Ty, class... _As>
-concept __nothrow_constructible_from = constructible_from<_Ty, _As...> &&
- __is_nothrow_constructible(_Ty, _As...);
-#else
-template <class _Ty, class... _As>
-concept __nothrow_constructible_from =
- constructible_from<_Ty, _As...> &&
- std::is_nothrow_constructible_v<_Ty, _As...>;
-#endif
-
-template <class _Ty>
-concept __nothrow_move_constructible = __nothrow_constructible_from<_Ty, _Ty>;
-
-template <class _Ty>
-concept __nothrow_copy_constructible =
- __nothrow_constructible_from<_Ty, const _Ty&>;
-
-template <class _Ty>
-concept __decay_copyable = constructible_from<__decay_t<_Ty>, _Ty>;
-
-template <class _Ty>
-concept __nothrow_decay_copyable =
- __nothrow_constructible_from<__decay_t<_Ty>, _Ty>;
-
-template <class _Ty, class _Up>
-concept __decays_to_derived_from = derived_from<__decay_t<_Ty>, _Up>;
-
-} // namespace stdexec
-
-// #if !STDEXEC_HAS_STD_CONCEPTS_HEADER()
-// namespace std {
-// using namespace stdexec::__std_concepts;
-// }
-// #endif
diff --git a/include/sdbusplus/async/stdexec/coroutine.hpp b/include/sdbusplus/async/stdexec/coroutine.hpp
index 01fb2fc..2426ed3 100644
--- a/include/sdbusplus/async/stdexec/coroutine.hpp
+++ b/include/sdbusplus/async/stdexec/coroutine.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
@@ -15,156 +15,130 @@
*/
#pragma once
-#include "__detail/__concepts.hpp"
+#include "__detail/__awaitable.hpp"
#include "__detail/__config.hpp"
-#include "concepts.hpp"
-#include <version>
-#if __cpp_impl_coroutine >= 201902 && __cpp_lib_coroutine >= 201902
-#include <coroutine>
-namespace __coro = std;
-#elif defined(__cpp_coroutines) && __has_include(<experimental/coroutine>)
-#include <experimental/coroutine>
-namespace __coro = std::experimental;
-#else
-#define STDEXEC_STD_NO_COROUTINES_ 1
-#endif
-
+#if STDEXEC_MSVC() && _MSC_VER >= 1939
namespace stdexec
{
-#if !STDEXEC_STD_NO_COROUTINES_
-// Define some concepts and utilities for working with awaitables
-template <class _Tp>
-concept __await_suspend_result =
- __one_of<_Tp, void, bool> ||
- __is_instance_of<_Tp, __coro::coroutine_handle>;
-
-template <class _Awaiter, class _Promise>
-concept __with_await_suspend =
- same_as<_Promise, void> || //
- requires(_Awaiter& __awaiter, __coro::coroutine_handle<_Promise> __h) {
- {
- __awaiter.await_suspend(__h)
- } -> __await_suspend_result;
- };
-
-template <class _Awaiter, class _Promise = void>
-concept __awaiter = //
- requires(_Awaiter& __awaiter) {
- __awaiter.await_ready() ? 1 : 0;
- __awaiter.await_resume();
- } && //
- __with_await_suspend<_Awaiter, _Promise>;
-
-#if STDEXEC_MSVC()
// MSVCBUG
-// https://developercommunity.visualstudio.com/t/operator-co_await-not-found-in-requires/10452721
+// https://developercommunity.visualstudio.com/t/destroy-coroutine-from-final_suspend-r/10096047
-template <class _Awaitable>
-void __co_await_constraint(_Awaitable&& __awaitable)
- requires requires {
- operator co_await(static_cast<_Awaitable&&>(__awaitable));
- };
-#endif
+// Prior to Visual Studio 17.9 (Feb, 2024), aka MSVC 19.39, MSVC incorrectly
+// allocates the return buffer for await_suspend calls within the suspended
+// coroutine frame. When the suspended coroutine is destroyed within
+// await_suspend, the continuation coroutine handle is not only used after free,
+// but also overwritten by the debug malloc implementation when NRVO is in play.
-template <class _Awaitable>
-auto __get_awaiter(_Awaitable&& __awaitable, void*) -> decltype(auto)
+// This workaround delays the destruction of the suspended coroutine by wrapping
+// the continuation in another coroutine which destroys the former and transfers
+// execution to the original continuation.
+
+// The wrapping coroutine is thread-local and is reused within the thread for
+// each destroy-and-continue sequence. The wrapping coroutine itself is
+// destroyed at thread exit.
+
+namespace __destroy_and_continue_msvc
{
- if constexpr (requires {
- static_cast<_Awaitable&&>(__awaitable)
- .
- operator co_await();
- })
- {
- return static_cast<_Awaitable&&>(__awaitable).operator co_await();
- }
- else if constexpr (requires {
-#if STDEXEC_MSVC()
- __co_await_constraint(
- static_cast<_Awaitable&&>(__awaitable));
-#else
- operator co_await(
- static_cast<_Awaitable&&>(__awaitable));
-#endif
- })
- {
- return operator co_await(static_cast<_Awaitable&&>(__awaitable));
- }
- else
- {
- return static_cast<_Awaitable&&>(__awaitable);
- }
-}
-
-template <class _Awaitable, class _Promise>
-auto __get_awaiter(_Awaitable&& __awaitable, _Promise* __promise)
- -> decltype(auto)
- requires requires {
- __promise->await_transform(
- static_cast<_Awaitable&&>(__awaitable));
- }
+struct __task
{
- if constexpr (requires {
- __promise
- ->await_transform(
- static_cast<_Awaitable&&>(__awaitable))
- .
- operator co_await();
- })
+ struct promise_type
{
- return __promise
- ->await_transform(static_cast<_Awaitable&&>(__awaitable))
- .
- operator co_await();
- }
- else if constexpr (requires {
-#if STDEXEC_MSVC()
- __co_await_constraint(__promise->await_transform(
- static_cast<_Awaitable&&>(__awaitable)));
-#else
- operator co_await(__promise->await_transform(
- static_cast<_Awaitable&&>(__awaitable)));
-#endif
- })
- {
- return operator co_await(
- __promise->await_transform(static_cast<_Awaitable&&>(__awaitable)));
- }
- else
- {
- return __promise->await_transform(
- static_cast<_Awaitable&&>(__awaitable));
- }
-}
-
-template <class _Awaitable, class _Promise = void>
-concept __awaitable = //
- requires(_Awaitable&& __awaitable, _Promise* __promise) {
+ __task get_return_object() noexcept
{
- stdexec::__get_awaiter(static_cast<_Awaitable&&>(__awaitable),
- __promise)
- } -> __awaiter<_Promise>;
+ return {
+ __coro::coroutine_handle<promise_type>::from_promise(*this)};
+ }
+
+ static std::suspend_never initial_suspend() noexcept
+ {
+ return {};
+ }
+
+ static std::suspend_never final_suspend() noexcept
+ {
+ STDEXEC_ASSERT(!"Should never get here");
+ return {};
+ }
+
+ static void return_void() noexcept
+ {
+ STDEXEC_ASSERT(!"Should never get here");
+ }
+
+ static void unhandled_exception() noexcept
+ {
+ STDEXEC_ASSERT(!"Should never get here");
+ }
};
-template <class _Tp>
-auto __as_lvalue(_Tp&&) -> _Tp&;
+ __coro::coroutine_handle<> __coro_;
+};
-template <class _Awaitable, class _Promise = void>
- requires __awaitable<_Awaitable, _Promise>
-using __await_result_t =
- decltype(stdexec::__as_lvalue(
- stdexec::__get_awaiter(std::declval<_Awaitable>(),
- static_cast<_Promise*>(nullptr)))
- .await_resume());
+struct __continue_t
+{
+ static constexpr bool await_ready() noexcept
+ {
+ return false;
+ }
-#else
+ __coro::coroutine_handle<>
+ await_suspend(__coro::coroutine_handle<>) noexcept
+ {
+ return __continue_;
+ }
-template <class _Awaitable, class _Promise = void>
-concept __awaitable = false;
+ static void await_resume() noexcept {}
-template <class _Awaitable, class _Promise = void>
- requires __awaitable<_Awaitable, _Promise>
-using __await_result_t = void;
+ __coro::coroutine_handle<> __continue_;
+};
-#endif
+struct __context
+{
+ __coro::coroutine_handle<> __destroy_;
+ __coro::coroutine_handle<> __continue_;
+};
+
+inline __task __co_impl(__context& __c)
+{
+ while (true)
+ {
+ co_await __continue_t{__c.__continue_};
+ __c.__destroy_.destroy();
+ }
+}
+
+struct __context_and_coro
+{
+ __context_and_coro()
+ {
+ __context_.__continue_ = __coro::noop_coroutine();
+ __coro_ = __co_impl(__context_).__coro_;
+ }
+
+ ~__context_and_coro()
+ {
+ __coro_.destroy();
+ }
+
+ __context __context_;
+ __coro::coroutine_handle<> __coro_;
+};
+
+inline __coro::coroutine_handle<> __impl(__coro::coroutine_handle<> __destroy,
+ __coro::coroutine_handle<> __continue)
+{
+ static thread_local __context_and_coro __c;
+ __c.__context_.__destroy_ = __destroy;
+ __c.__context_.__continue_ = __continue;
+ return __c.__coro_;
+}
+} // namespace __destroy_and_continue_msvc
} // namespace stdexec
+
+#define STDEXEC_DESTROY_AND_CONTINUE(__destroy, __continue) \
+ (::stdexec::__destroy_and_continue_msvc::__impl(__destroy, __continue))
+#else
+#define STDEXEC_DESTROY_AND_CONTINUE(__destroy, __continue) \
+ (__destroy.destroy(), __continue)
+#endif
diff --git a/include/sdbusplus/async/stdexec/env.hpp b/include/sdbusplus/async/stdexec/env.hpp
index d83948a..27590f2 100644
--- a/include/sdbusplus/async/stdexec/env.hpp
+++ b/include/sdbusplus/async/stdexec/env.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
@@ -23,7 +23,7 @@
namespace exec
{
template <class _Tag, class _Value>
-using with_t = stdexec::__env::__with<_Value, _Tag>;
+using with_t = stdexec::prop<_Tag, _Value>;
namespace __envs
{
@@ -32,7 +32,7 @@
template <class _Tag, class _Value>
auto operator()(_Tag, _Value&& __val) const
{
- return stdexec::__env::__with(static_cast<_Value&&>(__val), _Tag());
+ return stdexec::prop{_Tag(), static_cast<_Value&&>(__val)};
}
};
@@ -93,24 +93,25 @@
_Default __default_;
_Receiver __rcvr_;
- friend void tag_invoke(start_t, __t& __self) noexcept
+ void start() & noexcept
{
try
{
if constexpr (__callable<_Tag, env_of_t<_Receiver>>)
{
- const auto& __env = get_env(__self.__rcvr_);
- set_value(std::move(__self.__rcvr_), _Tag{}(__env));
+ const auto& __env = get_env(__rcvr_);
+ stdexec::set_value(std::move(__rcvr_), _Tag{}(__env));
}
else
{
- set_value(std::move(__self.__rcvr_),
- std::move(__self.__default_));
+ stdexec::set_value(std::move(__rcvr_),
+ std::move(__default_));
}
}
catch (...)
{
- set_error(std::move(__self.__rcvr_), std::current_exception());
+ stdexec::set_error(std::move(__rcvr_),
+ std::current_exception());
}
}
};
@@ -133,6 +134,7 @@
__with_default<__mbind_back_q<__call_result_t, _Env>, _Default>, _Tag>;
template <class _Env>
using __default_t = __if_c<__callable<_Tag, _Env>, __ignore, _Default>;
+
template <class _Env>
using __completions_t =
completion_signatures<set_value_t(__value_t<_Env>),
@@ -140,7 +142,7 @@
template <__decays_to<__sender> _Self, class _Receiver>
requires receiver_of<_Receiver, __completions_t<env_of_t<_Receiver>>>
- friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) //
+ static auto connect(_Self&& __self, _Receiver __rcvr) //
noexcept(std::is_nothrow_move_constructible_v<_Receiver>)
-> __operation_t<_Tag, __default_t<env_of_t<_Receiver>>, _Receiver>
{
@@ -150,8 +152,7 @@
}
template <class _Env>
- friend auto tag_invoke(get_completion_signatures_t, __sender, _Env&&)
- -> __completions_t<_Env>
+ auto get_completion_signatures(_Env&&) -> __completions_t<_Env>
{
return {};
}
@@ -170,7 +171,8 @@
inline constexpr __read_with_default::__read_with_default_t read_with_default{};
-inline constexpr stdexec::__write_::__write_t write{};
+inline constexpr stdexec::__write_::__write_env_t write{};
+inline constexpr stdexec::__write_::__write_env_t write_env{};
} // namespace exec
STDEXEC_PRAGMA_POP()
diff --git a/include/sdbusplus/async/stdexec/execution.hpp b/include/sdbusplus/async/stdexec/execution.hpp
index 31cfe3f..0dc3800 100644
--- a/include/sdbusplus/async/stdexec/execution.hpp
+++ b/include/sdbusplus/async/stdexec/execution.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
@@ -15,6864 +15,62 @@
*/
#pragma once
-#include "__detail/__basic_sender.hpp"
-#include "__detail/__config.hpp"
-#include "__detail/__cpo.hpp"
-#include "__detail/__domain.hpp"
-#include "__detail/__env.hpp"
#include "__detail/__execution_fwd.hpp"
+
+// include these after __execution_fwd.hpp
+#include "__detail/__as_awaitable.hpp"
+#include "__detail/__basic_sender.hpp"
+#include "__detail/__bulk.hpp"
+#include "__detail/__completion_signatures.hpp"
+#include "__detail/__connect_awaitable.hpp"
+#include "__detail/__continue_on.hpp"
+#include "__detail/__cpo.hpp"
+#include "__detail/__debug.hpp"
+#include "__detail/__domain.hpp"
+#include "__detail/__ensure_started.hpp"
+#include "__detail/__env.hpp"
+#include "__detail/__execute.hpp"
+#include "__detail/__inline_scheduler.hpp"
+#include "__detail/__into_variant.hpp"
#include "__detail/__intrusive_ptr.hpp"
+#include "__detail/__intrusive_slist.hpp"
+#include "__detail/__just.hpp"
+#include "__detail/__let.hpp"
#include "__detail/__meta.hpp"
-#include "__detail/__scope.hpp"
+#include "__detail/__on.hpp"
+#include "__detail/__operation_states.hpp"
+#include "__detail/__read_env.hpp"
+#include "__detail/__receiver_adaptor.hpp"
+#include "__detail/__receivers.hpp"
+#include "__detail/__run_loop.hpp"
+#include "__detail/__schedule_from.hpp"
+#include "__detail/__schedulers.hpp"
+#include "__detail/__sender_adaptor_closure.hpp"
+#include "__detail/__senders.hpp"
+#include "__detail/__split.hpp"
+#include "__detail/__start_detached.hpp"
+#include "__detail/__start_on.hpp"
+#include "__detail/__stopped_as_error.hpp"
+#include "__detail/__stopped_as_optional.hpp"
+#include "__detail/__submit.hpp"
+#include "__detail/__sync_wait.hpp"
+#include "__detail/__then.hpp"
+#include "__detail/__transfer_just.hpp"
+#include "__detail/__transform_completion_signatures.hpp"
+#include "__detail/__transform_sender.hpp"
#include "__detail/__type_traits.hpp"
+#include "__detail/__upon_error.hpp"
+#include "__detail/__upon_stopped.hpp"
#include "__detail/__utility.hpp"
+#include "__detail/__when_all.hpp"
+#include "__detail/__with_awaitable_senders.hpp"
+#include "__detail/__write_env.hpp"
#include "concepts.hpp"
#include "coroutine.hpp"
#include "functional.hpp"
#include "stop_token.hpp"
-#include <atomic>
-#include <cassert>
-#include <concepts>
-#include <condition_variable>
-#include <cstddef>
-#include <exception>
-#include <memory>
-#include <mutex>
-#include <optional>
-#include <stdexcept>
-#include <system_error>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-#include <variant>
-
-STDEXEC_PRAGMA_PUSH()
-STDEXEC_PRAGMA_IGNORE_GNU("-Wundefined-inline")
-STDEXEC_PRAGMA_IGNORE_GNU("-Wsubobject-linkage")
-STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
-
-STDEXEC_PRAGMA_IGNORE_EDG(1302)
-STDEXEC_PRAGMA_IGNORE_EDG(497)
-STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference)
-
-namespace stdexec
-{
-/////////////////////////////////////////////////////////////////////////////
-template <class _Sender, class _Scheduler, class _Tag = set_value_t>
-concept __completes_on = __decays_to<
- __call_result_t<get_completion_scheduler_t<_Tag>, env_of_t<_Sender>>,
- _Scheduler>;
-
-/////////////////////////////////////////////////////////////////////////////
-template <class _Sender, class _Scheduler, class _Env>
-concept __starts_on =
- __decays_to<__call_result_t<get_scheduler_t, _Env>, _Scheduler>;
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.receivers]
-namespace __receivers
-{
-struct set_value_t
-{
- template <class _Fn, class... _Args>
- using __f = __minvoke<_Fn, _Args...>;
-
- template <class _Receiver, class... _As>
- requires tag_invocable<set_value_t, _Receiver, _As...>
- STDEXEC_ATTRIBUTE((host, device, always_inline)) void
- operator()(_Receiver&& __rcvr, _As&&... __as) const noexcept
- {
- static_assert(nothrow_tag_invocable<set_value_t, _Receiver, _As...>);
- (void)tag_invoke(set_value_t{}, static_cast<_Receiver&&>(__rcvr),
- static_cast<_As&&>(__as)...);
- }
-};
-
-struct set_error_t
-{
- template <class _Fn, class... _Args>
- requires(sizeof...(_Args) == 1)
- using __f = __minvoke<_Fn, _Args...>;
-
- template <class _Receiver, class _Error>
- requires tag_invocable<set_error_t, _Receiver, _Error>
- STDEXEC_ATTRIBUTE((host, device, always_inline)) void
- operator()(_Receiver&& __rcvr, _Error&& __err) const noexcept
- {
- static_assert(nothrow_tag_invocable<set_error_t, _Receiver, _Error>);
- (void)tag_invoke(set_error_t{}, static_cast<_Receiver&&>(__rcvr),
- static_cast<_Error&&>(__err));
- }
-};
-
-struct set_stopped_t
-{
- template <class _Fn, class... _Args>
- requires(sizeof...(_Args) == 0)
- using __f = __minvoke<_Fn, _Args...>;
-
- template <class _Receiver>
- requires tag_invocable<set_stopped_t, _Receiver>
- STDEXEC_ATTRIBUTE((host, device, always_inline)) void
- operator()(_Receiver&& __rcvr) const noexcept
- {
- static_assert(nothrow_tag_invocable<set_stopped_t, _Receiver>);
- (void)tag_invoke(set_stopped_t{}, static_cast<_Receiver&&>(__rcvr));
- }
-};
-} // namespace __receivers
-
-using __receivers::set_error_t;
-using __receivers::set_stopped_t;
-using __receivers::set_value_t;
-inline constexpr set_value_t set_value{};
-inline constexpr set_error_t set_error{};
-inline constexpr set_stopped_t set_stopped{};
-
-inline constexpr struct __try_call_t
-{
- template <class _Receiver, class _Fun, class... _Args>
- requires __callable<_Fun, _Args...>
- void operator()(_Receiver&& __rcvr, _Fun __fun,
- _Args&&... __args) const noexcept
- {
- if constexpr (__nothrow_callable<_Fun, _Args...>)
- {
- static_cast<_Fun&&>(__fun)(static_cast<_Args&&>(__args)...);
- }
- else
- {
- try
- {
- static_cast<_Fun&&>(__fun)(static_cast<_Args&&>(__args)...);
- }
- catch (...)
- {
- set_error(static_cast<_Receiver&&>(__rcvr),
- std::current_exception());
- }
- }
- }
-} __try_call{};
-
-namespace __error__
-{
-inline constexpr __mstring __unrecognized_sender_type_diagnostic =
- "The given type cannot be used as a sender with the given environment "
- "because the attempt to compute the completion signatures failed."_mstr;
-
-template <__mstring _Diagnostic = __unrecognized_sender_type_diagnostic>
-struct _UNRECOGNIZED_SENDER_TYPE_;
-
-template <class _Sender>
-struct _WITH_SENDER_;
-
-template <class... _Senders>
-struct _WITH_SENDERS_;
-
-template <class _Env>
-struct _WITH_ENVIRONMENT_;
-
-template <class _Ty>
-struct _WITH_TYPE_;
-
-template <class _Receiver>
-struct _WITH_RECEIVER_;
-
-template <class _Sig>
-struct _MISSING_COMPLETION_SIGNAL_;
-} // namespace __error__
-
-using __error__::_MISSING_COMPLETION_SIGNAL_;
-using __error__::_UNRECOGNIZED_SENDER_TYPE_;
-using __error__::_WITH_ENVIRONMENT_;
-using __error__::_WITH_RECEIVER_;
-using __error__::_WITH_TYPE_;
-
-template <class _Sender>
-using _WITH_SENDER_ = __error__::_WITH_SENDER_<__name_of<_Sender>>;
-
-template <class... _Senders>
-using _WITH_SENDERS_ = __error__::_WITH_SENDERS_<__name_of<_Senders>...>;
-
-/////////////////////////////////////////////////////////////////////////////
-// completion_signatures
-namespace __compl_sigs
-{
-template <class _Sig>
-inline constexpr bool __is_compl_sig = false;
-template <class... _Args>
-inline constexpr bool __is_compl_sig<set_value_t(_Args...)> = true;
-template <class _Error>
-inline constexpr bool __is_compl_sig<set_error_t(_Error)> = true;
-template <>
-inline constexpr bool __is_compl_sig<set_stopped_t()> = true;
-} // namespace __compl_sigs
-
-template <class _Sig>
-concept __completion_signature = __compl_sigs::__is_compl_sig<_Sig>;
-
-template <__completion_signature... _Sigs>
-struct completion_signatures
-{};
-
-namespace __compl_sigs
-{
-template <class _TaggedTuple, class _Tag, class... _Ts>
-auto __as_tagged_tuple_(_Tag (*)(_Ts...), _TaggedTuple*)
- -> __mconst<__minvoke<_TaggedTuple, _Tag, _Ts...>>;
-
-template <class _Sig, class _TaggedTuple>
-using __as_tagged_tuple = decltype(__compl_sigs::__as_tagged_tuple_(
- static_cast<_Sig*>(nullptr), static_cast<_TaggedTuple*>(nullptr)));
-
-template <class _TaggedTuple, class _Variant, class... _Sigs>
-auto __for_all_sigs_(completion_signatures<_Sigs...>*, _TaggedTuple*, _Variant*)
- -> __mconst<__minvoke<
- _Variant, __minvoke<__as_tagged_tuple<_Sigs, _TaggedTuple>>...>>;
-
-template <class _Completions, class _TaggedTuple, class _Variant>
-using __for_all_sigs = //
- __minvoke< //
- decltype(__compl_sigs::__for_all_sigs_( //
- static_cast<_Completions*>(nullptr),
- static_cast<_TaggedTuple*>(nullptr),
- static_cast<_Variant*>(nullptr)))>;
-
-template <class _Completions, class _TaggedTuple, class _Variant>
-using __maybe_for_all_sigs =
- __meval<__for_all_sigs, _Completions, _TaggedTuple, _Variant>;
-} // namespace __compl_sigs
-
-template <class _Completions>
-concept __valid_completion_signatures = //
- __ok<_Completions> && __is_instance_of<_Completions, completion_signatures>;
-
-template <class _Completions>
-using __invalid_completion_signatures_t = //
- __mbool<!__valid_completion_signatures<_Completions>>;
-
-template <__mstring _Msg =
- "Expected an instance of template completion_signatures<>"_mstr>
-struct _INVALID_COMPLETION_SIGNATURES_TYPE_
-{
- template <class... _Completions>
- using __f = //
- __mexception<
- _INVALID_COMPLETION_SIGNATURES_TYPE_<>,
- __minvoke<__mfind_if<__q<__invalid_completion_signatures_t>,
- __mcompose<__q<_WITH_TYPE_>, __q<__mfront>>>,
- _Completions...>>;
-};
-
-template <class... _Completions>
-using __concat_completion_signatures_impl_t = //
- __minvoke<__if_c<(__valid_completion_signatures<_Completions> && ...),
- __mconcat<__munique<__q<completion_signatures>>>,
- _INVALID_COMPLETION_SIGNATURES_TYPE_<>>,
- _Completions...>;
-
-template <class... _Completions>
-struct __concat_completion_signatures_
-{
- using __t = __meval<__concat_completion_signatures_impl_t, _Completions...>;
-};
-
-template <class... _Completions>
-using __concat_completion_signatures_t =
- __t<__concat_completion_signatures_<_Completions...>>;
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.receivers]
-template <class _Receiver, class _Tag, class... _Args>
-auto __try_completion(_Tag (*)(_Args...))
- -> __mexception<_MISSING_COMPLETION_SIGNAL_<_Tag(_Args...)>,
- _WITH_RECEIVER_<_Receiver>>;
-
-template <class _Receiver, class _Tag, class... _Args>
- requires nothrow_tag_invocable<_Tag, _Receiver, _Args...>
-auto __try_completion(_Tag (*)(_Args...)) -> __msuccess;
-
-template <class _Receiver, class... _Sigs>
-auto __try_completions(completion_signatures<_Sigs...>*) -> decltype((
- __msuccess(), ...,
- stdexec::__try_completion<_Receiver>(static_cast<_Sigs*>(nullptr))));
-
-template <class _Sender, class _Env>
-using __unrecognized_sender_error = //
- __mexception<_UNRECOGNIZED_SENDER_TYPE_<>, _WITH_SENDER_<_Sender>,
- _WITH_ENVIRONMENT_<_Env>>;
-
-template <class _Sender, class _Env>
-using __completion_signatures_of_t =
- __call_result_t<get_completion_signatures_t, _Sender, _Env>;
-
-/////////////////////////////////////////////////////////////////////////////
-// early sender type-checking
-template <class _Sender>
-concept __well_formed_sender = //
- !__detail::__non_dependent_sender<_Sender> ||
- __valid_completion_signatures<
- __completion_signatures_of_t<_Sender, empty_env>>;
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.receivers]
-struct receiver_t
-{
- using receiver_concept = receiver_t; // NOT TO SPEC
-};
-
-namespace __detail
-{
-template <class _Receiver>
-concept __enable_receiver = //
- (STDEXEC_NVHPC(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,
- _Receiver); // NOT TO SPEC, for receiver_adaptor
-} // namespace __detail
-
-template <class _Receiver>
-inline constexpr bool enable_receiver =
- __detail::__enable_receiver<_Receiver>; // NOT TO SPEC
-
-template <class _Receiver>
-concept receiver = enable_receiver<__decay_t<_Receiver>> && //
- environment_provider<__cref_t<_Receiver>> && //
- move_constructible<__decay_t<_Receiver>> && //
- constructible_from<__decay_t<_Receiver>, _Receiver>;
-
-template <class _Receiver, class _Completions>
-concept receiver_of = //
- receiver<_Receiver> && //
- requires(_Completions* __completions) {
- {
- stdexec::__try_completions<__decay_t<_Receiver>>(__completions)
- } -> __ok;
- };
-
-template <class _Receiver, class _Sender>
-concept __receiver_from =
- receiver_of<_Receiver,
- __completion_signatures_of_t<_Sender, env_of_t<_Receiver>>>;
-
-/////////////////////////////////////////////////////////////////////////////
-// Some utilities for debugging senders
-namespace __debug
-{
-struct __is_debug_env_t
-{
- friend constexpr auto tag_invoke(forwarding_query_t,
- const __is_debug_env_t&) noexcept -> bool
- {
- return true;
- }
- template <class _Env>
- requires tag_invocable<__is_debug_env_t, const _Env&>
- auto operator()(const _Env&) const noexcept
- -> tag_invoke_result_t<__is_debug_env_t, const _Env&>;
-};
-template <class _Env>
-using __debug_env_t =
- __env::__join_t<__env::__with<bool, __is_debug_env_t>, _Env>;
-
-template <class _Env>
-concept __is_debug_env = tag_invocable<__debug::__is_debug_env_t, _Env>;
-
-struct __completion_signatures
-{};
-
-#if STDEXEC_MSVC()
-// MSVCBUG
-// https://developercommunity.visualstudio.com/t/Explicit-variable-template-specialisatio/10360032
-// MSVCBUG
-// https://developercommunity.visualstudio.com/t/Non-function-type-interpreted-as-functio/10447831
-
-template <class _Sig>
-struct __normalize_sig;
-
-template <class _Tag, class... _Args>
-struct __normalize_sig<_Tag(_Args...)>
-{
- using __type = _Tag (*)(_Args&&...);
-};
-
-template <class _Sig>
-using __normalize_sig_t = typename __normalize_sig<_Sig>::__type;
-#else
-template <class _Sig>
-extern int __normalize_sig;
-
-template <class _Tag, class... _Args>
-extern _Tag (*__normalize_sig<_Tag(_Args...)>)(_Args&&...);
-
-template <class _Sig>
-using __normalize_sig_t = decltype(__normalize_sig<_Sig>);
-#endif
-
-template <class... _Sigs>
-struct __valid_completions
-{
- template <derived_from<__valid_completions> _Self, class _Tag,
- class... _Args>
- requires __one_of<_Tag (*)(_Args&&...), _Sigs...>
- STDEXEC_ATTRIBUTE((host,
- device)) friend void tag_invoke(_Tag, _Self&&,
- _Args&&...) noexcept
- {
- STDEXEC_TERMINATE();
- }
-};
-
-template <class _CvrefSenderId, class _Env, class _Completions>
-struct __debug_receiver
-{
- using __t = __debug_receiver;
- using __id = __debug_receiver;
- using receiver_concept = receiver_t;
-};
-
-template <class _CvrefSenderId, class _Env, class... _Sigs>
-struct __debug_receiver<_CvrefSenderId, _Env,
- completion_signatures<_Sigs...>> //
- : __valid_completions<__normalize_sig_t<_Sigs>...>
-{
- using __t = __debug_receiver;
- using __id = __debug_receiver;
- using receiver_concept = receiver_t;
-
- template <same_as<get_env_t> _Tag>
- STDEXEC_ATTRIBUTE((host, device))
- friend auto tag_invoke(_Tag, __debug_receiver) noexcept
- -> __debug_env_t<_Env>
- {
- STDEXEC_TERMINATE();
- }
-};
-
-struct _COMPLETION_SIGNATURES_MISMATCH_
-{};
-
-template <class _Sig>
-struct _COMPLETION_SIGNATURE_
-{};
-
-template <class... _Sigs>
-struct _IS_NOT_ONE_OF_
-{};
-
-template <class _Sender>
-struct _SIGNAL_SENT_BY_SENDER_
-{};
-
-template <class _Warning>
-[[deprecated(
- "The sender claims to send a particular set of completions,"
- " but in actual fact it completes with a result that is not"
- " one of the declared completion signatures.")]] STDEXEC_ATTRIBUTE((host,
- device)) void _ATTENTION_() noexcept
-{}
-
-template <class _Sig>
-struct __invalid_completion
-{
- struct __t
- {
- template <class _CvrefSenderId, class _Env, class... _Sigs>
- // BUGBUG this works around a recently (aug 2023) introduced regression
- // in nvc++
- requires(!__one_of<_Sig, _Sigs...>)
- __t(__debug_receiver<_CvrefSenderId, _Env,
- completion_signatures<_Sigs...>>&&) noexcept
- {
- using _SenderId = __decay_t<_CvrefSenderId>;
- using _Sender = stdexec::__t<_SenderId>;
- using _What = //
- _WARNING_< //
- _COMPLETION_SIGNATURES_MISMATCH_,
- _COMPLETION_SIGNATURE_<_Sig>, _IS_NOT_ONE_OF_<_Sigs...>,
- _SIGNAL_SENT_BY_SENDER_<__name_of<_Sender>>>;
- __debug::_ATTENTION_<_What>();
- }
- };
-};
-
-template <__completion_tag _Tag, class... _Args>
-STDEXEC_ATTRIBUTE((host, device))
-void tag_invoke(_Tag, __t<__invalid_completion<_Tag(_Args...)>>,
- _Args&&...) noexcept
-{}
-
-struct __debug_operation
-{
- template <same_as<start_t> _Tag>
- friend void tag_invoke(_Tag, __debug_operation&) noexcept
- {}
-};
-
-////////////////////////////////////////////////////////////////////////////
-// `__debug_sender`
-// ===============
-//
-// Understanding why a particular sender doesn't connect to a particular
-// receiver is nigh impossible in the current design due to limitations in
-// how the compiler reports overload resolution failure in the presence of
-// constraints. `__debug_sender` is a utility to assist with the process. It
-// gives you the deep template instantiation backtrace that you need to
-// understand where in a chain of senders the problem is occurring.
-//
-// ```c++
-// template <class _Sigs, class _Env = empty_env, class _Sender>
-// void __debug_sender(_Sender&& __sndr, _Env = {});
-//
-// template <class _Env = empty_env, class _Sender>
-// void __debug_sender(_Sender&& __sndr, _Env = {});
-// ```
-//
-// **Usage:**
-//
-// To find out where in a chain of senders a sender is failing to connect
-// to a receiver, pass it to `__debug_sender`, optionally with an
-// environment argument; e.g. `__debug_sender(sndr [, env])`
-//
-// To find out why a sender will not connect to a receiver of a particular
-// signature, specify the set of completion signatures as an explicit template
-// argument that names an instantiation of `completion_signatures`; e.g.:
-// `__debug_sender<completion_signatures<set_value_t(int)>>(sndr [, env])`.
-//
-// **How it works:**
-//
-// The `__debug_sender` function `connect`'s the sender to a
-// `__debug_receiver`, whose environment is augmented with a special
-// `__is_debug_env_t` query. An additional fall-back overload is added to
-// the `connect` CPO that recognizes receivers whose environments respond to
-// that query and lets them through. Then in a non-immediate context, it
-// looks for a `tag_invoke(connect_t...)` overload for the input sender and
-// receiver. This will recurse until it hits the `tag_invoke` call that is
-// causing the failure.
-//
-// At least with clang, this gives me a nice backtrace, at the bottom of
-// which is the faulty `tag_invoke` overload with a mention of the
-// constraint that failed.
-template <class _Sigs, class _Env = empty_env, class _Sender>
-void __debug_sender(_Sender&& __sndr, const _Env& = {})
-{
- if constexpr (!__is_debug_env<_Env>)
- {
- if (sizeof(_Sender) == ~0u)
- { // never true
- using _Receiver =
- __debug_receiver<__cvref_id<_Sender>, _Env, _Sigs>;
- using _Operation = connect_result_t<_Sender, _Receiver>;
- // static_assert(receiver_of<_Receiver, _Sigs>);
- if constexpr (!same_as<_Operation, __debug_operation>)
- {
- auto __op = connect(static_cast<_Sender&&>(__sndr),
- _Receiver{});
- start(__op);
- }
- }
- }
-}
-
-template <class _Env = empty_env, class _Sender>
-void __debug_sender(_Sender&& __sndr, const _Env& = {})
-{
- if constexpr (!__is_debug_env<_Env>)
- {
- if (sizeof(_Sender) == ~0)
- { // never true
- using _Sigs =
- __completion_signatures_of_t<_Sender, __debug_env_t<_Env>>;
- if constexpr (!same_as<_Sigs, __debug::__completion_signatures>)
- {
- using _Receiver =
- __debug_receiver<__cvref_id<_Sender>, _Env, _Sigs>;
- using _Operation = connect_result_t<_Sender, _Receiver>;
- // static_assert(receiver_of<_Receiver, _Sigs>);
- if constexpr (!same_as<_Operation, __debug_operation>)
- {
- auto __op = connect(static_cast<_Sender&&>(__sndr),
- _Receiver{});
- start(__op);
- }
- }
- }
- }
-}
-} // namespace __debug
-
-using __debug::__debug_sender;
-using __debug::__is_debug_env;
-
-/////////////////////////////////////////////////////////////////////////////
-// dependent_domain
-struct dependent_domain
-{
- template <sender_expr _Sender, class _Env>
- requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
- STDEXEC_ATTRIBUTE((always_inline)) decltype(auto)
- transform_sender(_Sender&& __sndr, const _Env& __env) const;
-};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.transform_sender]
-namespace __domain
-{
-struct __transform_env
-{
- template <class _Domain, class _Sender, class _Env>
- STDEXEC_ATTRIBUTE((always_inline))
- /*constexpr*/
- decltype(auto) operator()(_Domain __dom, _Sender&& __sndr,
- _Env&& __env) const noexcept
- {
- if constexpr (__domain::__has_transform_env<_Domain, _Sender, _Env>)
- {
- return __dom.transform_env(static_cast<_Sender&&>(__sndr),
- static_cast<_Env&&>(__env));
- }
- else
- {
- return default_domain().transform_env(
- static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env));
- }
- }
-};
-
-struct __transform_sender_1
-{
- template <class _Domain, class _Sender, class... _Env>
- STDEXEC_ATTRIBUTE((always_inline))
- /*constexpr*/
- decltype(auto) operator()(_Domain __dom, _Sender&& __sndr,
- const _Env&... __env) const
- {
- if constexpr (__domain::__has_transform_sender<_Domain, _Sender,
- _Env...>)
- {
- return __dom.transform_sender(static_cast<_Sender&&>(__sndr),
- __env...);
- }
- else
- {
- return default_domain().transform_sender(
- static_cast<_Sender&&>(__sndr), __env...);
- }
- }
-};
-
-template <class _Ty, class _Uy>
-concept __decay_same_as = same_as<__decay_t<_Ty>, __decay_t<_Uy>>;
-
-struct __transform_sender
-{
- template <class _Self = __transform_sender, class _Domain, class _Sender,
- class... _Env>
- STDEXEC_ATTRIBUTE((always_inline))
- /*constexpr*/
- decltype(auto) operator()(_Domain __dom, _Sender&& __sndr,
- const _Env&... __env) const
- {
- using _Sender2 = __call_result_t<__transform_sender_1, _Domain, _Sender,
- const _Env&...>;
- // If the transformation doesn't change the sender's type, then do not
- // apply the transform recursively.
- if constexpr (__decay_same_as<_Sender, _Sender2>)
- {
- return __transform_sender_1()(__dom, static_cast<_Sender&&>(__sndr),
- __env...);
- }
- else
- {
- // We transformed the sender and got back a different sender.
- // Transform that one too.
- return _Self()(__dom,
- __transform_sender_1()(
- __dom, static_cast<_Sender&&>(__sndr), __env...),
- __env...);
- }
- }
-};
-
-struct __transform_dependent_sender
-{
- // If we are doing a lazy customization of a type whose domain is
- // value-dependent (e.g., let_value), first transform the sender to
- // determine the domain. Then continue transforming the sender with the
- // requested domain.
- template <class _Domain, sender_expr _Sender, class _Env>
- requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
- /*constexpr*/ auto operator()(_Domain __dom, _Sender&& __sndr,
- const _Env& __env) const -> decltype(auto)
- {
- static_assert(__none_of<_Domain, dependent_domain>);
- return __transform_sender()(__dom,
- dependent_domain().transform_sender(
- static_cast<_Sender&&>(__sndr), __env),
- __env);
- }
-};
-} // namespace __domain
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.transform_sender]
-inline constexpr struct transform_sender_t :
- __domain::__transform_sender,
- __domain::__transform_dependent_sender
-{
- using __domain::__transform_sender::operator();
- using __domain::__transform_dependent_sender::operator();
-} transform_sender{};
-
-template <class _Domain, class _Sender, class... _Env>
-using transform_sender_result_t =
- __call_result_t<transform_sender_t, _Domain, _Sender, _Env...>;
-
-inline constexpr __domain::__transform_env transform_env{};
-
-struct _CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_
-{};
-
-template <sender_expr _Sender, class _Env>
- requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
-auto dependent_domain::transform_sender(_Sender&& __sndr,
- const _Env& __env) const
- -> decltype(auto)
-{
- // apply any algorithm-specific transformation to the environment
- const auto& __env2 = transform_env(*this, static_cast<_Sender&&>(__sndr),
- __env);
-
- // recursively transform the sender to determine the domain
- return __sexpr_apply(static_cast<_Sender&&>(__sndr),
- [&]<class _Tag, class _Data, class... _Childs>(
- _Tag, _Data&& __data, _Childs&&... __childs) {
- // TODO: propagate meta-exceptions here:
- auto __sndr2 = __make_sexpr<_Tag>(
- static_cast<_Data&&>(__data),
- __domain::__transform_sender()(
- *this, static_cast<_Childs&&>(__childs), __env2)...);
- using _Sender2 = decltype(__sndr2);
-
- auto __domain2 = __sexpr_apply(__sndr2, __domain::__common_domain_fn());
- using _Domain2 = decltype(__domain2);
-
- if constexpr (same_as<_Domain2, __none_such>)
- {
- return __mexception<_CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_,
- _WITH_SENDER_<_Sender2>>();
- }
- else
- {
- return __domain::__transform_sender()(__domain2, std::move(__sndr2),
- __env);
- }
- });
-}
-
-/////////////////////////////////////////////////////////////////////////////
-template <class _Tag, class _Domain, class _Sender, class... _Args>
-concept __has_implementation_for =
- __domain::__has_apply_sender<_Domain, _Tag, _Sender, _Args...> ||
- __domain::__has_apply_sender<default_domain, _Tag, _Sender, _Args...>;
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.apply_sender]
-inline constexpr struct apply_sender_t
-{
- template <class _Domain, class _Tag, class _Sender, class... _Args>
- requires __has_implementation_for<_Tag, _Domain, _Sender, _Args...>
- STDEXEC_ATTRIBUTE((always_inline))
- /*constexpr*/
- decltype(auto)
- operator()(_Domain __dom, _Tag, _Sender&& __sndr,
- _Args&&... __args) const
- {
- if constexpr (__domain::__has_apply_sender<_Domain, _Tag, _Sender,
- _Args...>)
- {
- return __dom.apply_sender(_Tag(), static_cast<_Sender&&>(__sndr),
- static_cast<_Args&&>(__args)...);
- }
- else
- {
- return default_domain().apply_sender(
- _Tag(), static_cast<_Sender&&>(__sndr),
- static_cast<_Args&&>(__args)...);
- }
- }
-} apply_sender{};
-
-template <class _Domain, class _Tag, class _Sender, class... _Args>
-using apply_sender_result_t =
- __call_result_t<apply_sender_t, _Domain, _Tag, _Sender, _Args...>;
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.sndtraits]
-namespace __get_completion_signatures
-{
-template <class _Sender, class _Env>
-using __tfx_sender =
- transform_sender_result_t<__late_domain_of_t<_Sender, _Env>, _Sender, _Env>;
-
-template <class _Sender, class _Env>
-concept __with_tag_invoke = //
- tag_invocable<get_completion_signatures_t, __tfx_sender<_Sender, _Env>,
- _Env>;
-
-template <class _Sender, class _Env>
-using __member_alias_t = //
- typename __decay_t<__tfx_sender<_Sender, _Env>>::completion_signatures;
-
-template <class _Sender, class _Env = empty_env>
-concept __with_member_alias = __mvalid<__member_alias_t, _Sender, _Env>;
-
-struct get_completion_signatures_t
-{
- template <class _Sender, class _Env>
- static auto __impl()
- {
- static_assert(sizeof(_Sender),
- "Incomplete type used with get_completion_signatures");
- static_assert(sizeof(_Env),
- "Incomplete type used with get_completion_signatures");
-
- // Compute the type of the transformed sender:
- using _TfxSender = __tfx_sender<_Sender, _Env>;
-
- if constexpr (__merror<_TfxSender>)
- {
- // Computing the type of the transformed sender returned an error
- // type. Propagate it.
- return static_cast<_TfxSender (*)()>(nullptr);
- }
- else if constexpr (__with_tag_invoke<_Sender, _Env>)
- {
- using _Result = tag_invoke_result_t<get_completion_signatures_t,
- _TfxSender, _Env>;
- return static_cast<_Result (*)()>(nullptr);
- }
- else if constexpr (__with_member_alias<_Sender, _Env>)
- {
- using _Result = __member_alias_t<_Sender, _Env>;
- return static_cast<_Result (*)()>(nullptr);
- }
- else if constexpr (__awaitable<_Sender, __env::__promise<_Env>>)
- {
- using _AwaitResult =
- __await_result_t<_Sender, __env::__promise<_Env>>;
- using _Result = completion_signatures<
- // set_value_t() or set_value_t(T)
- __minvoke<__remove<void, __qf<set_value_t>>, _AwaitResult>,
- set_error_t(std::exception_ptr), set_stopped_t()>;
- return static_cast<_Result (*)()>(nullptr);
- }
- else if constexpr (__is_debug_env<_Env>)
- {
- using __tag_invoke::tag_invoke;
- // This ought to cause a hard error that indicates where the problem
- // is.
- using _Completions [[maybe_unused]] =
- tag_invoke_result_t<get_completion_signatures_t, _Sender, _Env>;
- return static_cast<__debug::__completion_signatures (*)()>(nullptr);
- }
- else
- {
- using _Result =
- __mexception<_UNRECOGNIZED_SENDER_TYPE_<>,
- _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
- return static_cast<_Result (*)()>(nullptr);
- }
- }
-
- // NOT TO SPEC: if we're unable to compute the completion signatures,
- // return an error type instead of SFINAE.
- template <class _Sender, class _Env = empty_env>
- constexpr auto operator()(_Sender&&, const _Env&) const noexcept
- -> decltype(__impl<_Sender, _Env>()())
- {
- return {};
- }
-};
-} // namespace __get_completion_signatures
-
-using __get_completion_signatures::get_completion_signatures_t;
-inline constexpr get_completion_signatures_t get_completion_signatures{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders]
-struct sender_t
-{
- using sender_concept = sender_t;
-};
-
-namespace __detail
-{
-template <class _Sender>
-concept __enable_sender = //
- derived_from<typename _Sender::sender_concept, sender_t> ||
- requires { typename _Sender::is_sender; } // NOT TO SPEC back compat
- || __awaitable<_Sender, __env::__promise<empty_env>>;
-} // namespace __detail
-
-template <class _Sender>
-inline constexpr bool enable_sender = __detail::__enable_sender<_Sender>;
-
-template <class _Sender, class _Env = empty_env>
-concept sender = enable_sender<__decay_t<_Sender>> && //
- environment_provider<__cref_t<_Sender>> && //
- __detail::__consistent_completion_domains<_Sender> && //
- move_constructible<__decay_t<_Sender>> && //
- constructible_from<__decay_t<_Sender>, _Sender>;
-
-template <class _Sender, class _Env = empty_env>
-concept sender_in = //
- sender<_Sender> && //
- requires(_Sender&& __sndr, _Env&& __env) {
- {
- get_completion_signatures(static_cast<_Sender&&>(__sndr),
- static_cast<_Env&&>(__env))
- } -> __valid_completion_signatures;
- };
-
-#if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING()
-// __checked_completion_signatures is for catching logic bugs in a typed
-// sender's metadata. If sender<S> and sender_in<S, Ctx> are both true, then
-// they had better report the same metadata. This completion signatures wrapper
-// enforces that at compile time.
-template <class _Sender, class _Env>
-auto __checked_completion_signatures(_Sender&& __sndr,
- const _Env& __env) noexcept
-{
- using __completions_t = __completion_signatures_of_t<_Sender, _Env>;
- stdexec::__debug_sender<__completions_t>(static_cast<_Sender&&>(__sndr),
- __env);
- return __completions_t{};
-}
-
-template <class _Sender, class _Env = empty_env>
- requires sender_in<_Sender, _Env>
-using completion_signatures_of_t =
- decltype(stdexec::__checked_completion_signatures(__declval<_Sender>(),
- __declval<_Env>()));
-#else
-template <class _Sender, class _Env = empty_env>
- requires sender_in<_Sender, _Env>
-using completion_signatures_of_t = __completion_signatures_of_t<_Sender, _Env>;
-#endif
-
-struct __not_a_variant
-{
- __not_a_variant() = delete;
-};
-template <class... _Ts>
-using __variant = //
- __minvoke<__if_c<sizeof...(_Ts) != 0,
- __transform<__q<__decay_t>, __munique<__q<std::variant>>>,
- __mconst<__not_a_variant>>,
- _Ts...>;
-
-using __nullable_variant_t =
- __munique<__mbind_front_q<std::variant, std::monostate>>;
-
-template <class... _Ts>
-using __decayed_tuple = __meval<std::tuple, __decay_t<_Ts>...>;
-
-template <class _Tag, class _Tuple>
-struct __select_completions_for
-{
- template <same_as<_Tag> _Tag2, class... _Args>
- using __f = __minvoke<_Tag2, _Tuple, _Args...>;
-};
-
-template <class _Tuple>
-struct __invoke_completions
-{
- template <class _Tag, class... _Args>
- using __f = __minvoke<_Tag, _Tuple, _Args...>;
-};
-
-template <class _Tag, class _Tuple>
-using __select_completions_for_or = //
- __with_default<__select_completions_for<_Tag, _Tuple>, __>;
-
-template <class _Tag, class _Completions>
-using __only_gather_signal = //
- __compl_sigs::__maybe_for_all_sigs<
- _Completions, __select_completions_for_or<_Tag, __qf<_Tag>>,
- __remove<__, __q<completion_signatures>>>;
-
-template <class _Tag, class _Completions, class _Tuple, class _Variant>
-using __gather_signal = //
- __compl_sigs::__maybe_for_all_sigs<__only_gather_signal<_Tag, _Completions>,
- __invoke_completions<_Tuple>, _Variant>;
-
-template <class _Tag, class _Sender, class _Env, class _Tuple, class _Variant>
-using __gather_completions_for = //
- __meval< //
- __gather_signal, _Tag, __completion_signatures_of_t<_Sender, _Env>,
- _Tuple, _Variant>;
-
-template < //
- class _Sender, //
- class _Env = empty_env, //
- class _Tuple = __q<__decayed_tuple>, //
- class _Variant = __q<__variant>>
-using __try_value_types_of_t = //
- __gather_completions_for<set_value_t, _Sender, _Env, _Tuple, _Variant>;
-
-template < //
- class _Sender, //
- class _Env = empty_env, //
- class _Tuple = __q<__decayed_tuple>, //
- class _Variant = __q<__variant>>
- requires sender_in<_Sender, _Env>
-using __value_types_of_t = //
- __msuccess_or_t<__try_value_types_of_t<_Sender, _Env, _Tuple, _Variant>>;
-
-template <class _Sender, class _Env = empty_env,
- class _Variant = __q<__variant>>
-using __try_error_types_of_t =
- __gather_completions_for<set_error_t, _Sender, _Env, __q<__midentity>,
- _Variant>;
-
-template <class _Sender, class _Env = empty_env,
- class _Variant = __q<__variant>>
- requires sender_in<_Sender, _Env>
-using __error_types_of_t =
- __msuccess_or_t<__try_error_types_of_t<_Sender, _Env, _Variant>>;
-
-template < //
- class _Sender, //
- class _Env = empty_env, //
- template <class...> class _Tuple = __decayed_tuple, //
- template <class...> class _Variant = __variant>
- requires sender_in<_Sender, _Env>
-using value_types_of_t =
- __value_types_of_t<_Sender, _Env, __q<_Tuple>, __q<_Variant>>;
-
-template <class _Sender, class _Env = empty_env,
- template <class...> class _Variant = __variant>
- requires sender_in<_Sender, _Env>
-using error_types_of_t = __error_types_of_t<_Sender, _Env, __q<_Variant>>;
-
-template <class _Tag, class _Sender, class _Env = empty_env>
-using __try_count_of = //
- __compl_sigs::__maybe_for_all_sigs<
- __completion_signatures_of_t<_Sender, _Env>, __q<__mfront>,
- __mcount<_Tag>>;
-
-template <class _Tag, class _Sender, class _Env = empty_env>
- requires sender_in<_Sender, _Env>
-using __count_of = __msuccess_or_t<__try_count_of<_Tag, _Sender, _Env>>;
-
-template <class _Tag, class _Sender, class _Env = empty_env>
- requires __mvalid<__count_of, _Tag, _Sender, _Env>
-inline constexpr bool __sends = (__v<__count_of<_Tag, _Sender, _Env>> != 0);
-
-template <class _Sender, class _Env = empty_env>
- requires __mvalid<__count_of, set_stopped_t, _Sender, _Env>
-inline constexpr bool sends_stopped = __sends<set_stopped_t, _Sender, _Env>;
-
-template <class _Sender, class _Env = empty_env>
-using __single_sender_value_t =
- __value_types_of_t<_Sender, _Env, __msingle_or<void>, __q<__msingle>>;
-
-template <class _Sender, class _Env = empty_env>
-using __single_value_variant_sender_t =
- value_types_of_t<_Sender, _Env, __types, __msingle>;
-
-template <class _Sender, class _Env = empty_env>
-concept __single_typed_sender =
- sender_in<_Sender, _Env> &&
- __mvalid<__single_sender_value_t, _Sender, _Env>;
-
-template <class _Sender, class _Env = empty_env>
-concept __single_value_variant_sender =
- sender_in<_Sender, _Env> &&
- __mvalid<__single_value_variant_sender_t, _Sender, _Env>;
-
-template <class... Errs>
-using __nofail = __mbool<sizeof...(Errs) == 0>;
-
-template <class _Sender, class _Env = empty_env>
-concept __nofail_sender = sender_in<_Sender, _Env> &&
- (__v<error_types_of_t<_Sender, _Env, __nofail>>);
-
-/////////////////////////////////////////////////////////////////////////////
-namespace __compl_sigs
-{
-template <class... _Args>
-using __default_set_value = completion_signatures<set_value_t(_Args...)>;
-
-template <class _Error>
-using __default_set_error = completion_signatures<set_error_t(_Error)>;
-
-template <__valid_completion_signatures... _Sigs>
-using __ensure_concat_ =
- __minvoke<__mconcat<__q<completion_signatures>>, _Sigs...>;
-
-template <class... _Sigs>
-using __ensure_concat = __mtry_eval<__ensure_concat_, _Sigs...>;
-
-template <class _Sender, class _Env, class _Sigs, class _SetVal, class _SetErr,
- class _SetStp>
-using __compl_sigs_impl = //
- __concat_completion_signatures_t<
- _Sigs,
- __mtry_eval<__try_value_types_of_t, _Sender, _Env, _SetVal,
- __q<__ensure_concat>>,
- __mtry_eval<__try_error_types_of_t, _Sender, _Env,
- __transform<_SetErr, __q<__ensure_concat>>>,
- __if<__try_count_of<set_stopped_t, _Sender, _Env>, _SetStp,
- completion_signatures<>>>;
-
-template <class _Sender, class _Env, class _Sigs, class _SetVal, class _SetErr,
- class _SetStp>
- requires __mvalid<__compl_sigs_impl, _Sender, _Env, _Sigs, _SetVal, _SetErr,
- _SetStp>
-extern __compl_sigs_impl<_Sender, _Env, _Sigs, _SetVal, _SetErr, _SetStp>
- __compl_sigs_v;
-
-template <class _Sender, class _Env, class _Sigs, class _SetVal, class _SetErr,
- class _SetStp>
-using __compl_sigs_t =
- decltype(__compl_sigs_v<_Sender, _Env, _Sigs, _SetVal, _SetErr, _SetStp>);
-
-template < //
- class _Sender, //
- class _Env = empty_env, //
- class _Sigs = completion_signatures<>, //
- class _SetValue = __q<__default_set_value>, //
- class _SetError = __q<__default_set_error>, //
- class _SetStopped = completion_signatures<set_stopped_t()>> //
-using __try_make_completion_signatures = //
- __meval<__compl_sigs_t, _Sender, _Env, _Sigs, _SetValue, _SetError,
- _SetStopped>;
-} // namespace __compl_sigs
-
-using __compl_sigs::__try_make_completion_signatures;
-
-/////////////////////////////////////////////////////////////////////////////
-// NOT TO SPEC
-//
-// make_completion_signatures
-// ==========================
-//
-// `make_completion_signatures` takes a sender, and environment, and a bunch
-// of other template arguments for munging the completion signatures of a
-// sender in interesting ways.
-//
-// ```c++
-// template <class... Args>
-// using __default_set_value = completion_signatures<set_value_t(Args...)>;
-//
-// template <class Err>
-// using __default_set_error = completion_signatures<set_error_t(Err)>;
-//
-// template <
-// sender Sndr,
-// class Env = empty_env,
-// class AddlSigs = completion_signatures<>,
-// template <class...> class SetValue = __default_set_value,
-// template <class> class SetError = __default_set_error,
-// class SetStopped = completion_signatures<set_stopped_t()>>
-// requires sender_in<Sndr, Env>
-// using make_completion_signatures =
-// completion_signatures< ... >;
-// ```
-//
-// * `SetValue` : an alias template that accepts a set of value types and
-// returns an instance of `completion_signatures`.
-// * `SetError` : an alias template that accepts an error types and returns a
-// an instance of `completion_signatures`.
-// * `SetStopped` : an instantiation of `completion_signatures` with a list
-// of completion signatures `Sigs...` to the added to the list if the
-// sender can complete with a stopped signal.
-// * `AddlSigs` : an instantiation of `completion_signatures` with a list of
-// completion signatures `Sigs...` to the added to the list
-// unconditionally.
-//
-// `make_completion_signatures` does the following:
-// * Let `VCs...` be a pack of the `completion_signatures` types in the
-// `__typelist` named by `value_types_of_t<Sndr, Env, SetValue,
-// __typelist>`, and let `Vs...` be the concatenation of the packs that are
-// template arguments to each `completion_signature` in `VCs...`.
-// * Let `ECs...` be a pack of the `completion_signatures` types in the
-// `__typelist` named by `error_types_of_t<Sndr, Env, __errorlist>`, where
-// `__errorlist` is an alias template such that `__errorlist<Ts...>` names
-// `__typelist<SetError<Ts>...>`, and let `Es...` by the concatenation of
-// the packs that are the template arguments to each `completion_signature`
-// in `ECs...`.
-// * Let `Ss...` be an empty pack if `sends_stopped<Sndr, Env>` is
-// `false`; otherwise, a pack containing the template arguments of the
-// `completion_signatures` instantiation named by `SetStopped`.
-// * Let `MoreSigs...` be a pack of the template arguments of the
-// `completion_signatures` instantiation named by `AddlSigs`.
-//
-// Then `make_completion_signatures<Sndr, Env, AddlSigs, SetValue, SetError,
-// SendsStopped>` names the type `completion_signatures< Sigs... >` where
-// `Sigs...` is the unique set of types in `[Vs..., Es..., Ss...,
-// MoreSigs...]`.
-//
-// If any of the above type computations are ill-formed,
-// `make_completion_signatures<Sndr, Env, AddlSigs, SetValue, SetError,
-// SendsStopped>` is an alias for an empty struct
-template < //
- class _Sender, //
- class _Env = empty_env, //
- __valid_completion_signatures _Sigs = completion_signatures<>, //
- template <class...> class _SetValue = __compl_sigs::__default_set_value, //
- template <class> class _SetError = __compl_sigs::__default_set_error, //
- __valid_completion_signatures _SetStopped =
- completion_signatures<set_stopped_t()>>
- requires sender_in<_Sender, _Env>
-using make_completion_signatures = //
- __msuccess_or_t< //
- __try_make_completion_signatures<_Sender, _Env, _Sigs, __q<_SetValue>,
- __q<_SetError>, _SetStopped>>;
-
-// Needed fairly often
-using __with_exception_ptr =
- completion_signatures<set_error_t(std::exception_ptr)>;
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.schedule]
-namespace __schedule
-{
-struct schedule_t
-{
- template <class _Scheduler>
- requires tag_invocable<schedule_t, _Scheduler>
- STDEXEC_ATTRIBUTE((host, device)) auto
- operator()(_Scheduler&& __sched) const
- noexcept(nothrow_tag_invocable<schedule_t, _Scheduler>)
- {
- static_assert(sender<tag_invoke_result_t<schedule_t, _Scheduler>>);
- return tag_invoke(schedule_t{}, static_cast<_Scheduler&&>(__sched));
- }
-
- friend constexpr auto tag_invoke(forwarding_query_t, schedule_t) -> bool
- {
- return false;
- }
-};
-} // namespace __schedule
-
-using __schedule::schedule_t;
-inline constexpr schedule_t schedule{};
-
-// NOT TO SPEC
-template <class _Tag, const auto& _Predicate>
-concept tag_category = //
- requires {
- typename __mbool<bool{_Predicate(_Tag{})}>;
- requires bool {
- _Predicate(_Tag{})
- };
- };
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.schedulers]
-template <class _Scheduler>
-concept __has_schedule = //
- requires(_Scheduler&& __sched) {
- {
- schedule(static_cast<_Scheduler&&>(__sched))
- } -> sender;
- };
-
-template <class _Scheduler>
-concept __sender_has_completion_scheduler =
- requires(_Scheduler&& __sched,
- get_completion_scheduler_t<set_value_t>&& __tag) {
- {
- tag_invoke(std::move(__tag),
- get_env(schedule(static_cast<_Scheduler&&>(__sched))))
- } -> same_as<__decay_t<_Scheduler>>;
- };
-
-template <class _Scheduler>
-concept scheduler = //
- __has_schedule<_Scheduler> && //
- __sender_has_completion_scheduler<_Scheduler> && //
- equality_comparable<__decay_t<_Scheduler>> && //
- copy_constructible<__decay_t<_Scheduler>>;
-
-template <scheduler _Scheduler>
-using schedule_result_t = __call_result_t<schedule_t, _Scheduler>;
-
-template <receiver _Receiver>
-using __current_scheduler_t =
- __call_result_t<get_scheduler_t, env_of_t<_Receiver>>;
-
-template <class _SchedulerProvider>
-concept __scheduler_provider = //
- requires(const _SchedulerProvider& __sp) {
- {
- get_scheduler(__sp)
- } -> scheduler;
- };
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.op_state]
-namespace __start
-{
-struct start_t
-{
- template <class _Op>
- requires tag_invocable<start_t, _Op&>
- STDEXEC_ATTRIBUTE((always_inline)) void operator()(_Op& __op) const noexcept
- {
- static_assert(nothrow_tag_invocable<start_t, _Op&>);
- (void)tag_invoke(start_t{}, __op);
- }
-};
-} // namespace __start
-
-using __start::start_t;
-inline constexpr start_t start{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.op_state]
-template <class _Op>
-concept operation_state = //
- destructible<_Op> && //
- std::is_object_v<_Op> && //
- requires(_Op& __op) { //
- start(__op);
- };
-
-#if !STDEXEC_STD_NO_COROUTINES_
-/////////////////////////////////////////////////////////////////////////////
-// __connect_awaitable_
-namespace __connect_awaitable_
-{
-struct __promise_base
-{
- auto initial_suspend() noexcept -> __coro::suspend_always
- {
- return {};
- }
-
- [[noreturn]] auto final_suspend() noexcept -> __coro::suspend_always
- {
- std::terminate();
- }
-
- [[noreturn]] void unhandled_exception() noexcept
- {
- std::terminate();
- }
-
- [[noreturn]] void return_void() noexcept
- {
- std::terminate();
- }
-};
-
-struct __operation_base
-{
- __coro::coroutine_handle<> __coro_;
-
- explicit __operation_base(__coro::coroutine_handle<> __hcoro) noexcept :
- __coro_(__hcoro)
- {}
-
- __operation_base(__operation_base&& __other) noexcept :
- __coro_(std::exchange(__other.__coro_, {}))
- {}
-
- ~__operation_base()
- {
- if (__coro_)
- {
-#if STDEXEC_MSVC()
- // MSVCBUG
- // https://developercommunity.visualstudio.com/t/Double-destroy-of-a-local-in-coroutine-d/10456428
-
- // Reassign __coro_ before calling destroy to make the mutation
- // observable and to hopefully ensure that the compiler does not
- // eliminate it.
- auto __coro = __coro_;
- __coro_ = {};
- __coro.destroy();
-#else
- __coro_.destroy();
-#endif
- }
- }
-
- friend void tag_invoke(start_t, __operation_base& __self) noexcept
- {
- __self.__coro_.resume();
- }
-};
-
-template <class _ReceiverId>
-struct __promise;
-
-template <class _ReceiverId>
-struct __operation
-{
- struct __t : __operation_base
- {
- using promise_type = stdexec::__t<__promise<_ReceiverId>>;
- using __operation_base::__operation_base;
- };
-};
-
-template <class _ReceiverId>
-struct __promise
-{
- using _Receiver = stdexec::__t<_ReceiverId>;
-
- struct __t : __promise_base
- {
- using __id = __promise;
-
- explicit __t(auto&, _Receiver& __rcvr) noexcept : __rcvr_(__rcvr) {}
-
- auto unhandled_stopped() noexcept -> __coro::coroutine_handle<>
- {
- set_stopped(static_cast<_Receiver&&>(__rcvr_));
- // Returning noop_coroutine here causes the __connect_awaitable
- // coroutine to never resume past the point where it co_await's
- // the awaitable.
- return __coro::noop_coroutine();
- }
-
- auto get_return_object() noexcept
- -> stdexec::__t<__operation<_ReceiverId>>
- {
- return stdexec::__t<__operation<_ReceiverId>>{
- __coro::coroutine_handle<__t>::from_promise(*this)};
- }
-
- template <class _Awaitable>
- auto await_transform(_Awaitable&& __awaitable) noexcept -> _Awaitable&&
- {
- return static_cast<_Awaitable&&>(__awaitable);
- }
-
- template <class _Awaitable>
- requires tag_invocable<as_awaitable_t, _Awaitable, __t&>
- auto await_transform(_Awaitable&& __awaitable) //
- noexcept(nothrow_tag_invocable<as_awaitable_t, _Awaitable, __t&>)
- -> tag_invoke_result_t<as_awaitable_t, _Awaitable, __t&>
- {
- return tag_invoke(as_awaitable,
- static_cast<_Awaitable&&>(__awaitable), *this);
- }
-
- // Pass through the get_env receiver query
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> env_of_t<_Receiver>
- {
- return get_env(__self.__rcvr_);
- }
-
- _Receiver& __rcvr_;
- };
-};
-
-template <receiver _Receiver>
-using __promise_t = __t<__promise<__id<_Receiver>>>;
-
-template <receiver _Receiver>
-using __operation_t = __t<__operation<__id<_Receiver>>>;
-
-struct __connect_awaitable_t
-{
- private:
- template <class _Fun, class... _Ts>
- static auto __co_call(_Fun __fun, _Ts&&... __as) noexcept
- {
- auto __fn = [&, __fun]() noexcept {
- __fun(static_cast<_Ts&&>(__as)...);
- };
-
- struct __awaiter
- {
- decltype(__fn) __fn_;
-
- static constexpr auto await_ready() noexcept -> bool
- {
- return false;
- }
-
- void await_suspend(__coro::coroutine_handle<>) noexcept
- {
- __fn_();
- }
-
- [[noreturn]] void await_resume() noexcept
- {
- std::terminate();
- }
- };
-
- return __awaiter{__fn};
- }
-
- template <class _Awaitable, class _Receiver>
-#if STDEXEC_GCC() && (__GNUC__ > 11)
- __attribute__((__used__))
-#endif
- static auto
- __co_impl(_Awaitable __awaitable, _Receiver __rcvr)
- -> __operation_t<_Receiver>
- {
- using __result_t = __await_result_t<_Awaitable, __promise_t<_Receiver>>;
- std::exception_ptr __eptr;
- try
- {
- if constexpr (same_as<__result_t, void>)
- co_await (
- co_await static_cast<_Awaitable&&>(__awaitable),
- __co_call(set_value, static_cast<_Receiver&&>(__rcvr)));
- else
- co_await __co_call(
- set_value, static_cast<_Receiver&&>(__rcvr),
- co_await static_cast<_Awaitable&&>(__awaitable));
- }
- catch (...)
- {
- __eptr = std::current_exception();
- }
- co_await __co_call(set_error, static_cast<_Receiver&&>(__rcvr),
- static_cast<std::exception_ptr&&>(__eptr));
- }
-
- template <receiver _Receiver, class _Awaitable>
- using __completions_t = //
- completion_signatures<
- __minvoke< // set_value_t() or set_value_t(T)
- __remove<void, __qf<set_value_t>>,
- __await_result_t<_Awaitable, __promise_t<_Receiver>>>,
- set_error_t(std::exception_ptr), set_stopped_t()>;
-
- public:
- template <class _Receiver, __awaitable<__promise_t<_Receiver>> _Awaitable>
- requires receiver_of<_Receiver, __completions_t<_Receiver, _Awaitable>>
- auto operator()(_Awaitable&& __awaitable, _Receiver __rcvr) const
- -> __operation_t<_Receiver>
- {
- return __co_impl(static_cast<_Awaitable&&>(__awaitable),
- static_cast<_Receiver&&>(__rcvr));
- }
-};
-} // namespace __connect_awaitable_
-
-using __connect_awaitable_::__connect_awaitable_t;
-#else
-struct __connect_awaitable_t
-{};
-#endif
-inline constexpr __connect_awaitable_t __connect_awaitable{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.connect]
-namespace __connect
-{
-struct connect_t;
-
-template <class _Sender, class _Receiver>
-using __tfx_sender = //
- transform_sender_result_t<__late_domain_of_t<_Sender, env_of_t<_Receiver&>>,
- _Sender, env_of_t<_Receiver&>>;
-
-template <class _Sender, class _Receiver>
-concept __connectable_with_tag_invoke_ = //
- receiver<_Receiver> && //
- sender_in<_Sender, env_of_t<_Receiver>> && //
- __receiver_from<_Receiver, _Sender> && //
- tag_invocable<connect_t, _Sender, _Receiver>;
-
-template <class _Sender, class _Receiver>
-concept __connectable_with_tag_invoke = //
- __connectable_with_tag_invoke_<__tfx_sender<_Sender, _Receiver>, _Receiver>;
-
-template <class _Sender, class _Receiver>
-concept __connectable_with_co_await = //
- __callable<__connect_awaitable_t, __tfx_sender<_Sender, _Receiver>,
- _Receiver>;
-
-struct connect_t
-{
- template <class _Sender, class _Env>
- static constexpr auto __check_signatures() -> bool
- {
- if constexpr (sender_in<_Sender, _Env>)
- {
- // Instantiate __debug_sender via completion_signatures_of_t
- // to check that the actual completions match the expected
- // completions.
- //
- // Instantiate completion_signatures_of_t only if sender_in
- // is true to workaround Clang not implementing CWG#2369 yet
- // (connect() does have a constraint for _Sender satisfying
- // sender_in).
- using __checked_signatures
- [[maybe_unused]] = completion_signatures_of_t<_Sender, _Env>;
- }
- return true;
- }
-
- template <class _Sender, class _Receiver>
- static constexpr auto __select_impl() noexcept
- {
- using _Domain = __late_domain_of_t<_Sender, env_of_t<_Receiver&>>;
- constexpr bool _NothrowTfxSender =
- __nothrow_callable<get_env_t, _Receiver&> &&
- __nothrow_callable<transform_sender_t, _Domain, _Sender,
- env_of_t<_Receiver&>>;
- using _TfxSender = __tfx_sender<_Sender, _Receiver&>;
-
-#if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING()
- static_assert(__check_signatures<_TfxSender, env_of_t<_Receiver>>());
-#endif
-
- if constexpr (__connectable_with_tag_invoke<_Sender, _Receiver>)
- {
- using _Result =
- tag_invoke_result_t<connect_t, _TfxSender, _Receiver>;
- constexpr bool _Nothrow = //
- _NothrowTfxSender &&
- nothrow_tag_invocable<connect_t, _TfxSender, _Receiver>;
- return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
- }
- else if constexpr (__connectable_with_co_await<_Sender, _Receiver>)
- {
- using _Result =
- __call_result_t<__connect_awaitable_t, _TfxSender, _Receiver>;
- return static_cast<_Result (*)()>(nullptr);
- }
- else
- {
- using _Result = __debug::__debug_operation;
- return static_cast<_Result (*)() noexcept(_NothrowTfxSender)>(
- nullptr);
- }
- }
-
- template <class _Sender, class _Receiver>
- using __select_impl_t = decltype(__select_impl<_Sender, _Receiver>());
-
- template <sender _Sender, receiver _Receiver>
- requires __connectable_with_tag_invoke<_Sender, _Receiver> ||
- __connectable_with_co_await<_Sender, _Receiver> ||
- __is_debug_env<env_of_t<_Receiver>>
- auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const
- noexcept(__nothrow_callable<__select_impl_t<_Sender, _Receiver>>)
- -> __call_result_t<__select_impl_t<_Sender, _Receiver>>
- {
- using _TfxSender = __tfx_sender<_Sender, _Receiver&>;
- auto&& __env = get_env(__rcvr);
- auto __domain = __get_late_domain(__sndr, __env);
-
- if constexpr (__connectable_with_tag_invoke<_Sender, _Receiver>)
- {
- static_assert(
- operation_state<
- tag_invoke_result_t<connect_t, _TfxSender, _Receiver>>,
- "stdexec::connect(sender, receiver) must return a type that "
- "satisfies the operation_state concept");
- return tag_invoke(connect_t{},
- transform_sender(__domain,
- static_cast<_Sender&&>(__sndr),
- __env),
- static_cast<_Receiver&&>(__rcvr));
- }
- else if constexpr (__connectable_with_co_await<_Sender, _Receiver>)
- {
- return __connect_awaitable( //
- transform_sender(__domain, static_cast<_Sender&&>(__sndr),
- __env),
- static_cast<_Receiver&&>(__rcvr));
- }
- else
- {
- // This should generate an instantiation backtrace that contains
- // useful debugging information.
- using __tag_invoke::tag_invoke;
- tag_invoke(*this,
- transform_sender(__domain,
- static_cast<_Sender&&>(__sndr), __env),
- static_cast<_Receiver&&>(__rcvr));
- }
- }
-
- friend constexpr auto tag_invoke(forwarding_query_t, connect_t) noexcept
- -> bool
- {
- return false;
- }
-};
-} // namespace __connect
-
-using __connect::connect_t;
-inline constexpr __connect::connect_t connect{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [exec.snd]
-template <class _Sender, class _Receiver>
-concept sender_to = receiver<_Receiver> && //
- sender_in<_Sender, env_of_t<_Receiver>> && //
- __receiver_from<_Receiver, _Sender> && //
- requires(_Sender&& __sndr, _Receiver&& __rcvr) {
- connect(static_cast<_Sender&&>(__sndr),
- static_cast<_Receiver&&>(__rcvr));
- };
-
-template <class _Tag, class... _Args>
-auto __tag_of_sig_(_Tag (*)(_Args...)) -> _Tag;
-template <class _Sig>
-using __tag_of_sig_t =
- decltype(stdexec::__tag_of_sig_(static_cast<_Sig*>(nullptr)));
-
-template <class _Sender, class _SetSig, class _Env = empty_env>
-concept sender_of =
- sender_in<_Sender, _Env> &&
- same_as<__types<_SetSig>, __gather_completions_for<
- __tag_of_sig_t<_SetSig>, _Sender, _Env,
- __qf<__tag_of_sig_t<_SetSig>>, __q<__types>>>;
-
-#if !STDEXEC_STD_NO_COROUTINES_
-/////////////////////////////////////////////////////////////////////////////
-// stdexec::as_awaitable [execution.coro_utils.as_awaitable]
-namespace __as_awaitable
-{
-struct __void
-{};
-template <class _Value>
-using __value_or_void_t = __if<std::is_same<_Value, void>, __void, _Value>;
-template <class _Value>
-using __expected_t =
- std::variant<std::monostate, __value_or_void_t<_Value>, std::exception_ptr>;
-
-template <class _Value>
-struct __receiver_base
-{
- using receiver_concept = receiver_t;
-
- template <same_as<set_value_t> _Tag, class... _Us>
- requires constructible_from<__value_or_void_t<_Value>, _Us...>
- friend void tag_invoke(_Tag, __receiver_base&& __self,
- _Us&&... __us) noexcept
- {
- try
- {
- __self.__result_->template emplace<1>(static_cast<_Us&&>(__us)...);
- __self.__continuation_.resume();
- }
- catch (...)
- {
- set_error(static_cast<__receiver_base&&>(__self),
- std::current_exception());
- }
- }
-
- template <same_as<set_error_t> _Tag, class _Error>
- friend void tag_invoke(_Tag, __receiver_base&& __self,
- _Error&& __err) noexcept
- {
- if constexpr (__decays_to<_Error, std::exception_ptr>)
- __self.__result_->template emplace<2>(static_cast<_Error&&>(__err));
- else if constexpr (__decays_to<_Error, std::error_code>)
- __self.__result_->template emplace<2>(
- std::make_exception_ptr(std::system_error(__err)));
- else
- __self.__result_->template emplace<2>(
- std::make_exception_ptr(static_cast<_Error&&>(__err)));
- __self.__continuation_.resume();
- }
-
- __expected_t<_Value>* __result_;
- __coro::coroutine_handle<> __continuation_;
-};
-
-template <class _PromiseId, class _Value>
-struct __receiver
-{
- using _Promise = stdexec::__t<_PromiseId>;
-
- struct __t : __receiver_base<_Value>
- {
- using __id = __receiver;
-
- template <same_as<set_stopped_t> _Tag>
- friend void tag_invoke(_Tag, __t&& __self) noexcept
- {
- auto __continuation =
- __coro::coroutine_handle<_Promise>::from_address(
- __self.__continuation_.address());
- __coro::coroutine_handle<> __stopped_continuation =
- __continuation.promise().unhandled_stopped();
- __stopped_continuation.resume();
- }
-
- // Forward get_env query to the coroutine promise
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> env_of_t<_Promise&>
- {
- auto __continuation =
- __coro::coroutine_handle<_Promise>::from_address(
- __self.__continuation_.address());
- return get_env(__continuation.promise());
- }
- };
-};
-
-// BUGBUG NOT TO SPEC: make senders of more-than-one-value awaitable
-// by packaging the values into a tuple.
-// See: https://github.com/cplusplus/sender-receiver/issues/182
-template <std::size_t _Count>
-extern const __q<__decayed_tuple> __as_single;
-
-template <>
-inline const __q<__midentity> __as_single<1>;
-
-template <>
-inline const __mconst<void> __as_single<0>;
-
-template <class... _Values>
-using __single_value =
- __minvoke<decltype(__as_single<sizeof...(_Values)>), _Values...>;
-
-template <class _Sender, class _Promise>
-using __value_t =
- __decay_t<__value_types_of_t<_Sender, env_of_t<_Promise&>,
- __q<__single_value>, __msingle_or<void>>>;
-
-template <class _Sender, class _Promise>
-using __receiver_t =
- __t<__receiver<__id<_Promise>, __value_t<_Sender, _Promise>>>;
-
-template <class _Value>
-struct __sender_awaitable_base
-{
- [[nodiscard]] auto await_ready() const noexcept -> bool
- {
- return false;
- }
-
- auto await_resume() -> _Value
- {
- switch (__result_.index())
- {
- case 0: // receiver contract not satisfied
- STDEXEC_ASSERT(!"_Should never get here");
- break;
- case 1: // set_value
- if constexpr (!std::is_void_v<_Value>)
- return static_cast<_Value&&>(std::get<1>(__result_));
- else
- return;
- case 2: // set_error
- std::rethrow_exception(std::get<2>(__result_));
- }
- std::terminate();
- }
-
- protected:
- __expected_t<_Value> __result_;
-};
-
-template <class _PromiseId, class _SenderId>
-struct __sender_awaitable
-{
- using _Promise = stdexec::__t<_PromiseId>;
- using _Sender = stdexec::__t<_SenderId>;
- using __value = __value_t<_Sender, _Promise>;
-
- struct __t : __sender_awaitable_base<__value>
- {
- __t(_Sender&& sndr, __coro::coroutine_handle<_Promise> __hcoro) //
- noexcept(__nothrow_connectable<_Sender, __receiver>) :
- __op_state_(connect(static_cast<_Sender&&>(sndr),
- __receiver{{&this->__result_, __hcoro}}))
- {}
-
- void await_suspend(__coro::coroutine_handle<_Promise>) noexcept
- {
- start(__op_state_);
- }
-
- private:
- using __receiver = __receiver_t<_Sender, _Promise>;
- connect_result_t<_Sender, __receiver> __op_state_;
- };
-};
-
-template <class _Promise, class _Sender>
-using __sender_awaitable_t =
- __t<__sender_awaitable<__id<_Promise>, __id<_Sender>>>;
-
-template <class _Sender, class _Promise>
-concept __awaitable_sender =
- sender_in<_Sender, env_of_t<_Promise&>> && //
- __mvalid<__value_t, _Sender, _Promise> && //
- sender_to<_Sender, __receiver_t<_Sender, _Promise>> && //
- requires(_Promise& __promise) {
- {
- __promise.unhandled_stopped()
- } -> convertible_to<__coro::coroutine_handle<>>;
- };
-
-struct __unspecified
-{
- auto get_return_object() noexcept -> __unspecified;
- auto initial_suspend() noexcept -> __unspecified;
- auto final_suspend() noexcept -> __unspecified;
- void unhandled_exception() noexcept;
- void return_void() noexcept;
- auto unhandled_stopped() noexcept -> __coro::coroutine_handle<>;
-};
-
-struct as_awaitable_t
-{
- template <class _Tp, class _Promise>
- static constexpr auto __select_impl_() noexcept
- {
- if constexpr (tag_invocable<as_awaitable_t, _Tp, _Promise&>)
- {
- using _Result = tag_invoke_result_t<as_awaitable_t, _Tp, _Promise&>;
- constexpr bool _Nothrow =
- nothrow_tag_invocable<as_awaitable_t, _Tp, _Promise&>;
- return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
- // NOLINTNEXTLINE(bugprone-branch-clone)
- }
- else if constexpr (__awaitable<_Tp, __unspecified>)
- { // NOT __awaitable<_Tp, _Promise> !!
- using _Result = _Tp&&;
- return static_cast<_Result (*)() noexcept>(nullptr);
- }
- else if constexpr (__awaitable_sender<_Tp, _Promise>)
- {
- using _Result = __sender_awaitable_t<_Promise, _Tp>;
- constexpr bool _Nothrow = __nothrow_constructible_from<
- _Result, _Tp, __coro::coroutine_handle<_Promise>>;
- return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
- }
- else
- {
- using _Result = _Tp&&;
- return static_cast<_Result (*)() noexcept>(nullptr);
- }
- }
- template <class _Tp, class _Promise>
- using __select_impl_t = decltype(__select_impl_<_Tp, _Promise>());
-
- template <class _Tp, class _Promise>
- auto operator()(_Tp&& __t, _Promise& __promise) const
- noexcept(__nothrow_callable<__select_impl_t<_Tp, _Promise>>)
- -> __call_result_t<__select_impl_t<_Tp, _Promise>>
- {
- if constexpr (tag_invocable<as_awaitable_t, _Tp, _Promise&>)
- {
- using _Result = tag_invoke_result_t<as_awaitable_t, _Tp, _Promise&>;
- static_assert(__awaitable<_Result, _Promise>);
- return tag_invoke(*this, static_cast<_Tp&&>(__t), __promise);
- // NOLINTNEXTLINE(bugprone-branch-clone)
- }
- else if constexpr (__awaitable<_Tp, __unspecified>)
- { // NOT __awaitable<_Tp, _Promise> !!
- return static_cast<_Tp&&>(__t);
- }
- else if constexpr (__awaitable_sender<_Tp, _Promise>)
- {
- auto __hcoro =
- __coro::coroutine_handle<_Promise>::from_promise(__promise);
- return __sender_awaitable_t<_Promise, _Tp>{static_cast<_Tp&&>(__t),
- __hcoro};
- }
- else
- {
- return static_cast<_Tp&&>(__t);
- }
- }
-};
-} // namespace __as_awaitable
-
-using __as_awaitable::as_awaitable_t;
-inline constexpr as_awaitable_t as_awaitable{};
-
-namespace __with_awaitable_senders
-{
-
-template <class _Promise = void>
-class __continuation_handle;
-
-template <>
-class __continuation_handle<void>
-{
- public:
- __continuation_handle() = default;
-
- template <class _Promise>
- __continuation_handle(__coro::coroutine_handle<_Promise> __coro) noexcept :
- __coro_(__coro)
- {
- if constexpr (requires(_Promise& __promise) {
- __promise.unhandled_stopped();
- })
- {
- __stopped_callback_ =
- [](void* __address) noexcept -> __coro::coroutine_handle<> {
- // This causes the rest of the coroutine (the part after the
- // co_await of the sender) to be skipped and invokes the calling
- // coroutine's stopped handler.
- return __coro::coroutine_handle<_Promise>::from_address(
- __address)
- .promise()
- .unhandled_stopped();
- };
- }
- // If _Promise doesn't implement unhandled_stopped(), then if a
- // "stopped" unwind reaches this point, it's considered an unhandled
- // exception and terminate() is called.
- }
-
- [[nodiscard]] auto handle() const noexcept -> __coro::coroutine_handle<>
- {
- return __coro_;
- }
-
- [[nodiscard]] auto unhandled_stopped() const noexcept
- -> __coro::coroutine_handle<>
- {
- return __stopped_callback_(__coro_.address());
- }
-
- private:
- __coro::coroutine_handle<> __coro_{};
- using __stopped_callback_t = __coro::coroutine_handle<> (*)(void*) noexcept;
- __stopped_callback_t __stopped_callback_ =
- [](void*) noexcept -> __coro::coroutine_handle<> { std::terminate(); };
-};
-
-template <class _Promise>
-class __continuation_handle
-{
- public:
- __continuation_handle() = default;
-
- __continuation_handle(__coro::coroutine_handle<_Promise> __coro) noexcept :
- __continuation_{__coro}
- {}
-
- auto handle() const noexcept -> __coro::coroutine_handle<_Promise>
- {
- return __coro::coroutine_handle<_Promise>::from_address(
- __continuation_.handle().address());
- }
-
- [[nodiscard]] auto unhandled_stopped() const noexcept
- -> __coro::coroutine_handle<>
- {
- return __continuation_.unhandled_stopped();
- }
-
- private:
- __continuation_handle<> __continuation_{};
-};
-
-struct __with_awaitable_senders_base
-{
- template <class _OtherPromise>
- void set_continuation(
- __coro::coroutine_handle<_OtherPromise> __hcoro) noexcept
- {
- static_assert(!std::is_void_v<_OtherPromise>);
- __continuation_ = __hcoro;
- }
-
- void set_continuation(__continuation_handle<> __continuation) noexcept
- {
- __continuation_ = __continuation;
- }
-
- [[nodiscard]] auto continuation() const noexcept -> __continuation_handle<>
- {
- return __continuation_;
- }
-
- auto unhandled_stopped() noexcept -> __coro::coroutine_handle<>
- {
- return __continuation_.unhandled_stopped();
- }
-
- private:
- __continuation_handle<> __continuation_{};
-};
-
-template <class _Promise>
-struct with_awaitable_senders : __with_awaitable_senders_base
-{
- template <class _Value>
- auto await_transform(_Value&& __val)
- -> __call_result_t<as_awaitable_t, _Value, _Promise&>
- {
- static_assert(derived_from<_Promise, with_awaitable_senders>);
- return as_awaitable(static_cast<_Value&&>(__val),
- static_cast<_Promise&>(*this));
- }
-};
-} // namespace __with_awaitable_senders
-
-using __with_awaitable_senders::__continuation_handle;
-using __with_awaitable_senders::with_awaitable_senders;
-#endif
-
-namespace
-{
-inline constexpr auto __ref = []<class _Ty>(_Ty& __ty) noexcept {
- return [__ty = &__ty]() noexcept -> decltype(auto) { return (*__ty); };
-};
-} // namespace
-
-template <class _Ty>
-using __ref_t = decltype(__ref(__declval<_Ty&>()));
-
-/////////////////////////////////////////////////////////////////////////////
-// NOT TO SPEC: __submit
-namespace __submit_
-{
-template <class _OpRef>
-struct __receiver
-{
- using receiver_concept = receiver_t;
- using __t = __receiver;
- using __id = __receiver;
-
- using _Operation = __decay_t<__call_result_t<_OpRef>>;
- using _Receiver = stdexec::__t<__mapply<__q<__msecond>, _Operation>>;
-
- _OpRef __opref_;
-
- // Forward all the receiver ops, and delete the operation state.
- template <__completion_tag _Tag, class... _As>
- requires __callable<_Tag, _Receiver, _As...>
- friend void tag_invoke(_Tag __tag, __receiver&& __self,
- _As&&... __as) noexcept
- {
- __tag(static_cast<_Receiver&&>(__self.__opref_().__rcvr_),
- static_cast<_As&&>(__as)...);
- __self.__delete_op();
- }
-
- void __delete_op() noexcept
- {
- _Operation* __op = &__opref_();
- if constexpr (__callable<get_allocator_t, env_of_t<_Receiver>>)
- {
- auto&& __env = get_env(__op->__rcvr_);
- auto __alloc = get_allocator(__env);
- using _Alloc = decltype(__alloc);
- using _OpAlloc = typename std::allocator_traits<
- _Alloc>::template rebind_alloc<_Operation>;
- _OpAlloc __op_alloc{__alloc};
- std::allocator_traits<_OpAlloc>::destroy(__op_alloc, __op);
- std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __op, 1);
- }
- else
- {
- delete __op;
- }
- }
-
- // Forward all receiver queries.
- friend auto tag_invoke(get_env_t, const __receiver& __self) noexcept
- -> env_of_t<_Receiver&>
- {
- return get_env(__self.__opref_().__rcvr_);
- }
-};
-
-template <class _SenderId, class _ReceiverId>
-struct __operation
-{
- using _Sender = stdexec::__t<_SenderId>;
- using _Receiver = stdexec::__t<_ReceiverId>;
- using __receiver_t = __receiver<__ref_t<__operation>>;
-
- STDEXEC_ATTRIBUTE((no_unique_address))
- _Receiver __rcvr_;
- connect_result_t<_Sender, __receiver_t> __op_state_;
-
- __operation(_Sender&& __sndr, _Receiver __rcvr) :
- __rcvr_(static_cast<_Receiver&&>(__rcvr)),
- __op_state_(
- connect(static_cast<_Sender&&>(__sndr), __receiver_t{__ref(*this)}))
- {}
-};
-
-struct __submit_t
-{
- template <receiver _Receiver, sender_to<_Receiver> _Sender>
- void operator()(_Sender&& __sndr, _Receiver __rcvr) const noexcept(false)
- {
- if constexpr (__callable<get_allocator_t, env_of_t<_Receiver>>)
- {
- auto&& __env = get_env(__rcvr);
- auto __alloc = get_allocator(__env);
- using _Alloc = decltype(__alloc);
- using _Op = __operation<__id<_Sender>, __id<_Receiver>>;
- using _OpAlloc = typename std::allocator_traits<
- _Alloc>::template rebind_alloc<_Op>;
- _OpAlloc __op_alloc{__alloc};
- auto __op = std::allocator_traits<_OpAlloc>::allocate(__op_alloc,
- 1);
- try
- {
- std::allocator_traits<_OpAlloc>::construct(
- __op_alloc, __op, static_cast<_Sender&&>(__sndr),
- static_cast<_Receiver&&>(__rcvr));
- start(__op->__op_state_);
- }
- catch (...)
- {
- std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __op,
- 1);
- throw;
- }
- }
- else
- {
- start((new __operation<__id<_Sender>, __id<_Receiver>>{
- static_cast<_Sender&&>(__sndr),
- static_cast<_Receiver&&>(__rcvr)})
- ->__op_state_);
- }
- }
-};
-} // namespace __submit_
-
-using __submit_::__submit_t;
-inline constexpr __submit_t __submit{};
-
-namespace __inln
-{
-struct __schedule_t
-{};
-
-struct __scheduler
-{
- using __t = __scheduler;
- using __id = __scheduler;
-
- template <class _Tag = __schedule_t>
- STDEXEC_ATTRIBUTE((host, device))
- friend auto tag_invoke(schedule_t, __scheduler)
- {
- return __make_sexpr<_Tag>();
- }
-
- friend auto tag_invoke(get_forward_progress_guarantee_t,
- __scheduler) noexcept -> forward_progress_guarantee
- {
- return forward_progress_guarantee::weakly_parallel;
- }
-
- auto operator==(const __scheduler&) const noexcept -> bool = default;
-};
-} // namespace __inln
-
-template <>
-struct __sexpr_impl<__inln::__schedule_t> : __sexpr_defaults
-{
- static constexpr auto get_attrs = //
- [](__ignore) noexcept
- -> __env::__with<__inln::__scheduler,
- get_completion_scheduler_t<set_value_t>> {
- return __env::__with(__inln::__scheduler{},
- get_completion_scheduler<set_value_t>);
- };
-
- static constexpr auto get_completion_signatures = //
- [](__ignore,
- __ignore) noexcept -> completion_signatures<set_value_t()> {
- return {};
- };
-
- static constexpr auto start = //
- []<class _Receiver>(__ignore, _Receiver& __rcvr) noexcept -> void {
- set_value(static_cast<_Receiver&&>(__rcvr));
- };
-};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.consumer.start_detached]
-namespace __start_detached
-{
-template <class _EnvId>
-struct __detached_receiver
-{
- using _Env = stdexec::__t<_EnvId>;
-
- struct __t
- {
- using receiver_concept = receiver_t;
- using __id = __detached_receiver;
- STDEXEC_ATTRIBUTE((no_unique_address))
- _Env __env_;
-
- template <same_as<set_value_t> _Tag, class... _As>
- friend void tag_invoke(_Tag, __t&&, _As&&...) noexcept
- {}
-
- template <same_as<set_error_t> _Tag, class _Error>
- [[noreturn]] friend void tag_invoke(_Tag, __t&&, _Error&&) noexcept
- {
- std::terminate();
- }
-
- template <same_as<set_stopped_t> _Tag>
- friend void tag_invoke(_Tag, __t&&) noexcept
- {}
-
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> const _Env&
- {
- // BUGBUG NOT TO SPEC
- return __self.__env_;
- }
- };
-};
-template <class _Env = empty_env>
-using __detached_receiver_t = __t<__detached_receiver<__id<__decay_t<_Env>>>>;
-
-struct start_detached_t
-{
- template <sender_in<empty_env> _Sender>
- requires __callable<apply_sender_t, __early_domain_of_t<_Sender>,
- start_detached_t, _Sender>
- void operator()(_Sender&& __sndr) const
- {
- auto __domain = __get_early_domain(__sndr);
- stdexec::apply_sender(__domain, *this, static_cast<_Sender&&>(__sndr));
- }
-
- template <class _Env, sender_in<_Env> _Sender>
- requires __callable<apply_sender_t, __late_domain_of_t<_Sender, _Env>,
- start_detached_t, _Sender, _Env>
- void operator()(_Sender&& __sndr, _Env&& __env) const
- {
- auto __domain = __get_late_domain(__sndr, __env);
- stdexec::apply_sender(__domain, *this, static_cast<_Sender&&>(__sndr),
- static_cast<_Env&&>(__env));
- }
-
- using _Sender = __0;
- using __legacy_customizations_t =
- __types<tag_invoke_t(start_detached_t,
- get_completion_scheduler_t<set_value_t>(
- get_env_t(const _Sender&)),
- _Sender),
- tag_invoke_t(start_detached_t, _Sender)>;
-
- template <class _Sender, class _Env = empty_env>
- requires sender_to<_Sender, __detached_receiver_t<_Env>>
- void apply_sender(_Sender&& __sndr, _Env&& __env = {}) const
- {
- __submit(static_cast<_Sender&&>(__sndr),
- __detached_receiver_t<_Env>{static_cast<_Env&&>(__env)});
- }
-};
-} // namespace __start_detached
-
-using __start_detached::start_detached_t;
-inline constexpr start_detached_t start_detached{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.factories]
-namespace __just
-{
-template <class _JustTag>
-struct __impl : __sexpr_defaults
-{
- using __tag_t = typename _JustTag::__tag_t;
-
- static constexpr auto get_completion_signatures =
- []<class _Sender>(_Sender&&, __ignore) noexcept {
- static_assert(sender_expr_for<_Sender, _JustTag>);
- return completion_signatures<
- __mapply<__qf<__tag_t>, __decay_t<__data_of<_Sender>>>>{};
- };
-
- static constexpr auto start =
- []<class _State, class _Receiver>(_State& __state,
- _Receiver& __rcvr) noexcept -> void {
- __tup::__apply(
- [&]<class... _Ts>(_Ts&... __ts) noexcept {
- __tag_t()(std::move(__rcvr), std::move(__ts)...);
- },
- __state);
- };
-};
-
-struct just_t
-{
- using __tag_t = set_value_t;
-
- template <__movable_value... _Ts>
- STDEXEC_ATTRIBUTE((host, device))
- auto operator()(_Ts&&... __ts) const
- noexcept((__nothrow_decay_copyable<_Ts> && ...))
- {
- return __make_sexpr<just_t>(__tuple{static_cast<_Ts&&>(__ts)...});
- }
-};
-
-struct just_error_t
-{
- using __tag_t = set_error_t;
-
- template <__movable_value _Error>
- STDEXEC_ATTRIBUTE((host, device))
- auto operator()(_Error&& __err) const
- noexcept(__nothrow_decay_copyable<_Error>)
- {
- return __make_sexpr<just_error_t>(
- __tuple{static_cast<_Error&&>(__err)});
- }
-};
-
-struct just_stopped_t
-{
- using __tag_t = set_stopped_t;
-
- template <class _Tag = just_stopped_t>
- STDEXEC_ATTRIBUTE((host, device))
- auto operator()() const noexcept
- {
- return __make_sexpr<_Tag>(__tuple{});
- }
-};
-} // namespace __just
-
-using __just::just_error_t;
-using __just::just_stopped_t;
-using __just::just_t;
-
-template <>
-struct __sexpr_impl<just_t> : __just::__impl<just_t>
-{};
-
-template <>
-struct __sexpr_impl<just_error_t> : __just::__impl<just_error_t>
-{};
-
-template <>
-struct __sexpr_impl<just_stopped_t> : __just::__impl<just_stopped_t>
-{};
-
-inline constexpr just_t just{};
-inline constexpr just_error_t just_error{};
-inline constexpr just_stopped_t just_stopped{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.execute]
-namespace __execute_
-{
-template <class _Fun>
-struct __as_receiver
-{
- using receiver_concept = receiver_t;
- _Fun __fun_;
-
- template <same_as<set_value_t> _Tag>
- friend void tag_invoke(_Tag, __as_receiver&& __rcvr) noexcept
- {
- try
- {
- __rcvr.__fun_();
- }
- catch (...)
- {
- set_error(static_cast<__as_receiver&&>(__rcvr),
- std::exception_ptr());
- }
- }
-
- template <same_as<set_error_t> _Tag>
- [[noreturn]] friend void tag_invoke(_Tag, __as_receiver&&,
- std::exception_ptr) noexcept
- {
- std::terminate();
- }
-
- template <same_as<set_stopped_t> _Tag>
- friend void tag_invoke(_Tag, __as_receiver&&) noexcept
- {}
-
- friend auto tag_invoke(get_env_t, const __as_receiver&) noexcept
- -> empty_env
- {
- return {};
- }
-};
-
-struct execute_t
-{
- template <scheduler _Scheduler, class _Fun>
- requires __callable<_Fun&> && move_constructible<_Fun>
- void operator()(_Scheduler&& __sched, _Fun __fun) const noexcept(false)
- {
- // Look for a legacy customization
- if constexpr (tag_invocable<execute_t, _Scheduler, _Fun>)
- {
- tag_invoke(execute_t{}, static_cast<_Scheduler&&>(__sched),
- static_cast<_Fun&&>(__fun));
- }
- else
- {
- auto __domain = query_or(get_domain, __sched, default_domain());
- stdexec::apply_sender(__domain, *this,
- schedule(static_cast<_Scheduler&&>(__sched)),
- static_cast<_Fun&&>(__fun));
- }
- }
-
- template <sender_of<set_value_t()> _Sender, class _Fun>
- requires __callable<_Fun&> && move_constructible<_Fun>
- void apply_sender(_Sender&& __sndr, _Fun __fun) const noexcept(false)
- {
- __submit(static_cast<_Sender&&>(__sndr),
- __as_receiver<_Fun>{static_cast<_Fun&&>(__fun)});
- }
-};
-} // namespace __execute_
-
-using __execute_::execute_t;
-inline constexpr execute_t execute{};
-
-// NOT TO SPEC:
-namespace __closure
-{
-template <__class _Dp>
-struct sender_adaptor_closure;
-} // namespace __closure
-
-using __closure::sender_adaptor_closure;
-
-template <class _Tp>
-concept __sender_adaptor_closure =
- derived_from<__decay_t<_Tp>, sender_adaptor_closure<__decay_t<_Tp>>> &&
- move_constructible<__decay_t<_Tp>> &&
- constructible_from<__decay_t<_Tp>, _Tp>;
-
-template <class _Tp, class _Sender>
-concept __sender_adaptor_closure_for =
- __sender_adaptor_closure<_Tp> && sender<__decay_t<_Sender>> &&
- __callable<_Tp, __decay_t<_Sender>> &&
- sender<__call_result_t<_Tp, __decay_t<_Sender>>>;
-
-namespace __closure
-{
-template <class _T0, class _T1>
-struct __compose : sender_adaptor_closure<__compose<_T0, _T1>>
-{
- STDEXEC_ATTRIBUTE((no_unique_address))
- _T0 __t0_;
- STDEXEC_ATTRIBUTE((no_unique_address))
- _T1 __t1_;
-
- template <sender _Sender>
- requires __callable<_T0, _Sender> &&
- __callable<_T1, __call_result_t<_T0, _Sender>>
- STDEXEC_ATTRIBUTE((always_inline))
- __call_result_t<_T1, __call_result_t<_T0, _Sender>>
- operator()(_Sender&& __sndr) &&
- {
- return static_cast<_T1&&>(__t1_)(
- static_cast<_T0&&>(__t0_)(static_cast<_Sender&&>(__sndr)));
- }
-
- template <sender _Sender>
- requires __callable<const _T0&, _Sender> &&
- __callable<const _T1&, __call_result_t<const _T0&, _Sender>>
- STDEXEC_ATTRIBUTE((always_inline))
- __call_result_t<_T1, __call_result_t<_T0, _Sender>>
- operator()(_Sender&& __sndr) const&
- {
- return __t1_(__t0_(static_cast<_Sender&&>(__sndr)));
- }
-};
-
-template <__class _Dp>
-struct sender_adaptor_closure
-{};
-
-template <sender _Sender, __sender_adaptor_closure_for<_Sender> _Closure>
-STDEXEC_ATTRIBUTE((always_inline))
-__call_result_t<_Closure, _Sender> operator|(_Sender&& __sndr,
- _Closure&& __clsur)
-{
- return static_cast<_Closure&&>(__clsur)(static_cast<_Sender&&>(__sndr));
-}
-
-template <__sender_adaptor_closure _T0, __sender_adaptor_closure _T1>
-STDEXEC_ATTRIBUTE((always_inline))
-__compose<__decay_t<_T0>, __decay_t<_T1>> operator|(_T0&& __t0, _T1&& __t1)
-{
- return {{}, static_cast<_T0&&>(__t0), static_cast<_T1&&>(__t1)};
-}
-
-template <class _Fun, class... _As>
-struct __binder_back : sender_adaptor_closure<__binder_back<_Fun, _As...>>
-{
- STDEXEC_ATTRIBUTE((no_unique_address))
- _Fun __fun_;
- std::tuple<_As...> __as_;
-
- 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...>)
- {
- return __apply(
- [&__sndr,
- this](_As&... __as) -> __call_result_t<_Fun, _Sender, _As...> {
- return static_cast<_Fun&&>(__fun_)(static_cast<_Sender&&>(__sndr),
- static_cast<_As&&>(__as)...);
- },
- __as_);
- }
-
- template <sender _Sender>
- requires __callable<const _Fun&, _Sender, const _As&...>
- STDEXEC_ATTRIBUTE((host, device)) auto
- operator()(_Sender&& __sndr) const& //
- noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>)
- -> __call_result_t<const _Fun&, _Sender, const _As&...>
- {
- return __apply(
- [&__sndr, this](const _As&... __as)
- -> __call_result_t<const _Fun&, _Sender, const _As&...> {
- return __fun_(static_cast<_Sender&&>(__sndr), __as...);
- },
- __as_);
- }
-};
-} // namespace __closure
-
-using __closure::__binder_back;
-
-namespace __adaptors
-{
-STDEXEC_PRAGMA_PUSH()
-STDEXEC_PRAGMA_IGNORE_GNU("-Wold-style-cast")
-
-// A derived-to-base cast that works even when the base is not
-// accessible from derived.
-template <class _Tp, class _Up>
-STDEXEC_ATTRIBUTE((host, device))
-auto __c_cast(_Up&& u) noexcept -> __copy_cvref_t<_Up&&, _Tp>
- requires __decays_to<_Tp, _Tp>
-{
- static_assert(std::is_reference_v<__copy_cvref_t<_Up&&, _Tp>>);
- static_assert(STDEXEC_IS_BASE_OF(_Tp, __decay_t<_Up>));
- return (__copy_cvref_t<_Up&&, _Tp>)static_cast<_Up&&>(u);
-}
-STDEXEC_PRAGMA_POP()
-
-namespace __no
-{
-struct __nope
-{};
-
-struct __receiver : __nope
-{
- using receiver_concept = receiver_t;
-};
-
-template <same_as<set_error_t> _Tag>
-void tag_invoke(_Tag, __receiver, std::exception_ptr) noexcept;
-template <same_as<set_stopped_t> _Tag>
-void tag_invoke(_Tag, __receiver) noexcept;
-auto tag_invoke(get_env_t, __receiver) noexcept -> empty_env;
-} // namespace __no
-
-using __not_a_receiver = __no::__receiver;
-
-template <class _Base>
-struct __adaptor_base
-{
- template <class _T1>
- requires constructible_from<_Base, _T1>
- explicit __adaptor_base(_T1&& __base) : __base_(static_cast<_T1&&>(__base))
- {}
-
- private:
- STDEXEC_ATTRIBUTE((no_unique_address))
- _Base __base_;
-
- protected:
- STDEXEC_ATTRIBUTE((host, device, always_inline))
-
- _Base& base() & noexcept
- {
- return __base_;
- }
-
- STDEXEC_ATTRIBUTE((host, device, always_inline))
-
- const _Base& base() const& noexcept
- {
- return __base_;
- }
-
- STDEXEC_ATTRIBUTE((host, device, always_inline))
-
- _Base&& base() && noexcept
- {
- return static_cast<_Base&&>(__base_);
- }
-};
-
-template <derived_from<__no::__nope> _Base>
-struct __adaptor_base<_Base>
-{};
-
-// BUGBUG Not to spec: on gcc and nvc++, member functions in derived classes
-// don't shadow type aliases of the same name in base classes. :-O
-// On mingw gcc, 'bool(type::existing_member_function)' evaluates to true,
-// but 'int(type::existing_member_function)' is an error (as desired).
-#define STDEXEC_DISPATCH_MEMBER(_TAG) \
- template <class _Self, class... _Ts> \
- STDEXEC_ATTRIBUTE((host, device, always_inline)) \
- static auto __call_##_TAG(_Self&& __self, _Ts&&... __ts) noexcept \
- -> decltype((static_cast<_Self&&>(__self)) \
- ._TAG(static_cast<_Ts&&>(__ts)...)) \
- { \
- static_assert(noexcept((static_cast<_Self&&>(__self)) \
- ._TAG(static_cast<_Ts&&>(__ts)...))); \
- return static_cast<_Self&&>(__self)._TAG(static_cast<_Ts&&>(__ts)...); \
- } /**/
-#define STDEXEC_CALL_MEMBER(_TAG, ...) __call_##_TAG(__VA_ARGS__)
-
-#if STDEXEC_CLANG()
-// Only clang gets this right.
-#define STDEXEC_MISSING_MEMBER(_Dp, _TAG) requires { typename _Dp::_TAG; }
-#define STDEXEC_DEFINE_MEMBER(_TAG) \
- STDEXEC_DISPATCH_MEMBER(_TAG) using _TAG = void
-#else
-#define STDEXEC_MISSING_MEMBER(_Dp, _TAG) (__missing_##_TAG<_Dp>())
-#define STDEXEC_DEFINE_MEMBER(_TAG) \
- template <class _Dp> \
- static constexpr bool __missing_##_TAG() noexcept \
- { \
- return requires { requires bool(int(_Dp::_TAG)); }; \
- } \
- STDEXEC_DISPATCH_MEMBER(_TAG) \
- static constexpr int _TAG = 1 /**/
-#endif
-
-template <__class _Derived, class _Base = __not_a_receiver>
-struct receiver_adaptor : __adaptor_base<_Base>, receiver_t
-{
- friend _Derived;
- STDEXEC_DEFINE_MEMBER(set_value);
- STDEXEC_DEFINE_MEMBER(set_error);
- STDEXEC_DEFINE_MEMBER(set_stopped);
- STDEXEC_DEFINE_MEMBER(get_env);
-
- static constexpr bool __has_base = !derived_from<_Base, __no::__nope>;
-
- template <class _Dp>
- using __base_from_derived_t = decltype(__declval<_Dp>().base());
-
- using __get_base_t =
- __if_c<__has_base, __mbind_back_q<__copy_cvref_t, _Base>,
- __q<__base_from_derived_t>>;
-
- template <class _Dp>
- using __base_t = __minvoke<__get_base_t, _Dp&&>;
-
- template <class _Dp>
- STDEXEC_ATTRIBUTE((host, device))
- static auto __get_base(_Dp&& __self) noexcept -> __base_t<_Dp>
- {
- if constexpr (__has_base)
- {
- return __c_cast<receiver_adaptor>(static_cast<_Dp&&>(__self))
- .base();
- }
- else
- {
- return static_cast<_Dp&&>(__self).base();
- }
- }
-
- template <same_as<set_value_t> _SetValue, class... _As>
- STDEXEC_ATTRIBUTE((host, device, always_inline))
- friend auto tag_invoke(_SetValue, _Derived&& __self,
- _As&&... __as) noexcept //
- -> __msecond< //
- __if_c<same_as<set_value_t, _SetValue>>,
- decltype(STDEXEC_CALL_MEMBER(set_value,
- static_cast<_Derived&&>(__self),
- static_cast<_As&&>(__as)...))>
- {
- static_assert(noexcept(
- STDEXEC_CALL_MEMBER(set_value, static_cast<_Derived&&>(__self),
- static_cast<_As&&>(__as)...)));
- STDEXEC_CALL_MEMBER(set_value, static_cast<_Derived&&>(__self),
- static_cast<_As&&>(__as)...);
- }
-
- template <same_as<set_value_t> _SetValue, class _Dp = _Derived,
- class... _As>
- requires STDEXEC_MISSING_MEMBER
- (_Dp, set_value) &&
- tag_invocable<_SetValue, __base_t<_Dp>, _As...> STDEXEC_ATTRIBUTE((
- host, device,
- always_inline)) friend void tag_invoke(_SetValue, _Derived&& __self,
- _As&&... __as) noexcept
- {
- stdexec::set_value(__get_base(static_cast<_Dp&&>(__self)),
- static_cast<_As&&>(__as)...);
- }
-
- template <same_as<set_error_t> _SetError, class _Error>
- STDEXEC_ATTRIBUTE((host, device, always_inline))
- friend auto tag_invoke(_SetError, _Derived&& __self,
- _Error&& __err) noexcept //
- -> __msecond< //
- __if_c<same_as<set_error_t, _SetError>>,
- decltype(STDEXEC_CALL_MEMBER(set_error,
- static_cast<_Derived&&>(__self),
- static_cast<_Error&&>(__err)))>
- {
- static_assert(noexcept(
- STDEXEC_CALL_MEMBER(set_error, static_cast<_Derived&&>(__self),
- static_cast<_Error&&>(__err))));
- STDEXEC_CALL_MEMBER(set_error, static_cast<_Derived&&>(__self),
- static_cast<_Error&&>(__err));
- }
-
- template <same_as<set_error_t> _SetError, class _Error,
- class _Dp = _Derived>
- requires STDEXEC_MISSING_MEMBER
- (_Dp, set_error) &&
- tag_invocable<_SetError, __base_t<_Dp>, _Error> STDEXEC_ATTRIBUTE((
- host, device,
- always_inline)) friend void tag_invoke(_SetError, _Derived&& __self,
- _Error&& __err) noexcept
- {
- stdexec::set_error(__get_base(static_cast<_Derived&&>(__self)),
- static_cast<_Error&&>(__err));
- }
-
- template <same_as<set_stopped_t> _SetStopped, class _Dp = _Derived>
- STDEXEC_ATTRIBUTE((host, device, always_inline))
- friend auto tag_invoke(_SetStopped, _Derived&& __self) noexcept //
- -> __msecond< //
- __if_c<same_as<set_stopped_t, _SetStopped>>,
- decltype(STDEXEC_CALL_MEMBER(set_stopped,
- static_cast<_Dp&&>(__self)))>
- {
- static_assert(noexcept(
- STDEXEC_CALL_MEMBER(set_stopped, static_cast<_Derived&&>(__self))));
- STDEXEC_CALL_MEMBER(set_stopped, static_cast<_Derived&&>(__self));
- }
-
- template <same_as<set_stopped_t> _SetStopped, class _Dp = _Derived>
- requires STDEXEC_MISSING_MEMBER
- (_Dp, set_stopped) &&
- tag_invocable<_SetStopped, __base_t<_Dp>> STDEXEC_ATTRIBUTE(
- (host, device,
- always_inline)) friend void tag_invoke(_SetStopped,
- _Derived&& __self) noexcept
- {
- stdexec::set_stopped(__get_base(static_cast<_Derived&&>(__self)));
- }
-
- // Pass through the get_env receiver query
- template <same_as<get_env_t> _GetEnv, class _Dp = _Derived>
- STDEXEC_ATTRIBUTE((host, device, always_inline))
- friend auto tag_invoke(_GetEnv, const _Derived& __self) noexcept
- -> decltype(STDEXEC_CALL_MEMBER(get_env,
- static_cast<const _Dp&>(__self)))
- {
- static_assert(noexcept(STDEXEC_CALL_MEMBER(get_env, __self)));
- return STDEXEC_CALL_MEMBER(get_env, __self);
- }
-
- template <same_as<get_env_t> _GetEnv, class _Dp = _Derived>
- requires STDEXEC_MISSING_MEMBER
- (_Dp, get_env)
- STDEXEC_ATTRIBUTE((host, device, always_inline)) friend auto tag_invoke(
- _GetEnv, const _Derived& __self) noexcept
- -> env_of_t<__base_t<const _Dp&>>
- {
- return stdexec::get_env(__get_base(__self));
- }
-
- public:
- receiver_adaptor() = default;
- using __adaptor_base<_Base>::__adaptor_base;
-
- using receiver_concept = receiver_t;
-};
-} // namespace __adaptors
-
-template <__class _Derived, receiver _Base = __adaptors::__not_a_receiver>
-using receiver_adaptor = __adaptors::receiver_adaptor<_Derived, _Base>;
-
-template <class _Receiver, class _Fun, class... _As>
-concept __receiver_of_invoke_result = //
- receiver_of<_Receiver, completion_signatures<
- __minvoke<__remove<void, __qf<set_value_t>>,
- __invoke_result_t<_Fun, _As...>>>>;
-
-template <bool _CanThrow = false, class _Receiver, class _Fun, class... _As>
-void __set_value_invoke(_Receiver&& __rcvr, _Fun&& __fun,
- _As&&... __as) noexcept(!_CanThrow)
-{
- if constexpr (_CanThrow || __nothrow_invocable<_Fun, _As...>)
- {
- if constexpr (same_as<void, __invoke_result_t<_Fun, _As...>>)
- {
- __invoke(static_cast<_Fun&&>(__fun), static_cast<_As&&>(__as)...);
- set_value(static_cast<_Receiver&&>(__rcvr));
- }
- else
- {
- set_value(static_cast<_Receiver&&>(__rcvr),
- __invoke(static_cast<_Fun&&>(__fun),
- static_cast<_As&&>(__as)...));
- }
- }
- else
- {
- try
- {
- stdexec::__set_value_invoke<true>(static_cast<_Receiver&&>(__rcvr),
- static_cast<_Fun&&>(__fun),
- static_cast<_As&&>(__as)...);
- }
- catch (...)
- {
- set_error(static_cast<_Receiver&&>(__rcvr),
- std::current_exception());
- }
- }
-}
-
-template <class _Fun>
-struct _WITH_FUNCTION_
-{};
-
-template <class... _Args>
-struct _WITH_ARGUMENTS_
-{};
-
-inline constexpr __mstring __not_callable_diag =
- "The specified function is not callable with the arguments provided."_mstr;
-
-template <__mstring _Context, __mstring _Diagnostic = __not_callable_diag>
-struct _NOT_CALLABLE_
-{};
-
-template <__mstring _Context>
-struct __callable_error
-{
- template <class _Fun, class... _Args>
- using __f = //
- __mexception< //
- _NOT_CALLABLE_<_Context>, _WITH_FUNCTION_<_Fun>,
- _WITH_ARGUMENTS_<_Args...>>;
-};
-
-template <class _Fun, class... _Args>
- requires __invocable<_Fun, _Args...>
-using __non_throwing_ = __mbool<__nothrow_invocable<_Fun, _Args...>>;
-
-template <class _Tag, class _Fun, class _Sender, class _Env, class _Catch>
-using __with_error_invoke_t = //
- __if<__gather_completions_for<
- _Tag, _Sender, _Env,
- __mbind_front<__mtry_catch_q<__non_throwing_, _Catch>, _Fun>,
- __q<__mand>>,
- completion_signatures<>, __with_exception_ptr>;
-
-template <class _Fun, class... _Args>
- requires __invocable<_Fun, _Args...>
-using __set_value_invoke_t = //
- completion_signatures<__minvoke<__remove<void, __qf<set_value_t>>,
- __invoke_result_t<_Fun, _Args...>>>;
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.adaptors.then]
-namespace __then
-{
-inline constexpr __mstring __then_context =
- "In stdexec::then(Sender, Function)..."_mstr;
-using __on_not_callable = __callable_error<__then_context>;
-
-template <class _Fun, class _CvrefSender, class _Env>
-using __completion_signatures_t = //
- __try_make_completion_signatures<
- _CvrefSender, _Env,
- __with_error_invoke_t<set_value_t, _Fun, _CvrefSender, _Env,
- __on_not_callable>,
- __mbind_front<__mtry_catch_q<__set_value_invoke_t, __on_not_callable>,
- _Fun>>;
-
-////////////////////////////////////////////////////////////////////////////////////////////////
-struct then_t
-{
- template <sender _Sender, __movable_value _Fun>
- auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender
- auto
- {
- auto __domain = __get_early_domain(__sndr);
- return stdexec::transform_sender(
- __domain, __make_sexpr<then_t>(static_cast<_Fun&&>(__fun),
- static_cast<_Sender&&>(__sndr)));
- }
-
- template <__movable_value _Fun>
- STDEXEC_ATTRIBUTE((always_inline))
- __binder_back<then_t, _Fun> operator()(_Fun __fun) const
- {
- return {{}, {}, {static_cast<_Fun&&>(__fun)}};
- }
-
- using _Sender = __1;
- using _Fun = __0;
- using __legacy_customizations_t =
- __types<tag_invoke_t(then_t,
- get_completion_scheduler_t<set_value_t>(
- get_env_t(_Sender&)),
- _Sender, _Fun),
- tag_invoke_t(then_t, _Sender, _Fun)>;
-};
-
-struct __then_impl : __sexpr_defaults
-{
- static constexpr auto get_completion_signatures = //
- []<class _Sender, class _Env>(_Sender&&, _Env&&) noexcept
- -> __completion_signatures_t<__decay_t<__data_of<_Sender>>,
- __child_of<_Sender>, _Env> {
- static_assert(sender_expr_for<_Sender, then_t>);
- return {};
- };
-
- static constexpr auto complete = //
- []<class _Tag, class... _Args>(__ignore, auto& __state, auto& __rcvr,
- _Tag,
- _Args&&... __args) noexcept -> void {
- if constexpr (std::same_as<_Tag, set_value_t>)
- {
- stdexec::__set_value_invoke(std::move(__rcvr), std::move(__state),
- static_cast<_Args&&>(__args)...);
- }
- else
- {
- _Tag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
- }
- };
-};
-} // namespace __then
-
-using __then::then_t;
-
-/// @brief The then sender adaptor, which invokes a function with the result of
-/// a sender, making the result available to the next receiver.
-/// @hideinitializer
-inline constexpr then_t then{};
-
-template <>
-struct __sexpr_impl<then_t> : __then::__then_impl
-{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.adaptors.upon_error]
-namespace __upon_error
-{
-inline constexpr __mstring __upon_error_context =
- "In stdexec::upon_error(Sender, Function)..."_mstr;
-using __on_not_callable = __callable_error<__upon_error_context>;
-
-template <class _Fun, class _CvrefSender, class _Env>
-using __completion_signatures_t = //
- __try_make_completion_signatures<
- _CvrefSender, _Env,
- __with_error_invoke_t<set_error_t, _Fun, _CvrefSender, _Env,
- __on_not_callable>,
- __q<__compl_sigs::__default_set_value>,
- __mbind_front<__mtry_catch_q<__set_value_invoke_t, __on_not_callable>,
- _Fun>>;
-
-////////////////////////////////////////////////////////////////////////////////////////////////
-struct upon_error_t
-{
- template <sender _Sender, __movable_value _Fun>
- auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender
- auto
- {
- auto __domain = __get_early_domain(__sndr);
- return stdexec::transform_sender(
- __domain,
- __make_sexpr<upon_error_t>(static_cast<_Fun&&>(__fun),
- static_cast<_Sender&&>(__sndr)));
- }
-
- template <__movable_value _Fun>
- STDEXEC_ATTRIBUTE((always_inline))
- __binder_back<upon_error_t, _Fun> operator()(_Fun __fun) const
- {
- return {{}, {}, {static_cast<_Fun&&>(__fun)}};
- }
-
- using _Sender = __1;
- using _Fun = __0;
- using __legacy_customizations_t =
- __types<tag_invoke_t(upon_error_t,
- get_completion_scheduler_t<set_value_t>(
- get_env_t(_Sender&)),
- _Sender, _Fun),
- tag_invoke_t(upon_error_t, _Sender, _Fun)>;
-};
-
-struct __upon_error_impl : __sexpr_defaults
-{
- static constexpr auto get_completion_signatures = //
- []<class _Sender, class _Env>(_Sender&&, _Env&&) noexcept
- -> __completion_signatures_t<__decay_t<__data_of<_Sender>>,
- __child_of<_Sender>, _Env> {
- static_assert(sender_expr_for<_Sender, upon_error_t>);
- return {};
- };
-
- static constexpr auto complete = //
- []<class _Tag, class... _Args>(__ignore, auto& __state, auto& __rcvr,
- _Tag,
- _Args&&... __args) noexcept -> void {
- if constexpr (std::same_as<_Tag, set_error_t>)
- {
- stdexec::__set_value_invoke(std::move(__rcvr), std::move(__state),
- static_cast<_Args&&>(__args)...);
- }
- else
- {
- _Tag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
- }
- };
-};
-} // namespace __upon_error
-
-using __upon_error::upon_error_t;
-inline constexpr upon_error_t upon_error{};
-
-template <>
-struct __sexpr_impl<upon_error_t> : __upon_error::__upon_error_impl
-{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.adaptors.upon_stopped]
-namespace __upon_stopped
-{
-inline constexpr __mstring __upon_stopped_context =
- "In stdexec::upon_stopped(Sender, Function)..."_mstr;
-using __on_not_callable = __callable_error<__upon_stopped_context>;
-
-template <class _Fun, class _CvrefSender, class _Env>
-using __completion_signatures_t = //
- __try_make_completion_signatures<
- _CvrefSender, _Env,
- __with_error_invoke_t<set_stopped_t, _Fun, _CvrefSender, _Env,
- __on_not_callable>,
- __q<__compl_sigs::__default_set_value>,
- __q<__compl_sigs::__default_set_error>, __set_value_invoke_t<_Fun>>;
-
-////////////////////////////////////////////////////////////////////////////////////////////////
-struct upon_stopped_t
-{
- template <sender _Sender, __movable_value _Fun>
- requires __callable<_Fun>
- auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender
- auto
- {
- auto __domain = __get_early_domain(__sndr);
- return stdexec::transform_sender(
- __domain,
- __make_sexpr<upon_stopped_t>(static_cast<_Fun&&>(__fun),
- static_cast<_Sender&&>(__sndr)));
- }
-
- template <__movable_value _Fun>
- requires __callable<_Fun>
- STDEXEC_ATTRIBUTE((always_inline)) __binder_back<upon_stopped_t, _Fun>
- operator()(_Fun __fun) const
- {
- return {{}, {}, {static_cast<_Fun&&>(__fun)}};
- }
-
- using _Sender = __1;
- using _Fun = __0;
- using __legacy_customizations_t =
- __types<tag_invoke_t(upon_stopped_t,
- get_completion_scheduler_t<set_value_t>(
- get_env_t(_Sender&)),
- _Sender, _Fun),
- tag_invoke_t(upon_stopped_t, _Sender, _Fun)>;
-};
-
-struct __upon_stopped_impl : __sexpr_defaults
-{
- static constexpr auto get_completion_signatures = //
- []<class _Sender, class _Env>(_Sender&&, _Env&&) noexcept
- -> __completion_signatures_t<__decay_t<__data_of<_Sender>>,
- __child_of<_Sender>, _Env> {
- static_assert(sender_expr_for<_Sender, upon_stopped_t>);
- return {};
- };
-
- static constexpr auto complete = //
- []<class _Tag, class... _Args>(__ignore, auto& __state, auto& __rcvr,
- _Tag,
- _Args&&... __args) noexcept -> void {
- if constexpr (std::same_as<_Tag, set_stopped_t>)
- {
- stdexec::__set_value_invoke(std::move(__rcvr), std::move(__state),
- static_cast<_Args&&>(__args)...);
- }
- else
- {
- _Tag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
- }
- };
-};
-} // namespace __upon_stopped
-
-using __upon_stopped::upon_stopped_t;
-inline constexpr upon_stopped_t upon_stopped{};
-
-template <>
-struct __sexpr_impl<upon_stopped_t> : __upon_stopped::__upon_stopped_impl
-{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.adaptors.bulk]
-namespace __bulk
-{
-inline constexpr __mstring __bulk_context =
- "In stdexec::bulk(Sender, Shape, Function)..."_mstr;
-using __on_not_callable = __callable_error<__bulk_context>;
-
-template <class _Shape, class _Fun>
-struct __data
-{
- _Shape __shape_;
- STDEXEC_ATTRIBUTE((no_unique_address))
- _Fun __fun_;
- static constexpr auto __mbrs_ =
- __mliterals<&__data::__shape_, &__data::__fun_>();
-};
-template <class _Shape, class _Fun>
-__data(_Shape, _Fun) -> __data<_Shape, _Fun>;
-
-template <class _Ty>
-using __decay_ref = __decay_t<_Ty>&;
-
-template <class _CvrefSender, class _Env, class _Shape, class _Fun,
- class _Catch>
-using __with_error_invoke_t = //
- __if<__try_value_types_of_t<
- _CvrefSender, _Env,
- __transform<__q<__decay_ref>,
- __mbind_front<__mtry_catch_q<__non_throwing_, _Catch>,
- _Fun, _Shape>>,
- __q<__mand>>,
- completion_signatures<>, __with_exception_ptr>;
-
-template <class _CvrefSender, class _Env, class _Shape, class _Fun>
-using __completion_signatures = //
- __try_make_completion_signatures<
- _CvrefSender, _Env,
- __with_error_invoke_t<_CvrefSender, _Env, _Shape, _Fun,
- __on_not_callable>>;
-
-struct bulk_t
-{
- template <sender _Sender, integral _Shape, __movable_value _Fun>
- STDEXEC_ATTRIBUTE((host, device))
- auto operator()(_Sender&& __sndr, _Shape __shape, _Fun __fun) const
- -> __well_formed_sender auto
- {
- auto __domain = __get_early_domain(__sndr);
- return stdexec::transform_sender(
- __domain,
- __make_sexpr<bulk_t>(__data{__shape, static_cast<_Fun&&>(__fun)},
- static_cast<_Sender&&>(__sndr)));
- }
-
- template <integral _Shape, class _Fun>
- STDEXEC_ATTRIBUTE((always_inline))
- __binder_back<bulk_t, _Shape, _Fun> operator()(_Shape __shape,
- _Fun __fun) const
- {
- return {{},
- {},
- {static_cast<_Shape&&>(__shape), static_cast<_Fun&&>(__fun)}};
- }
-
- // This describes how to use the pieces of a bulk sender to find
- // legacy customizations of the bulk algorithm.
- using _Sender = __1;
- using _Shape = __nth_member<0>(__0);
- using _Fun = __nth_member<1>(__0);
- using __legacy_customizations_t =
- __types<tag_invoke_t(bulk_t,
- get_completion_scheduler_t<set_value_t>(
- get_env_t(_Sender&)),
- _Sender, _Shape, _Fun),
- tag_invoke_t(bulk_t, _Sender, _Shape, _Fun)>;
-};
-
-struct __bulk_impl : __sexpr_defaults
-{
- template <class _Sender>
- using __fun_t = decltype(__decay_t<__data_of<_Sender>>::__fun_);
-
- template <class _Sender>
- using __shape_t = decltype(__decay_t<__data_of<_Sender>>::__shape_);
-
- static constexpr auto get_completion_signatures = //
- []<class _Sender, class _Env>(_Sender&&, _Env&&) noexcept
- -> __completion_signatures<__child_of<_Sender>, _Env,
- __shape_t<_Sender>, __fun_t<_Sender>> {
- static_assert(sender_expr_for<_Sender, bulk_t>);
- return {};
- };
-
- static constexpr auto complete = //
- []<class _Tag, class... _Args>(__ignore, auto& __state, auto& __rcvr,
- _Tag,
- _Args&&... __args) noexcept -> void {
- if constexpr (std::same_as<_Tag, set_value_t>)
- {
- using __shape_t = decltype(__state.__shape_);
- if constexpr (noexcept(__state.__fun_(__shape_t{}, __args...)))
- {
- for (__shape_t __i{}; __i != __state.__shape_; ++__i)
- {
- __state.__fun_(__i, __args...);
- }
- _Tag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
- }
- else
- {
- try
- {
- for (__shape_t __i{}; __i != __state.__shape_; ++__i)
- {
- __state.__fun_(__i, __args...);
- }
- _Tag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
- }
- catch (...)
- {
- set_error(std::move(__rcvr), std::current_exception());
- }
- }
- }
- else
- {
- _Tag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
- }
- };
-};
-} // namespace __bulk
-
-using __bulk::bulk_t;
-inline constexpr bulk_t bulk{};
-
-template <>
-struct __sexpr_impl<bulk_t> : __bulk::__bulk_impl
-{};
-
-////////////////////////////////////////////////////////////////////////////
-// shared components of split and ensure_started
-//
-// The split and ensure_started algorithms are very similar in implementation.
-// The salient differences are:
-//
-// split: the input async operation is always connected. It is only
-// started when one of the split senders is connected and started.
-// split senders are copyable, so there are multiple operation states
-// to be notified on completion. These are stored in an instrusive
-// linked list.
-//
-// ensure_started: the input async operation is always started, so
-// the internal receiver will always be completed. The ensure_started
-// sender is move-only and single-shot, so there will only ever be one
-// operation state to be notified on completion.
-//
-// The shared state should add-ref itself when the input async
-// operation is started and release itself when its completion
-// is notified.
-namespace __shared
-{
-template <class _BaseEnv>
-using __env_t = //
- __env::__join_t<__env::__with<in_place_stop_token, get_stop_token_t>,
- _BaseEnv>; // BUGBUG NOT TO SPEC
-
-struct __on_stop_request
-{
- in_place_stop_source& __stop_source_;
-
- void operator()() noexcept
- {
- __stop_source_.request_stop();
- }
-};
-
-template <class _Receiver>
-auto __notify_visitor(_Receiver& __rcvr) noexcept
-{
- return [&]<class _Tuple>(_Tuple&& __tupl) noexcept -> void {
- __apply(
- [&](auto __tag, auto&&... __args) noexcept -> void {
- __tag(static_cast<_Receiver&&>(__rcvr),
- __forward_like<_Tuple>(__args)...);
- },
- __tupl);
- };
-}
-
-enum class __action_kind : bool
-{
- __notify,
- __detach
-};
-
-struct __local_state_base : __immovable
-{
- using __action_fn = void(__local_state_base*, __action_kind) noexcept;
-
- __action_fn* __action_{};
- __local_state_base* __next_{};
-};
-
-template <class _CvrefSender, class _Env>
-struct __shared_state;
-
-// Each operation state of a split sender has one of these,
-// created when a split sender is connected. There are 0 or
-// more of them per input async operation. It is what
-// the split sender's `get_state` fn returns. It holds a
-// reference to the shared state of the input async operation.
-template <class _CvrefSender, class _Receiver>
-struct __local_state :
- __local_state_base,
- __enable_receiver_from_this<_CvrefSender, _Receiver>
-{
- using __data_t = __decay_t<__data_of<_CvrefSender>>;
- using __shared_state_t = __mapply<__q<__mfront>, __data_t>;
- using __on_stop_cb_t = //
- typename stop_token_of_t<env_of_t<_Receiver>&>::template callback_type<
- __on_stop_request>;
- using __tag_t = tag_of_t<_CvrefSender>;
- static_assert(__one_of<__tag_t, __split::__split_t,
- __ensure_started::__ensure_started_t>);
-
- explicit __local_state(_CvrefSender&& __sndr) noexcept :
- __local_state::__local_state_base{{},
- &__action<tag_of_t<_CvrefSender>>},
- __shared_state_(STDEXEC_CALL_EXPLICIT_THIS_MEMFN(
- static_cast<_CvrefSender&&>(__sndr),
- apply)(__detail::__get_data())
- .__shared_state)
- {}
-
- ~__local_state()
- {
- __action_(this, __action_kind::__detach);
- }
-
- // This is called when the input async operation completes; or,
- // if it has already completed when start is called, it is called
- // from start:
- template <class _Tag>
- static void __action(__local_state_base* __self,
- __action_kind __kind) noexcept
- {
- auto* const __op = static_cast<__local_state*>(__self);
- if (__kind == __action_kind::__notify)
- {
- __op->__on_stop_.reset();
-
- // The split algorithm sends by T const&. ensure_started sends by
- // T&&.
- if constexpr (same_as<__split::__split_t, _Tag>)
- {
- std::visit(__notify_visitor(__op->__receiver()),
- std::as_const(__op->__shared_state_->__data_));
- }
- else
- {
- std::visit(__notify_visitor(__op->__receiver()),
- std::move(__op->__shared_state_->__data_));
- }
- }
- else
- {
- // This is a detach operation
- if constexpr (same_as<__split::__split_t, _Tag>)
- {
- // no-op
- }
- else
- {
- __op->__shared_state_->__detach();
- }
- }
- }
-
- std::optional<__on_stop_cb_t> __on_stop_{};
- __intrusive_ptr<__shared_state_t> __shared_state_;
-};
-
-template <class _CvrefSenderId, class _EnvId>
-struct __receiver
-{
- using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
- using _Env = stdexec::__t<_EnvId>;
-
- struct __t
- {
- using receiver_concept = receiver_t;
- using __id = __receiver;
-
- explicit __t(
- __shared_state<_CvrefSender, _Env>* __shared_state) noexcept :
- __shared_state_(__shared_state)
- {}
-
- template <__completion_tag _Tag, class... _As>
- friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept
- {
- __shared_state<_CvrefSender, _Env>& __state =
- *__self.__shared_state_;
-
- try
- {
- using __tuple_t = __decayed_tuple<_Tag, _As...>;
- __state.__data_.template emplace<__tuple_t>(
- __tag, static_cast<_As&&>(__as)...);
- }
- catch (...)
- {
- using __tuple_t =
- __decayed_tuple<set_error_t, std::exception_ptr>;
- __state.__data_.template emplace<__tuple_t>(
- set_error, std::current_exception());
- }
-
- __state.__notify();
- }
-
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> const __env_t<_Env>&
- {
- return __self.__shared_state_->__env_;
- }
-
- __shared_state<_CvrefSender, _Env>* __shared_state_;
- };
-};
-
-template <class _CvrefSender, class _Env>
-struct __shared_state :
- __enable_intrusive_from_this<__shared_state<_CvrefSender, _Env>>
-{
- using __variant_t = __compl_sigs::__for_all_sigs<
- __completion_signatures_of_t<_CvrefSender, _Env>, __q<__decayed_tuple>,
- __mbind_front_q<__variant,
- std::tuple<set_stopped_t>, // Initial state of the
- // variant is set_stopped
- std::tuple<set_error_t, std::exception_ptr>>>;
-
- using __receiver_t = __t<__receiver<__cvref_id<_CvrefSender>, __id<_Env>>>;
-
- in_place_stop_source __stop_source_{};
- __variant_t __data_;
- std::atomic<void*> __head_{nullptr};
- __env_t<_Env> __env_;
- connect_result_t<_CvrefSender, __receiver_t> __op_state2_;
-
- explicit __shared_state(_CvrefSender&& __sndr, _Env __env) :
- __env_(__env::__join(
- __env::__with(__stop_source_.get_token(), get_stop_token),
- static_cast<_Env&&>(__env))),
- __op_state2_(
- connect(static_cast<_CvrefSender&&>(__sndr), __receiver_t{this}))
- {}
-
- void __start_op() noexcept
- {
- // the inner sender isn't running. if we reach here, then
- // one way or the other, __shared_state::__notify() will be
- // called, which decrements the ref count of *this.
- // So we need to increment it here:
- this->__inc_ref();
-
- if (__stop_source_.stop_requested())
- {
- // 1. resets __head to completion state
- // 2. notifies waiting threads
- // 3. propagates "stopped" signal to `out_r'`
- __notify();
- }
- else
- {
- stdexec::start(__op_state2_);
- }
- }
-
- // This is called when the shared async operation completes:
- void __notify() noexcept
- {
- void* const __completion_state = static_cast<void*>(this);
- void* const __old = __head_.exchange(__completion_state,
- std::memory_order_acq_rel);
- auto* __state = static_cast<__local_state_base*>(__old);
-
- while (__state != nullptr)
- {
- __local_state_base* __next = __state->__next_;
- __state->__action_(__state, __action_kind::__notify);
- __state = __next;
- }
-
- // The async operation has completed, so we can release our
- // ref-count on it:
- this->__dec_ref();
- }
-
- void __detach() noexcept
- {
- // Check to see if this operation was ever started. If not,
- // detach the (potentially still running) operation:
- if (nullptr == __head_.load(std::memory_order_acquire))
- {
- __stop_source_.request_stop();
- }
- }
-};
-
-template <class _Cvref, class _CvrefSenderId, class _EnvId>
-using __completions_t = //
- __try_make_completion_signatures<
- // NOT TO SPEC:
- // See https://github.com/cplusplus/sender-receiver/issues/23
- __cvref_t<_CvrefSenderId>, __env_t<__t<_EnvId>>,
- completion_signatures<set_error_t(
- __minvoke<_Cvref, std::exception_ptr>),
- set_stopped_t()>, // NOT TO SPEC
- __transform<_Cvref,
- __mcompose<__q<completion_signatures>, __qf<set_value_t>>>,
- __transform<_Cvref,
- __mcompose<__q<completion_signatures>, __qf<set_error_t>>>>;
-
-template <class _Ty>
-using __clref_t = const __decay_t<_Ty>&;
-
-template <class _Ty>
-using __rref_t = __decay_t<_Ty>&&;
-
-template <class _Tag>
-using __cvref_results_t = //
- __if_c<same_as<_Tag, __split::__split_t>, __q<__clref_t>, __q<__rref_t>>;
-
-template <class _Tag>
-inline auto __get_completion_signatures_fn() noexcept
-{ //
- return
- []<template <class> class _Data, class _ShState>(
- auto, const _Data<_ShState>&) //
- -> __mapply<__mbind_front_q<__completions_t, __cvref_results_t<_Tag>>,
- _ShState> { return {}; };
-}
-
-template <class _Tag>
-struct __shared_impl : __sexpr_defaults
-{
- static constexpr auto get_state = //
- []<class _Sender, class _Receiver>(
- _Sender&& __sndr,
- _Receiver&) noexcept -> __local_state<_Sender, _Receiver> {
- static_assert(sender_expr_for<_Sender, _Tag>);
- return __local_state<_Sender, _Receiver>{
- static_cast<_Sender&&>(__sndr)};
- };
-
- static constexpr auto get_completion_signatures = //
- []<class _Self, class _OtherEnv>(_Self&&, _OtherEnv&&) noexcept
- -> __call_result_t<__sexpr_apply_t, _Self,
- __result_of<__get_completion_signatures_fn<_Tag>>> {
- static_assert(sender_expr_for<_Self, _Tag>);
- return {};
- };
-
- static constexpr auto start = //
- []<class _Sender, class _Receiver>(
- __local_state<_Sender, _Receiver>& __state,
- _Receiver& __rcvr) noexcept -> void {
- auto* __shared_state = __state.__shared_state_.get();
- std::atomic<void*>& __head = __shared_state->__head_;
- void* const __completion_state = static_cast<void*>(__shared_state);
- void* __old = __head.load(std::memory_order_acquire);
-
- if (__old != __completion_state)
- {
- __state.__on_stop_.emplace(
- get_stop_token(stdexec::get_env(__rcvr)),
- __on_stop_request{__shared_state->__stop_source_});
-
- if constexpr (same_as<_Tag, __ensure_started::__ensure_started_t>)
- {
- // Check if the stop_source has requested cancellation
- if (__shared_state->__stop_source_.stop_requested())
- {
- // Stop has already been requested. Don't bother starting
- // the child operations.
- stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr));
- return;
- }
- }
- }
-
- // With the split algorithm, multiple split senders can be started
- // simultaneously, but only one should start the async operation. The
- // following loop atomically (re)tries to set the pointer to the head of
- // the list to __state. When it finally succeeds, the prior value is
- // checked. If it is nullptr, then this split sender has won the race
- // and has the honor of starting the async operation.
- do
- {
- if (__old == __completion_state)
- {
- __state.template __action<_Tag>(&__state,
- __action_kind::__notify);
- return;
- }
- __state.__next_ = static_cast<__local_state_base*>(__old);
- } while (!__head.compare_exchange_weak(
- __old, static_cast<void*>(&__state), std::memory_order_release,
- std::memory_order_acquire));
-
- if constexpr (same_as<_Tag, __split::__split_t>)
- {
- if (__old == nullptr)
- {
- __shared_state->__start_op();
- }
- }
- };
-};
-} // namespace __shared
-
-////////////////////////////////////////////////////////////////////////////
-// [execution.senders.adaptors.split]
-namespace __split
-{
-using namespace __shared;
-
-template <class _ShState>
-struct __data
-{
- explicit __data(__intrusive_ptr<_ShState> __shared_state) noexcept :
- __shared_state(std::move(__shared_state))
- {}
-
- __intrusive_ptr<_ShState> __shared_state;
-};
-
-struct __split_t
-{};
-
-struct split_t
-{
- template <sender _Sender, class _Env = empty_env>
- requires sender_in<_Sender, _Env> && __decay_copyable<env_of_t<_Sender>>
- auto operator()(_Sender&& __sndr, _Env&& __env = {}) const
- -> __well_formed_sender auto
- {
- auto __domain = __get_late_domain(__sndr, __env);
- return stdexec::transform_sender(
- __domain, __make_sexpr<split_t>(static_cast<_Env&&>(__env),
- static_cast<_Sender&&>(__sndr)));
- }
-
- STDEXEC_ATTRIBUTE((always_inline))
-
- __binder_back<split_t> operator()() const
- {
- return {{}, {}, {}};
- }
-
- using _Sender = __1;
- using __legacy_customizations_t = //
- __types<tag_invoke_t(split_t,
- get_completion_scheduler_t<set_value_t>(
- get_env_t(const _Sender&)),
- _Sender),
- tag_invoke_t(split_t, _Sender)>;
-
- template <class _CvrefSender, class _Env>
- using __receiver_t =
- __t<__meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>>>;
-
- template <class _Sender>
- static auto transform_sender(_Sender&& __sndr)
- {
- using _Receiver =
- __receiver_t<__child_of<_Sender>, __decay_t<__data_of<_Sender>>>;
- static_assert(sender_to<__child_of<_Sender>, _Receiver>);
- return __sexpr_apply(static_cast<_Sender&&>(__sndr),
- [&]<class _Env, class _Child>(
- __ignore, _Env&& __env, _Child&& __child) {
- auto __state =
- __make_intrusive<__shared_state<_Child, __decay_t<_Env>>>(
- static_cast<_Child&&>(__child), static_cast<_Env&&>(__env));
- return __make_sexpr<__split_t>(__data{std::move(__state)});
- });
- }
-};
-} // namespace __split
-
-using __split::split_t;
-inline constexpr split_t split{};
-
-template <>
-struct __sexpr_impl<__split::__split_t> :
- __shared::__shared_impl<__split::__split_t>
-{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.adaptors.ensure_started]
-namespace __ensure_started
-{
-using namespace __shared;
-
-// Each ensure_started sender has one of these, created when
-// ensure_started() is called.
-template <class _ShState>
-struct __data
-{
- explicit __data(__intrusive_ptr<_ShState> __ptr) noexcept :
- __shared_state(std::move(__ptr))
- {
- // Eagerly launch the async operation.
- __shared_state->__start_op();
- }
-
- __data(__data&&) noexcept = default;
- auto operator=(__data&&) noexcept -> __data& = default;
-
- ~__data()
- {
- if (__shared_state != nullptr)
- {
- // detach from the still-running operation.
- // NOT TO SPEC: This also requests cancellation.
- __shared_state->__detach();
- }
- }
-
- __intrusive_ptr<_ShState> __shared_state;
-};
-
-struct __ensure_started_t
-{};
-
-struct ensure_started_t
-{
- template <sender _Sender, class _Env = empty_env>
- requires sender_in<_Sender, _Env> && __decay_copyable<env_of_t<_Sender>>
- [[nodiscard]] auto operator()(_Sender&& __sndr, _Env&& __env = {}) const
- -> __well_formed_sender auto
- {
- if constexpr (sender_expr_for<_Sender, __ensure_started_t>)
- {
- return static_cast<_Sender&&>(__sndr);
- }
- else
- {
- auto __domain = __get_late_domain(__sndr, __env);
- return stdexec::transform_sender(
- __domain,
- __make_sexpr<ensure_started_t>(static_cast<_Env&&>(__env),
- static_cast<_Sender&&>(__sndr)));
- }
- }
-
- STDEXEC_ATTRIBUTE((always_inline))
-
- __binder_back<ensure_started_t> operator()() const
- {
- return {{}, {}, {}};
- }
-
- using _Sender = __1;
- using __legacy_customizations_t = //
- __types<tag_invoke_t(ensure_started_t,
- get_completion_scheduler_t<set_value_t>(
- get_env_t(const _Sender&)),
- _Sender),
- tag_invoke_t(ensure_started_t, _Sender)>;
-
- template <class _CvrefSender, class _Env>
- using __receiver_t =
- __t<__meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>>>;
-
- template <class _Sender>
- static auto transform_sender(_Sender&& __sndr)
- {
- using _Receiver =
- __receiver_t<__child_of<_Sender>, __decay_t<__data_of<_Sender>>>;
- static_assert(sender_to<__child_of<_Sender>, _Receiver>);
- return __sexpr_apply(static_cast<_Sender&&>(__sndr),
- [&]<class _Env, class _Child>(
- __ignore, _Env&& __env, _Child&& __child) {
- auto __state =
- __make_intrusive<__shared_state<_Child, __decay_t<_Env>>>(
- static_cast<_Child&&>(__child), static_cast<_Env&&>(__env));
- return __make_sexpr<__ensure_started_t>(__data{std::move(__state)});
- });
- }
-};
-} // namespace __ensure_started
-
-using __ensure_started::ensure_started_t;
-inline constexpr ensure_started_t ensure_started{};
-
-template <>
-struct __sexpr_impl<__ensure_started::__ensure_started_t> :
- __shared::__shared_impl<__ensure_started::__ensure_started_t>
-{};
-
-STDEXEC_PRAGMA_PUSH()
-STDEXEC_PRAGMA_IGNORE_EDG(not_used_in_partial_spec_arg_list)
-
-/////////////////////////////////////////////////////////////////////////////
-// a receiver adaptor that augments its environment
-namespace __detail
-{
-template <auto _ReceiverPtr, auto... _EnvFns>
-struct __receiver_with;
-
-template <class _Operation, class _Receiver,
- _Receiver _Operation::*_ReceiverPtr, auto... _EnvFns>
-struct __receiver_with<_ReceiverPtr, _EnvFns...>
-{
- struct __t : receiver_adaptor<__t>
- {
- using __id = __receiver_with;
- using __env_t = __env::__join_t<__result_of<_EnvFns, _Operation*>...,
- env_of_t<_Receiver>>;
-
- _Operation* __op_state_;
-
- auto base() && noexcept -> _Receiver&&
- {
- return static_cast<_Receiver&&>(__op_state_->*_ReceiverPtr);
- }
-
- auto get_env() const noexcept -> __env_t
- {
- return __env::__join(_EnvFns(__op_state_)...,
- stdexec::get_env(__op_state_->*_ReceiverPtr));
- }
- };
-};
-} // namespace __detail
-
-STDEXEC_PRAGMA_POP()
-
-//////////////////////////////////////////////////////////////////////////////
-// [exec.let]
-namespace __let
-{
-template <class _Set, class _Domain = dependent_domain>
-struct __let_t;
-
-template <class _Set>
-inline constexpr __mstring __in_which_let_msg{
- "In stdexec::let_value(Sender, Function)..."};
-
-template <>
-inline constexpr __mstring __in_which_let_msg<set_error_t>{
- "In stdexec::let_error(Sender, Function)..."};
-
-template <>
-inline constexpr __mstring __in_which_let_msg<set_stopped_t>{
- "In stdexec::let_stopped(Sender, Function)..."};
-
-template <class _Set>
-using __on_not_callable = __callable_error<__in_which_let_msg<_Set>>;
-
-// FUTURE: when we have a scheduler query for "always completes inline",
-// then we can use that instead of hard-coding `__inln::__scheduler` here.
-template <class _Scheduler>
-concept __unknown_context =
- __one_of<_Scheduler, __none_such, __inln::__scheduler>;
-
-template <class _Receiver, class _Scheduler>
-struct __receiver_with_sched
-{
- using receiver_concept = receiver_t;
- _Receiver __rcvr_;
- _Scheduler __sched_;
-
- template <__completion_tag _Tag, same_as<__receiver_with_sched> _Self,
- class... _As>
- friend void tag_invoke(_Tag, _Self&& __self, _As&&... __as) noexcept
- {
- _Tag()(static_cast<_Receiver&&>(__self.__rcvr_),
- static_cast<_As&&>(__as)...);
- }
-
- template <same_as<get_env_t> _Tag>
- friend auto tag_invoke(_Tag, const __receiver_with_sched& __self) noexcept
- {
- return __env::__join(
- __env::__with(__self.__sched_, get_scheduler),
- __env::__without(get_env(__self.__rcvr_), get_domain));
- }
-};
-
-template <class _Receiver, class _Scheduler>
-__receiver_with_sched(_Receiver, _Scheduler)
- -> __receiver_with_sched<_Receiver, _Scheduler>;
-
-// If the input sender knows its completion scheduler, make it the current
-// scheduler in the environment seen by the result sender.
-template <class _Env, class _Scheduler>
-using __result_env_t =
- __if_c<__unknown_context<_Scheduler>, _Env,
- __env::__join_t<__env::__with<_Scheduler, get_scheduler_t>,
- __env::__without_t<_Env, get_domain_t>>>;
-
-template <class _Tp>
-using __decay_ref = __decay_t<_Tp>&;
-
-template <__mstring _Where, __mstring _What>
-struct _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_
-{};
-
-#if STDEXEC_NVHPC()
-template <class _Sender, class _Env, class _Set>
-struct __bad_result_sender_
-{
- using __t = __mexception<
- _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_<
- __in_which_let_msg<_Set>,
- "The function must return a valid sender for the current environment"_mstr>,
- _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
-};
-template <class _Sender, class _Env, class _Set>
-using __bad_result_sender = __t<__bad_result_sender_<_Sender, _Env, _Set>>;
-#else
-template <class _Sender, class _Env, class _Set>
-using __bad_result_sender = __mexception<
- _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_<
- __in_which_let_msg<_Set>,
- "The function must return a valid sender for the current environment"_mstr>,
- _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
-#endif
-
-template <class _Sender, class _Env, class _Set>
-using __ensure_sender = //
- __minvoke_if_c<sender_in<_Sender, _Env>, __q<__midentity>,
- __mbind_back_q<__bad_result_sender, _Env, _Set>, _Sender>;
-
-// A metafunction that computes the result sender type for a given set of
-// argument types
-template <class _Fun, class _Set, class _Env, class _Sched>
-using __result_sender_fn = //
- __mcompose<
- __mbind_back_q<__ensure_sender, __result_env_t<_Env, _Sched>, _Set>,
- __transform<__q<__decay_ref>,
- __mbind_front<__mtry_catch_q<__call_result_t,
- __on_not_callable<_Set>>,
- _Fun>>>;
-
-// The receiver that gets connected to the result sender is the input receiver,
-// possibly augmented with the input sender's completion scheduler (which is
-// where the result sender will be started).
-template <class _Receiver, class _Scheduler>
-using __result_receiver_t =
- __if_c<__unknown_context<_Scheduler>, _Receiver,
- __receiver_with_sched<_Receiver, _Scheduler>>;
-
-template <class _Receiver, class _Fun, class _Set, class _Sched>
-using __op_state_for = //
- __mcompose<__mbind_back_q<connect_result_t,
- __result_receiver_t<_Receiver, _Sched>>,
- __result_sender_fn<_Fun, _Set, env_of_t<_Receiver>, _Sched>>;
-
-template <class _Set, class _Sig>
-struct __tfx_signal_fn
-{
- template <class, class, class>
- using __f = completion_signatures<_Sig>;
-};
-
-template <class _Set, class... _Args>
-struct __tfx_signal_fn<_Set, _Set(_Args...)>
-{
- template <class _Env, class _Fun, class _Sched>
- using __f = //
- __try_make_completion_signatures<
- __minvoke<__result_sender_fn<_Fun, _Set, _Env, _Sched>, _Args...>,
- __result_env_t<_Env, _Sched>,
- // because we don't know if connect-ing the result sender will
- // throw:
- completion_signatures<set_error_t(std::exception_ptr)>>;
-};
-
-// `_Sched` is the input sender's completion scheduler, or __none_such if it
-// doesn't have one.
-template <class _Env, class _Fun, class _Set, class _Sched, class _Sig>
-using __tfx_signal_t =
- __minvoke<__tfx_signal_fn<_Set, _Sig>, _Env, _Fun, _Sched>;
-
-template <class _Sender, class _Set>
-using __completion_sched = __query_result_or_t<get_completion_scheduler_t<_Set>,
- env_of_t<_Sender>, __none_such>;
-
-template <class _CvrefSender, class _Env, class _LetTag, class _Fun>
-using __completions = //
- __mapply<__transform<__mbind_front_q<
- __tfx_signal_t, _Env, _Fun, __t<_LetTag>,
- __completion_sched<_CvrefSender, __t<_LetTag>>>,
- __q<__concat_completion_signatures_t>>,
- __completion_signatures_of_t<_CvrefSender, _Env>>;
-
-template <__mstring _Where, __mstring _What>
-struct _NO_COMMON_DOMAIN_
-{};
-
-template <class _Set>
-using __no_common_domain_t = //
- _NO_COMMON_DOMAIN_<
- __in_which_let_msg<_Set>,
- "The senders returned by Function do not all share a common domain"_mstr>;
-
-template <class _Set>
-using __try_common_domain_fn = //
- __mtry_catch_q<
- __domain::__common_domain_t,
- __mcompose<__mbind_front_q<__mexception, __no_common_domain_t<_Set>>,
- __q<_WITH_SENDERS_>>>;
-
-// Compute all the domains of all the result senders and make sure they're all
-// the same
-template <class _Set, class _Child, class _Fun, class _Env, class _Sched>
-using __result_domain_t = //
- __gather_completions_for<_Set, _Child, _Env,
- __result_sender_fn<_Fun, _Set, _Env, _Sched>,
- __try_common_domain_fn<_Set>>;
-
-template <class _LetTag, class _Env>
-auto __mk_transform_env_fn(const _Env& __env) noexcept
-{
- using _Set = __t<_LetTag>;
- return [&]<class _Fun, class _Child>(__ignore, _Fun&&,
- _Child&& __child) -> decltype(auto) {
- using __completions_t = __completion_signatures_of_t<_Child, _Env>;
- if constexpr (__merror<__completions_t>)
- {
- return __completions_t();
- }
- else
- {
- using _Scheduler = __completion_sched<_Child, _Set>;
- if constexpr (__unknown_context<_Scheduler>)
- {
- return (__env);
- }
- else
- {
- return __env::__join(
- __env::__with(get_completion_scheduler<_Set>(
- stdexec::get_env(__child)),
- get_scheduler),
- __env::__without(__env, get_domain));
- }
- }
- };
-}
-
-template <class _LetTag, class _Env>
-auto __mk_transform_sender_fn(const _Env&) noexcept
-{
- using _Set = __t<_LetTag>;
- return
- []<class _Fun, class _Child>(__ignore, _Fun&& __fun, _Child&& __child) {
- using __completions_t = __completion_signatures_of_t<_Child, _Env>;
- if constexpr (__merror<__completions_t>)
- {
- return __completions_t();
- }
- else
- {
- using _Sched = __completion_sched<_Child, _Set>;
- using _Domain = __result_domain_t<_Set, _Child, _Fun, _Env, _Sched>;
- if constexpr (__merror<_Domain>)
- {
- return _Domain();
- }
- else if constexpr (same_as<_Domain, dependent_domain>)
- {
- using _Domain2 = __late_domain_of_t<_Child, _Env>;
- return __make_sexpr<__let_t<_Set, _Domain2>>(
- static_cast<_Fun&&>(__fun), static_cast<_Child&&>(__child));
- }
- else
- {
- static_assert(!same_as<_Domain, __none_such>);
- return __make_sexpr<__let_t<_Set, _Domain>>(
- static_cast<_Fun&&>(__fun), static_cast<_Child&&>(__child));
- }
- }
- };
-}
-
-template <class _Receiver, class _Fun, class _Set, class _Sched,
- class... _Tuples>
-struct __let_state
-{
- using __fun_t = _Fun;
- using __sched_t = _Sched;
-
- using __result_variant = std::variant<std::monostate, _Tuples...>;
-
- using __op_state_variant = //
- __minvoke<__transform<
- __uncurry<__op_state_for<_Receiver, _Fun, _Set, _Sched>>,
- __nullable_variant_t>,
- _Tuples...>;
-
- auto __get_result_receiver(_Receiver&& __rcvr) -> decltype(auto)
- {
- if constexpr (__unknown_context<_Sched>)
- {
- return static_cast<_Receiver&&>(__rcvr);
- }
- else
- {
- return __receiver_with_sched{static_cast<_Receiver&&>(__rcvr),
- this->__sched_};
- }
- }
-
- STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Fun __fun_;
- STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Sched __sched_;
- __result_variant __args_;
- __op_state_variant __op_state3_;
-};
-
-template <class _Set, class _Domain>
-struct __let_t
-{
- using __domain_t = _Domain;
- using __t = _Set;
-
- template <sender _Sender, __movable_value _Fun>
- auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender
- auto
- {
- auto __domain = __get_early_domain(__sndr);
- return stdexec::transform_sender(
- __domain,
- __make_sexpr<__let_t<_Set>>(static_cast<_Fun&&>(__fun),
- static_cast<_Sender&&>(__sndr)));
- }
-
- template <class _Fun>
- STDEXEC_ATTRIBUTE((always_inline))
- __binder_back<__let_t, _Fun> operator()(_Fun __fun) const
- {
- return {{}, {}, {static_cast<_Fun&&>(__fun)}};
- }
-
- using _Sender = __1;
- using _Function = __0;
- using __legacy_customizations_t =
- __types<tag_invoke_t(__let_t,
- get_completion_scheduler_t<set_value_t>(
- get_env_t(const _Sender&)),
- _Sender, _Function),
- tag_invoke_t(__let_t, _Sender, _Function)>;
-
- template <sender_expr_for<__let_t<_Set>> _Sender, class _Env>
- static auto transform_env(_Sender&& __sndr, const _Env& __env)
- -> decltype(auto)
- {
- return __sexpr_apply(static_cast<_Sender&&>(__sndr),
- __mk_transform_env_fn<__let_t<_Set>>(__env));
- }
-
- template <sender_expr_for<__let_t<_Set>> _Sender, class _Env>
- requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
- static auto transform_sender(_Sender&& __sndr, const _Env& __env)
- -> decltype(auto)
- {
- return __sexpr_apply(static_cast<_Sender&&>(__sndr),
- __mk_transform_sender_fn<__let_t<_Set>>(__env));
- }
-};
-
-template <class _Set, class _Domain>
-struct __let_impl : __sexpr_defaults
-{
- static constexpr auto get_attrs = //
- []<class _Child>(__ignore, const _Child& __child) noexcept {
- return __env::__join(__env::__with(_Domain(), get_domain),
- stdexec::get_env(__child));
- };
-
- static constexpr auto get_completion_signatures = //
- []<class _Self, class _Env>(_Self&&, _Env&&) noexcept
- -> __completions<__child_of<_Self>, _Env, __let_t<_Set, _Domain>,
- __data_of<_Self>> {
- static_assert(sender_expr_for<_Self, __let_t<_Set, _Domain>>);
- return {};
- };
-
- static constexpr auto get_state = //
- []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver&) {
- static_assert(sender_expr_for<_Sender, __let_t<_Set, _Domain>>);
- using _Fun = __data_of<_Sender>;
- using _Sched = __completion_sched<_Sender, _Set>;
- using __mk_let_state =
- __mbind_front_q<__let_state, _Receiver, _Fun, _Set, _Sched>;
-
- using __let_state_t =
- __gather_completions_for<_Set, __child_of<_Sender>,
- env_of_t<_Receiver>, __q<__decayed_tuple>,
- __mk_let_state>;
-
- _Sched __sched = query_or(get_completion_scheduler<_Set>,
- stdexec::get_env(__sndr), __none_such());
- return __let_state_t{
- STDEXEC_CALL_EXPLICIT_THIS_MEMFN(static_cast<_Sender&&>(__sndr),
- apply)(__detail::__get_data()),
- __sched};
- };
-
- template <class _State, class _Receiver, class... _As>
- static void __bind(_State& __state, _Receiver& __rcvr,
- _As&&... __as) noexcept
- {
- try
- {
- auto& __args =
- __state.__args_.template emplace<__decayed_tuple<_As...>>(
- static_cast<_As&&>(__as)...);
- auto __sndr2 = __apply(std::move(__state.__fun_), __args);
- auto __rcvr2 =
- __state.__get_result_receiver(static_cast<_Receiver&&>(__rcvr));
- auto __mkop = [&] {
- return stdexec::connect(std::move(__sndr2), std::move(__rcvr2));
- };
- auto& __op2 =
- __state.__op_state3_.template emplace<decltype(__mkop())>(
- __conv{__mkop});
- stdexec::start(__op2);
- }
- catch (...)
- {
- set_error(std::move(__rcvr), std::current_exception());
- }
- }
-
- static constexpr auto complete = //
- []<class _State, class _Receiver, class _Tag, class... _As>(
- __ignore, _State& __state, _Receiver& __rcvr, _Tag,
- _As&&... __as) noexcept -> void {
- if constexpr (std::same_as<_Tag, _Set>)
- {
- __bind(__state, __rcvr, static_cast<_As&&>(__as)...);
- }
- else
- {
- _Tag()(static_cast<_Receiver&&>(__rcvr),
- static_cast<_As&&>(__as)...);
- }
- };
-};
-} // namespace __let
-
-using let_value_t = __let::__let_t<set_value_t>;
-inline constexpr let_value_t let_value{};
-
-using let_error_t = __let::__let_t<set_error_t>;
-inline constexpr let_error_t let_error{};
-
-using let_stopped_t = __let::__let_t<set_stopped_t>;
-inline constexpr let_stopped_t let_stopped{};
-
-template <class _Set, class _Domain>
-struct __sexpr_impl<__let::__let_t<_Set, _Domain>> :
- __let::__let_impl<_Set, _Domain>
-{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.adaptors.stopped_as_optional]
-// [execution.senders.adaptors.stopped_as_error]
-namespace __stopped_as_xxx
-{
-struct stopped_as_optional_t
-{
- template <sender _Sender>
- auto operator()(_Sender&& __sndr) const
- {
- return __make_sexpr<stopped_as_optional_t>(
- __(), static_cast<_Sender&&>(__sndr));
- }
-
- STDEXEC_ATTRIBUTE((always_inline))
-
- __binder_back<stopped_as_optional_t> operator()() const noexcept
- {
- return {};
- }
-};
-
-struct __stopped_as_optional_impl : __sexpr_defaults
-{
- template <class... _Tys>
- requires(sizeof...(_Tys) == 1)
- using __set_value_t =
- completion_signatures<set_value_t(std::optional<__decay_t<_Tys>>...)>;
-
- template <class _Ty>
- using __set_error_t = completion_signatures<set_error_t(_Ty)>;
-
- static constexpr auto get_completion_signatures = //
- []<class _Self, class _Env>(_Self&&, _Env&&) noexcept //
- -> make_completion_signatures<
- __child_of<_Self>, _Env,
- completion_signatures<set_error_t(std::exception_ptr)>,
- __set_value_t, __set_error_t, completion_signatures<>> {
- static_assert(sender_expr_for<_Self, stopped_as_optional_t>);
- return {};
- };
-
- static constexpr auto get_state = //
- []<class _Self, class _Receiver>(_Self&&, _Receiver&) noexcept
- requires __single_typed_sender<__child_of<_Self>, env_of_t<_Receiver>>
- {
- static_assert(sender_expr_for<_Self, stopped_as_optional_t>);
- using _Value = __decay_t<
- __single_sender_value_t<__child_of<_Self>, env_of_t<_Receiver>>>;
- return __mtype<_Value>();
- };
-
- static constexpr auto complete = //
- []<class _State, class _Receiver, __completion_tag _Tag,
- class... _Args>(__ignore, _State&, _Receiver& __rcvr, _Tag,
- _Args&&... __args) noexcept -> void {
- if constexpr (same_as<_Tag, set_value_t>)
- {
- try
- {
- static_assert(constructible_from<__t<_State>, _Args...>);
- stdexec::set_value(static_cast<_Receiver&&>(__rcvr),
- std::optional<__t<_State>>{
- static_cast<_Args&&>(__args)...});
- }
- catch (...)
- {
- stdexec::set_error(static_cast<_Receiver&&>(__rcvr),
- std::current_exception());
- }
- }
- else if constexpr (same_as<_Tag, set_error_t>)
- {
- stdexec::set_error(static_cast<_Receiver&&>(__rcvr),
- static_cast<_Args&&>(__args)...);
- }
- else
- {
- stdexec::set_value(static_cast<_Receiver&&>(__rcvr),
- std::optional<__t<_State>>{std::nullopt});
- }
- };
-};
-
-struct stopped_as_error_t
-{
- template <sender _Sender, __movable_value _Error>
- auto operator()(_Sender&& __sndr, _Error __err) const
- {
- return static_cast<_Sender&&>(__sndr) |
- let_stopped(
- [__err2 = static_cast<_Error&&>(__err)]() mutable //
- noexcept(std::is_nothrow_move_constructible_v<_Error>) {
- return just_error(static_cast<_Error&&>(__err2));
- });
- }
-
- template <__movable_value _Error>
- STDEXEC_ATTRIBUTE((always_inline))
- auto operator()(_Error __err) const
- -> __binder_back<stopped_as_error_t, _Error>
- {
- return {{}, {}, {static_cast<_Error&&>(__err)}};
- }
-};
-} // namespace __stopped_as_xxx
-
-using __stopped_as_xxx::stopped_as_optional_t;
-inline constexpr stopped_as_optional_t stopped_as_optional{};
-using __stopped_as_xxx::stopped_as_error_t;
-inline constexpr stopped_as_error_t stopped_as_error{};
-
-template <>
-struct __sexpr_impl<stopped_as_optional_t> :
- __stopped_as_xxx::__stopped_as_optional_impl
-{};
-
-/////////////////////////////////////////////////////////////////////////////
-// run_loop
-namespace __loop
-{
-class run_loop;
-
-struct __task : __immovable
-{
- __task* __next_ = this;
-
- union
- {
- void (*__execute_)(__task*) noexcept;
- __task* __tail_;
- };
-
- void __execute() noexcept
- {
- (*__execute_)(this);
- }
-};
-
-template <class _ReceiverId>
-struct __operation
-{
- using _Receiver = stdexec::__t<_ReceiverId>;
-
- struct __t : __task
- {
- using __id = __operation;
-
- run_loop* __loop_;
- STDEXEC_ATTRIBUTE((no_unique_address))
- _Receiver __rcvr_;
-
- static void __execute_impl(__task* __p) noexcept
- {
- auto& __rcvr = static_cast<__t*>(__p)->__rcvr_;
- try
- {
- if (get_stop_token(get_env(__rcvr)).stop_requested())
- {
- set_stopped(static_cast<_Receiver&&>(__rcvr));
- }
- else
- {
- set_value(static_cast<_Receiver&&>(__rcvr));
- }
- }
- catch (...)
- {
- set_error(static_cast<_Receiver&&>(__rcvr),
- std::current_exception());
- }
- }
-
- explicit __t(__task* __tail) noexcept : __task{.__tail_ = __tail} {}
-
- __t(__task* __next, run_loop* __loop, _Receiver __rcvr) :
- __task{{}, __next, {&__execute_impl}}, __loop_{__loop},
- __rcvr_{static_cast<_Receiver&&>(__rcvr)}
- {}
-
- friend void tag_invoke(start_t, __t& __self) noexcept
- {
- __self.__start_();
- }
-
- void __start_() noexcept;
- };
-};
-
-class run_loop
-{
- template <class... Ts>
- using __completion_signatures_ = completion_signatures<Ts...>;
-
- template <class>
- friend struct __operation;
-
- public:
- struct __scheduler
- {
- using __t = __scheduler;
- using __id = __scheduler;
- auto operator==(const __scheduler&) const noexcept -> bool = default;
-
- private:
- struct __schedule_task
- {
- using __t = __schedule_task;
- using __id = __schedule_task;
- using sender_concept = sender_t;
- using completion_signatures = //
- __completion_signatures_<set_value_t(),
- set_error_t(std::exception_ptr),
- set_stopped_t()>;
-
- private:
- friend __scheduler;
-
- template <class _Receiver>
- using __operation =
- stdexec::__t<__operation<stdexec::__id<_Receiver>>>;
-
- template <class _Receiver>
- friend auto tag_invoke(connect_t, const __schedule_task& __self,
- _Receiver __rcvr) -> __operation<_Receiver>
- {
- return __self.__connect_(static_cast<_Receiver&&>(__rcvr));
- }
-
- template <class _Receiver>
- auto __connect_(_Receiver&& __rcvr) const -> __operation<_Receiver>
- {
- return {&__loop_->__head_, __loop_,
- static_cast<_Receiver&&>(__rcvr)};
- }
-
- struct __env
- {
- run_loop* __loop_;
-
- template <class _CPO>
- friend auto tag_invoke(get_completion_scheduler_t<_CPO>,
- const __env& __self) noexcept
- -> __scheduler
- {
- return __self.__loop_->get_scheduler();
- }
- };
-
- friend auto tag_invoke(get_env_t,
- const __schedule_task& __self) noexcept
- -> __env
- {
- return __env{__self.__loop_};
- }
-
- explicit __schedule_task(run_loop* __loop) noexcept :
- __loop_(__loop)
- {}
-
- run_loop* const __loop_;
- };
-
- friend run_loop;
-
- explicit __scheduler(run_loop* __loop) noexcept : __loop_(__loop) {}
-
- friend auto tag_invoke(schedule_t, const __scheduler& __self) noexcept
- -> __schedule_task
- {
- return __self.__schedule();
- }
-
- friend auto tag_invoke(get_forward_progress_guarantee_t,
- const __scheduler&) noexcept
- -> stdexec::forward_progress_guarantee
- {
- return stdexec::forward_progress_guarantee::parallel;
- }
-
- // BUGBUG NOT TO SPEC
- friend auto tag_invoke(execute_may_block_caller_t,
- const __scheduler&) noexcept -> bool
- {
- return false;
- }
-
- [[nodiscard]] auto __schedule() const noexcept -> __schedule_task
- {
- return __schedule_task{__loop_};
- }
-
- run_loop* __loop_;
- };
-
- auto get_scheduler() noexcept -> __scheduler
- {
- return __scheduler{this};
- }
-
- void run();
-
- void finish();
-
- private:
- void __push_back_(__task* __task);
- auto __pop_front_() -> __task*;
-
- std::mutex __mutex_;
- std::condition_variable __cv_;
- __task __head_{.__tail_ = &__head_};
- bool __stop_ = false;
-};
-
-template <class _ReceiverId>
-inline void __operation<_ReceiverId>::__t::__start_() noexcept
-{
- try
- {
- __loop_->__push_back_(this);
- }
- catch (...)
- {
- set_error(static_cast<_Receiver&&>(__rcvr_), std::current_exception());
- }
-}
-
-inline void run_loop::run()
-{
- for (__task* __task; (__task = __pop_front_()) != &__head_;)
- {
- __task->__execute();
- }
-}
-
-inline void run_loop::finish()
-{
- std::unique_lock __lock{__mutex_};
- __stop_ = true;
- __cv_.notify_all();
-}
-
-inline void run_loop::__push_back_(__task* __task)
-{
- std::unique_lock __lock{__mutex_};
- __task->__next_ = &__head_;
- __head_.__tail_ = __head_.__tail_->__next_ = __task;
- __cv_.notify_one();
-}
-
-inline auto run_loop::__pop_front_() -> __task*
-{
- std::unique_lock __lock{__mutex_};
- __cv_.wait(__lock,
- [this] { return __head_.__next_ != &__head_ || __stop_; });
- if (__head_.__tail_ == __head_.__next_)
- __head_.__tail_ = &__head_;
- return std::exchange(__head_.__next_, __head_.__next_->__next_);
-}
-} // namespace __loop
-
-// NOT TO SPEC
-using run_loop = __loop::run_loop;
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.adaptors.schedule_from]
-namespace __schedule_from
-{
-// Compute a variant type that is capable of storing the results of the
-// input sender when it completes. The variant has type:
-// variant<
-// monostate,
-// tuple<set_stopped_t>,
-// tuple<set_value_t, __decay_t<_Values1>...>,
-// tuple<set_value_t, __decay_t<_Values2>...>,
-// ...
-// tuple<set_error_t, __decay_t<_Error1>>,
-// tuple<set_error_t, __decay_t<_Error2>>,
-// ...
-// >
-template <class _CvrefSender, class _Env>
-using __variant_for_t = __compl_sigs::__maybe_for_all_sigs<
- __completion_signatures_of_t<_CvrefSender, _Env>, __q<__decayed_tuple>,
- __nullable_variant_t>;
-
-template <class _Tp>
-using __decay_rvalue_ref = __decay_t<_Tp>&&;
-
-template <class _Tag>
-using __decay_signature =
- __transform<__q<__decay_rvalue_ref>,
- __mcompose<__q<completion_signatures>, __qf<_Tag>>>;
-
-template <class... _Ts>
-using __all_nothrow_decay_copyable_ =
- __mbool<(__nothrow_decay_copyable<_Ts> && ...)>;
-
-template <class _CvrefSender, class _Env>
-using __all_values_and_errors_nothrow_decay_copyable = //
- __compl_sigs::__maybe_for_all_sigs<
- __completion_signatures_of_t<_CvrefSender, _Env>,
- __q<__all_nothrow_decay_copyable_>, __q<__mand>>;
-
-template <class _CvrefSender, class _Env>
-using __with_error_t = //
- __if<__all_values_and_errors_nothrow_decay_copyable<_CvrefSender, _Env>,
- completion_signatures<>, __with_exception_ptr>;
-
-template <class _Scheduler, class _CvrefSender, class _Env>
-using __completions_t = //
- __try_make_completion_signatures<
- _CvrefSender, _Env,
- __try_make_completion_signatures<schedule_result_t<_Scheduler>, _Env,
- __with_error_t<_CvrefSender, _Env>,
- __mconst<completion_signatures<>>>,
- __decay_signature<set_value_t>, __decay_signature<set_error_t>>;
-
-template <class _SchedulerId>
-struct __environ
-{
- using _Scheduler = stdexec::__t<_SchedulerId>;
-
- struct __t :
- __env::__with<stdexec::__t<_SchedulerId>,
- get_completion_scheduler_t<set_value_t>,
- get_completion_scheduler_t<set_stopped_t>>
- {
- using __id = __environ;
-
- explicit __t(_Scheduler __sched) noexcept :
- __t::__with{std::move(__sched)}
- {}
-
- template <same_as<get_domain_t> _Key>
- friend auto tag_invoke(_Key, const __t& __self) noexcept
- {
- return query_or(get_domain, __self.__value_, default_domain());
- }
- };
-};
-
-template <class _Scheduler, class _Sexpr, class _Receiver>
-struct __state;
-
-// This receiver is to be completed on the execution context
-// associated with the scheduler. When the source sender
-// completes, the completion information is saved off in the
-// operation state so that when this receiver completes, it can
-// read the completion out of the operation state and forward it
-// to the output receiver after transitioning to the scheduler's
-// context.
-template <class _Scheduler, class _Sexpr, class _Receiver>
-struct __receiver2 :
- receiver_adaptor<__receiver2<_Scheduler, _Sexpr, _Receiver>>
-{
- explicit __receiver2(
- __state<_Scheduler, _Sexpr, _Receiver>* __state) noexcept :
- __state_{__state}
- {}
-
- auto base() && noexcept -> _Receiver&&
- {
- return std::move(__state_->__receiver());
- }
-
- auto base() const& noexcept -> const _Receiver&
- {
- return __state_->__receiver();
- }
-
- void set_value() && noexcept
- {
- STDEXEC_ASSERT(!__state_->__data_.valueless_by_exception());
- std::visit(
- [__state = __state_]<class _Tup>(_Tup& __tupl) noexcept -> void {
- if constexpr (same_as<_Tup, std::monostate>)
- {
- std::terminate(); // reaching this indicates a bug in
- // schedule_from
- }
- else
- {
- __apply(
- [&]<class... _Args>(auto __tag,
- _Args&... __args) noexcept -> void {
- __tag(std::move(__state->__receiver()),
- static_cast<_Args&&>(__args)...);
- },
- __tupl);
- }
- },
- __state_->__data_);
- }
-
- __state<_Scheduler, _Sexpr, _Receiver>* __state_;
-};
-
-template <class _Scheduler, class _Sexpr, class _Receiver>
-struct __state : __enable_receiver_from_this<_Sexpr, _Receiver>
-{
- using __variant_t =
- __variant_for_t<__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_;
-
- explicit __state(_Scheduler __sched) :
- __data_(), __state2_(connect(schedule(__sched), __receiver2_t{this}))
- {}
-};
-
-struct schedule_from_t
-{
- template <scheduler _Scheduler, sender _Sender>
- auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
- -> __well_formed_sender auto
- {
- using _Env = __t<__environ<__id<__decay_t<_Scheduler>>>>;
- auto __env = _Env{{static_cast<_Scheduler&&>(__sched)}};
- auto __domain = query_or(get_domain, __sched, default_domain());
- return stdexec::transform_sender(
- __domain, __make_sexpr<schedule_from_t>(
- std::move(__env), static_cast<_Sender&&>(__sndr)));
- }
-
- using _Sender = __1;
- using _Env = __0;
- using __legacy_customizations_t = __types<tag_invoke_t(
- schedule_from_t, get_completion_scheduler_t<set_value_t>(_Env&),
- _Sender)>;
-};
-
-struct __schedule_from_impl : __sexpr_defaults
-{
- template <class _Sender>
- using __scheduler_t =
- __decay_t<__call_result_t<get_completion_scheduler_t<set_value_t>,
- env_of_t<_Sender>>>;
-
- static constexpr auto get_attrs = //
- []<class _Data, class _Child>(const _Data& __data,
- const _Child& __child) noexcept {
- return __env::__join(__data, stdexec::get_env(__child));
- };
-
- static constexpr auto get_completion_signatures = //
- []<class _Sender, class _Env>(_Sender&&, const _Env&) noexcept
- -> __completions_t<__scheduler_t<_Sender>, __child_of<_Sender>, _Env> {
- static_assert(sender_expr_for<_Sender, schedule_from_t>);
- return {};
- };
-
- static constexpr auto get_state =
- []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver&) {
- static_assert(sender_expr_for<_Sender, schedule_from_t>);
- auto __sched =
- get_completion_scheduler<set_value_t>(stdexec::get_env(__sndr));
- using _Scheduler = decltype(__sched);
- return __state<_Scheduler, _Sender, _Receiver>{__sched};
- };
-
- static constexpr auto complete =
- []<class _Tag, class... _Args>(__ignore, auto& __state, auto& __rcvr,
- _Tag,
- _Args&&... __args) noexcept -> void {
- // Write the tag and the args into the operation state so that
- // we can forward the completion from within the scheduler's
- // execution context.
- using __async_result = __decayed_tuple<_Tag, _Args...>;
- if constexpr (__nothrow_constructible_from<__async_result, _Tag,
- _Args...>)
- {
- __state.__data_.template emplace<__async_result>(
- _Tag(), static_cast<_Args&&>(__args)...);
- }
- else
- {
- try
- {
- __state.__data_.template emplace<__async_result>(
- _Tag(), static_cast<_Args&&>(__args)...);
- }
- catch (...)
- {
- set_error(std::move(__rcvr), std::current_exception());
- return;
- }
- }
- // Enqueue the schedule operation so the completion happens
- // on the scheduler's execution context.
- stdexec::start(__state.__state2_);
- };
-};
-} // namespace __schedule_from
-
-using __schedule_from::schedule_from_t;
-inline constexpr schedule_from_t schedule_from{};
-
-template <>
-struct __sexpr_impl<schedule_from_t> : __schedule_from::__schedule_from_impl
-{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.adaptors.transfer]
-namespace __transfer
-{
-using __schedule_from::__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 transfer_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<transfer_t>(_Env{{static_cast<_Scheduler&&>(__sched)}},
- static_cast<_Sender&&>(__sndr)));
- }
-
- template <scheduler _Scheduler>
- STDEXEC_ATTRIBUTE((always_inline))
- __binder_back<transfer_t, __decay_t<_Scheduler>>
- operator()(_Scheduler&& __sched) const
- {
- return {{}, {}, {static_cast<_Scheduler&&>(__sched)}};
- }
-
- //////////////////////////////////////////////////////////////////////////////////////////////
- using _Env = __0;
- using _Sender = __1;
- using __legacy_customizations_t = //
- __types<tag_invoke_t(transfer_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(transfer_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 __transfer_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));
- };
-};
-} // namespace __transfer
-
-using __transfer::transfer_t;
-inline constexpr transfer_t transfer{};
-
-template <>
-struct __sexpr_impl<transfer_t> : __transfer::__transfer_impl
-{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.transfer_just]
-namespace __transfer_just
-{
-// This is a helper for finding legacy cusutomizations of transfer_just.
-inline auto __transfer_just_tag_invoke()
-{
- return []<class... _Ts>(
- _Ts&&... __ts) -> tag_invoke_result_t<transfer_just_t, _Ts...> {
- return tag_invoke(transfer_just, static_cast<_Ts&&>(__ts)...);
- };
-}
-
-template <class _Env>
-auto __make_transform_fn(const _Env& __env)
-{
- return [&]<class _Scheduler, class... _Values>(_Scheduler&& __sched,
- _Values&&... __vals) {
- return transfer(just(static_cast<_Values&&>(__vals)...),
- static_cast<_Scheduler&&>(__sched));
- };
-}
-
-template <class _Env>
-auto __transform_sender_fn(const _Env& __env)
-{
- return [&]<class _Data>(__ignore, _Data&& __data) {
- return __apply(__make_transform_fn(__env),
- static_cast<_Data&&>(__data));
- };
-}
-
-struct transfer_just_t
-{
- using _Data = __0;
- using __legacy_customizations_t = //
- __types<__apply_t(decltype(__transfer_just_tag_invoke()), _Data)>;
-
- template <scheduler _Scheduler, __movable_value... _Values>
- auto operator()(_Scheduler&& __sched, _Values&&... __vals) const
- -> __well_formed_sender auto
- {
- auto __domain = query_or(get_domain, __sched, default_domain());
- return stdexec::transform_sender(
- __domain, __make_sexpr<transfer_just_t>(
- std::tuple{static_cast<_Scheduler&&>(__sched),
- static_cast<_Values&&>(__vals)...}));
- }
-
- 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));
- }
-};
-
-inline auto __make_env_fn() noexcept
-{
- return []<class _Scheduler>(const _Scheduler& __sched,
- const auto&...) noexcept {
- using _Env = __t<__schedule_from::__environ<__id<_Scheduler>>>;
- return _Env{__sched};
- };
-}
-
-struct __transfer_just_impl : __sexpr_defaults
-{
- static constexpr auto get_attrs = //
- []<class _Data>(const _Data& __data) noexcept {
- return __apply(__make_env_fn(), __data);
- };
-};
-} // namespace __transfer_just
-
-using __transfer_just::transfer_just_t;
-inline constexpr transfer_just_t transfer_just{};
-
-template <>
-struct __sexpr_impl<transfer_just_t> : __transfer_just::__transfer_just_impl
-{};
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-// __write adaptor
-namespace __write_
-{
-struct __write_t
-{
- template <sender _Sender, class... _Envs>
- auto operator()(_Sender&& __sndr, _Envs... __envs) const
- {
- return __make_sexpr<__write_t>(__env::__join(std::move(__envs)...),
- static_cast<_Sender&&>(__sndr));
- }
-
- template <class... _Envs>
- STDEXEC_ATTRIBUTE((always_inline))
- auto operator()(_Envs... __envs) const -> __binder_back<__write_t, _Envs...>
- {
- return {{}, {}, {std::move(__envs)...}};
- }
-
- template <class _Env>
- STDEXEC_ATTRIBUTE((always_inline))
- static auto __transform_env_fn(_Env&& __env) noexcept
- {
- return [&](__ignore, const auto& __state, __ignore) noexcept {
- return __env::__join(__state, static_cast<_Env&&>(__env));
- };
- }
-
- template <sender_expr_for<__write_t> _Self, class _Env>
- static auto transform_env(const _Self& __self, _Env&& __env) noexcept
- {
- return __sexpr_apply(__self,
- __transform_env_fn(static_cast<_Env&&>(__env)));
- }
-};
-
-struct __write_impl : __sexpr_defaults
-{
- static constexpr auto get_env = //
- [](__ignore, const auto& __state, const auto& __rcvr) noexcept {
- return __env::__join(__state, stdexec::get_env(__rcvr));
- };
-
- static constexpr auto get_completion_signatures = //
- []<class _Self, class _Env>(_Self&&, _Env&&) noexcept
- -> stdexec::__completion_signatures_of_t<
- __child_of<_Self>,
- __env::__join_t<const __decay_t<__data_of<_Self>>&, _Env>> {
- static_assert(sender_expr_for<_Self, __write_t>);
- return {};
- };
-};
-} // namespace __write_
-
-using __write_::__write_t;
-inline constexpr __write_t __write{};
-
-template <>
-struct __sexpr_impl<__write_t> : __write_::__write_impl
-{};
-
-namespace __detail
-{
-template <class _Env, class _Scheduler>
-STDEXEC_ATTRIBUTE((always_inline))
-auto __mkenv_sched(_Env&& __env, _Scheduler __sched)
-{
- auto __env2 =
- __env::__join(__env::__with(__sched, get_scheduler),
- __env::__without(static_cast<_Env&&>(__env), get_domain));
- using _Env2 = decltype(__env2);
-
- struct __env_t : _Env2
- {};
-
- return __env_t{static_cast<_Env2&&>(__env2)};
-}
-
-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.on]
-namespace __on
-{
-struct on_t
-{
- using _Sender = __1;
- using _Scheduler = __0;
- using __legacy_customizations_t =
- __types<tag_invoke_t(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<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) {
- return let_value(
- schedule(__data),
- __detail::__always{static_cast<_Child&&>(__child)});
- });
- }
-};
-} // namespace __on
-
-using __on::on_t;
-inline constexpr on_t on{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.adaptors.into_variant]
-namespace __into_variant
-{
-template <class _Sender, class _Env>
- requires sender_in<_Sender, _Env>
-using __into_variant_result_t = value_types_of_t<_Sender, _Env>;
-
-template <class _Sender, class _Env>
-using __variant_t = __try_value_types_of_t<_Sender, _Env>;
-
-template <class _Variant>
-using __variant_completions =
- completion_signatures<set_value_t(_Variant),
- set_error_t(std::exception_ptr)>;
-
-template <class _Sender, class _Env>
-using __compl_sigs = //
- __try_make_completion_signatures<
- _Sender, _Env,
- __meval<__variant_completions, __variant_t<_Sender, _Env>>,
- __mconst<completion_signatures<>>>;
-
-struct into_variant_t
-{
- template <sender _Sender>
- auto operator()(_Sender&& __sndr) const -> __well_formed_sender auto
- {
- auto __domain = __get_early_domain(__sndr);
- return stdexec::transform_sender(
- __domain,
- __make_sexpr<into_variant_t>(__(), std::forward<_Sender>(__sndr)));
- }
-
- STDEXEC_ATTRIBUTE((always_inline))
-
- auto operator()() const noexcept
- {
- return __binder_back<into_variant_t>{};
- }
-};
-
-struct __into_variant_impl : __sexpr_defaults
-{
- static constexpr auto get_state = //
- []<class _Self, class _Receiver>(_Self&&, _Receiver&) noexcept {
- using __variant_t =
- value_types_of_t<__child_of<_Self>, env_of_t<_Receiver>>;
- return __mtype<__variant_t>();
- };
-
- static constexpr auto complete = //
- []<class _State, class _Receiver, class _Tag, class... _Args>(
- __ignore, _State, _Receiver& __rcvr, _Tag,
- _Args&&... __args) noexcept -> void {
- if constexpr (same_as<_Tag, set_value_t>)
- {
- using __variant_t = __t<_State>;
- try
- {
- set_value(static_cast<_Receiver&&>(__rcvr),
- __variant_t{std::tuple<_Args&&...>{
- static_cast<_Args&&>(__args)...}});
- }
- catch (...)
- {
- set_error(static_cast<_Receiver&&>(__rcvr),
- std::current_exception());
- }
- }
- else
- {
- _Tag()(static_cast<_Receiver&&>(__rcvr),
- static_cast<_Args&&>(__args)...);
- }
- };
-
- static constexpr auto get_completion_signatures = //
- []<class _Self, class _Env>(_Self&&, _Env&&) noexcept //
- -> __compl_sigs<__child_of<_Self>, _Env> {
- static_assert(sender_expr_for<_Self, into_variant_t>);
- return {};
- };
-};
-} // namespace __into_variant
-
-using __into_variant::into_variant_t;
-inline constexpr into_variant_t into_variant{};
-
-template <>
-struct __sexpr_impl<into_variant_t> : __into_variant::__into_variant_impl
-{};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.adaptors.when_all]
-// [execution.senders.adaptors.when_all_with_variant]
-namespace __when_all
-{
-enum __state_t
-{
- __started,
- __error,
- __stopped
-};
-
-struct __on_stop_request
-{
- in_place_stop_source& __stop_source_;
-
- void operator()() noexcept
- {
- __stop_source_.request_stop();
- }
-};
-
-template <class _Env>
-auto __mkenv(_Env&& __env, in_place_stop_source& __stop_source) noexcept
-{
- return __env::__join(
- __env::__with(__stop_source.get_token(), get_stop_token),
- static_cast<_Env&&>(__env));
-}
-
-template <class _Env>
-using __env_t = //
- decltype(__mkenv(__declval<_Env>(), __declval<in_place_stop_source&>()));
-
-template <class _Tp>
-using __decay_rvalue_ref = __decay_t<_Tp>&&;
-
-template <class _Sender, class _Env>
-concept __max1_sender = sender_in<_Sender, _Env> &&
- __mvalid<__value_types_of_t, _Sender, _Env,
- __mconst<int>, __msingle_or<void>>;
-
-template <
- __mstring _Context = "In stdexec::when_all()..."_mstr,
- __mstring _Diagnostic =
- "The given sender can complete successfully in more that one way. "
- "Use stdexec::when_all_with_variant() instead."_mstr>
-struct _INVALID_WHEN_ALL_ARGUMENT_;
-
-template <class _Sender, class _Env>
-using __too_many_value_completions_error =
- __mexception<_INVALID_WHEN_ALL_ARGUMENT_<>, _WITH_SENDER_<_Sender>,
- _WITH_ENVIRONMENT_<_Env>>;
-
-template <class _Sender, class _Env, class _ValueTuple, class... _Rest>
-using __value_tuple_t =
- __minvoke<__if_c<(0 == sizeof...(_Rest)), __mconst<_ValueTuple>,
- __q<__too_many_value_completions_error>>,
- _Sender, _Env>;
-
-template <class _Env, class _Sender>
-using __single_values_of_t = //
- __try_value_types_of_t<_Sender, _Env,
- __transform<__q<__decay_rvalue_ref>, __q<__types>>,
- __mbind_front_q<__value_tuple_t, _Sender, _Env>>;
-
-template <class _Env, class... _Senders>
-using __set_values_sig_t = //
- __meval<completion_signatures,
- __minvoke<__mconcat<__qf<set_value_t>>,
- __single_values_of_t<_Env, _Senders>...>>;
-
-template <class... _Args>
-using __all_nothrow_decay_copyable_ =
- __mbool<(__nothrow_decay_copyable<_Args> && ...)>;
-
-template <class _Env, class... _Senders>
-using __all_nothrow_decay_copyable = //
- __mand<__compl_sigs::__maybe_for_all_sigs<
- __completion_signatures_of_t<_Senders, _Env>,
- __q<__all_nothrow_decay_copyable_>, __q<__mand>>...>;
-
-template <class _Env, class... _Senders>
-using __completions_t = //
- __concat_completion_signatures_t<
- __if<__all_nothrow_decay_copyable<_Env, _Senders...>,
- completion_signatures<set_stopped_t()>,
- completion_signatures<set_stopped_t(),
- set_error_t(std::exception_ptr&&)>>,
- __minvoke<__with_default<__mbind_front_q<__set_values_sig_t, _Env>,
- completion_signatures<>>,
- _Senders...>,
- __try_make_completion_signatures<
- _Senders, _Env, completion_signatures<>,
- __mconst<completion_signatures<>>,
- __mcompose<__q<completion_signatures>, __qf<set_error_t>,
- __q<__decay_rvalue_ref>>>...>;
-
-struct __not_an_error
-{};
-
-struct __tie_fn
-{
- template <class... _Ty>
- auto operator()(_Ty&... __vals) noexcept -> std::tuple<_Ty&...>
- {
- return std::tuple<_Ty&...>{__vals...};
- }
-};
-
-template <class _Tag, class _Receiver>
-auto __complete_fn(_Tag, _Receiver& __rcvr) noexcept
-{
- return [&]<class... _Ts>(_Ts&... __ts) noexcept {
- if constexpr (!same_as<__types<_Ts...>, __types<__not_an_error>>)
- {
- _Tag()(static_cast<_Receiver&&>(__rcvr),
- static_cast<_Ts&&>(__ts)...);
- }
- };
-}
-
-template <class _Receiver, class _ValuesTuple>
-void __set_values(_Receiver& __rcvr, _ValuesTuple& __values) noexcept
-{
- __tup::__apply(
- [&](auto&... __opt_vals) noexcept -> void {
- __apply(__complete_fn(set_value, __rcvr), //
- std::tuple_cat(__tup::__apply(__tie_fn{}, *__opt_vals)...));
- },
- __values);
-}
-
-template <class... Ts>
-using __decayed_custom_tuple = __tup::__tuple_for<__decay_t<Ts>...>;
-
-template <class _Env, class _Sender>
-using __values_opt_tuple_t = //
- value_types_of_t<_Sender, __env_t<_Env>, __decayed_custom_tuple,
- std::optional>;
-
-template <class _Env, __max1_sender<__env_t<_Env>>... _Senders>
-struct __traits
-{
- // tuple<optional<tuple<Vs1...>>, optional<tuple<Vs2...>>, ...>
- using __values_tuple = //
- __minvoke<__with_default<
- __transform<__mbind_front_q<__values_opt_tuple_t, _Env>,
- __q<__tup::__tuple_for>>,
- __ignore>,
- _Senders...>;
-
- using __nullable_variant_t_ =
- __munique<__mbind_front_q<std::variant, __not_an_error>>;
-
- using __error_types = //
- __minvoke<__mconcat<__transform<__q<__decay_t>, __nullable_variant_t_>>,
- error_types_of_t<_Senders, __env_t<_Env>, __types>...>;
-
- using __errors_variant = //
- __if<__all_nothrow_decay_copyable<_Env, _Senders...>, __error_types,
- __minvoke<__push_back_unique<__q<std::variant>>, __error_types,
- std::exception_ptr>>;
-};
-
-struct _INVALID_ARGUMENTS_TO_WHEN_ALL_
-{};
-
-template <class _ErrorsVariant, class _ValuesTuple, class _StopToken>
-struct __when_all_state
-{
- using __stop_callback_t =
- typename _StopToken::template callback_type<__on_stop_request>;
-
- template <class _Receiver>
- void __arrive(_Receiver& __rcvr) noexcept
- {
- if (0 == --__count_)
- {
- __complete(__rcvr);
- }
- }
-
- template <class _Receiver>
- void __complete(_Receiver& __rcvr) noexcept
- {
- // Stop callback is no longer needed. Destroy it.
- __on_stop_.reset();
- // All child operations have completed and arrived at the barrier.
- switch (__state_.load(std::memory_order_relaxed))
- {
- case __started:
- if constexpr (!same_as<_ValuesTuple, __ignore>)
- {
- // All child operations completed successfully:
- __when_all::__set_values(__rcvr, __values_);
- }
- break;
- case __error:
- if constexpr (!same_as<_ErrorsVariant,
- std::variant<std::monostate>>)
- {
- // One or more child operations completed with an error:
- std::visit(__complete_fn(set_error, __rcvr), __errors_);
- }
- break;
- case __stopped:
- stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr));
- break;
- default:;
- }
- }
-
- std::atomic<std::size_t> __count_;
- in_place_stop_source __stop_source_{};
- // Could be non-atomic here and atomic_ref everywhere except __completion_fn
- std::atomic<__state_t> __state_{__started};
- _ErrorsVariant __errors_{};
- STDEXEC_ATTRIBUTE((no_unique_address))
- _ValuesTuple __values_{};
- std::optional<__stop_callback_t> __on_stop_{};
-};
-
-template <class _Env>
-static auto __mk_state_fn(const _Env& __env) noexcept
-{
- return [&]<__max1_sender<__env_t<_Env>>... _Child>(__ignore, __ignore,
- _Child&&...) {
- using _Traits = __traits<_Env, _Child...>;
- using _ErrorsVariant = typename _Traits::__errors_variant;
- using _ValuesTuple = typename _Traits::__values_tuple;
- using _State = __when_all_state<_ErrorsVariant, _ValuesTuple,
- stop_token_of_t<_Env>>;
- return _State{sizeof...(_Child), in_place_stop_source{}, __started,
- _ErrorsVariant{}, _ValuesTuple{}, std::nullopt};
- };
-}
-
-template <class _Env>
-using __mk_state_fn_t = decltype(__when_all::__mk_state_fn(__declval<_Env>()));
-
-struct when_all_t
-{
- // Used by the default_domain to find legacy customizations:
- using _Sender = __1;
- using __legacy_customizations_t = //
- __types<tag_invoke_t(when_all_t, _Sender...)>;
-
- template <sender... _Senders>
- requires __domain::__has_common_domain<_Senders...>
- auto operator()(_Senders&&... __sndrs) const -> __well_formed_sender auto
- {
- auto __domain = __domain::__common_domain_t<_Senders...>();
- return stdexec::transform_sender(
- __domain, __make_sexpr<when_all_t>(
- __(), static_cast<_Senders&&>(__sndrs)...));
- }
-};
-
-struct __when_all_impl : __sexpr_defaults
-{
- template <class _Self, class _Env>
- using __error_t = __mexception<_INVALID_ARGUMENTS_TO_WHEN_ALL_,
- __children_of<_Self, __q<_WITH_SENDERS_>>,
- _WITH_ENVIRONMENT_<_Env>>;
-
- template <class _Self, class _Env>
- using __completions = //
- __children_of<_Self, __mbind_front_q<__completions_t, __env_t<_Env>>>;
-
- static constexpr auto get_attrs = //
- []<class... _Child>(__ignore, const _Child&...) noexcept {
- using _Domain = __domain::__common_domain_t<_Child...>;
- if constexpr (same_as<_Domain, default_domain>)
- {
- return empty_env();
- }
- else
- {
- return __env::__with(_Domain(), get_domain);
- }
- };
-
- static constexpr auto get_completion_signatures = //
- []<class _Self, class _Env>(_Self&&, _Env&&) noexcept {
- static_assert(sender_expr_for<_Self, when_all_t>);
- return __minvoke<__mtry_catch<__q<__completions>, __q<__error_t>>,
- _Self, _Env>();
- };
-
- static constexpr auto get_env = //
- []<class _State, class _Receiver>(__ignore, _State& __state,
- const _Receiver& __rcvr) noexcept //
- -> __env_t<env_of_t<const _Receiver&>> {
- return __mkenv(stdexec::get_env(__rcvr), __state.__stop_source_);
- };
-
- static constexpr auto get_state = //
- []<class _Self, class _Receiver>(_Self&& __self, _Receiver& __rcvr)
- -> __sexpr_apply_result_t<_Self, __mk_state_fn_t<env_of_t<_Receiver>>> {
- return __sexpr_apply(
- static_cast<_Self&&>(__self),
- __when_all::__mk_state_fn(stdexec::get_env(__rcvr)));
- };
-
- static constexpr auto start = //
- []<class _State, class _Receiver, class... _Operations>(
- _State& __state, _Receiver& __rcvr,
- _Operations&... __child_ops) noexcept -> void {
- // register stop callback:
- __state.__on_stop_.emplace(get_stop_token(stdexec::get_env(__rcvr)),
- __on_stop_request{__state.__stop_source_});
- if (__state.__stop_source_.stop_requested())
- {
- // Stop has already been requested. Don't bother starting
- // the child operations.
- stdexec::set_stopped(std::move(__rcvr));
- }
- else
- {
- (stdexec::start(__child_ops), ...);
- if constexpr (sizeof...(__child_ops) == 0)
- {
- __state.__complete(__rcvr);
- }
- }
- };
-
- template <class _State, class _Receiver, class _Error>
- static void __set_error(_State& __state, _Receiver& __rcvr,
- _Error&& __err) noexcept
- {
- // TODO: What memory orderings are actually needed here?
- if (__error != __state.__state_.exchange(__error))
- {
- __state.__stop_source_.request_stop();
- // We won the race, free to write the error into the operation
- // state without worry.
- if constexpr (__nothrow_decay_copyable<_Error>)
- {
- __state.__errors_.template emplace<__decay_t<_Error>>(
- static_cast<_Error&&>(__err));
- }
- else
- {
- try
- {
- __state.__errors_.template emplace<__decay_t<_Error>>(
- static_cast<_Error&&>(__err));
- }
- catch (...)
- {
- __state.__errors_.template emplace<std::exception_ptr>(
- std::current_exception());
- }
- }
- }
- }
-
- static constexpr auto complete = //
- []<class _Index, class _State, class _Receiver, class _Set,
- class... _Args>(_Index, _State& __state, _Receiver& __rcvr, _Set,
- _Args&&... __args) noexcept -> void {
- if constexpr (same_as<_Set, set_error_t>)
- {
- __set_error(__state, __rcvr, static_cast<_Args&&>(__args)...);
- }
- else if constexpr (same_as<_Set, set_stopped_t>)
- {
- __state_t __expected = __started;
- // Transition to the "stopped" state if and only if we're in the
- // "started" state. (If this fails, it's because we're in an
- // error state, which trumps cancellation.)
- if (__state.__state_.compare_exchange_strong(__expected, __stopped))
- {
- __state.__stop_source_.request_stop();
- }
- }
- else if constexpr (!same_as<decltype(_State::__values_), __ignore>)
- {
- // We only need to bother recording the completion values
- // if we're not already in the "error" or "stopped" state.
- if (__state.__state_ == __started)
- {
- auto& __opt_values =
- __tup::__get<__v<_Index>>(__state.__values_);
- using _Tuple = __decayed_custom_tuple<_Args...>;
- static_assert(
- same_as<decltype(*__opt_values), _Tuple&>,
- "One of the senders in this when_all() is fibbing about what types it sends");
- if constexpr ((__nothrow_decay_copyable<_Args> && ...))
- {
- __opt_values.emplace(
- _Tuple{{static_cast<_Args&&>(__args)}...});
- }
- else
- {
- try
- {
- __opt_values.emplace(
- _Tuple{{static_cast<_Args&&>(__args)}...});
- }
- catch (...)
- {
- __set_error(__state, __rcvr, std::current_exception());
- }
- }
- }
- }
-
- __state.__arrive(__rcvr);
- };
-};
-
-struct when_all_with_variant_t
-{
- using _Sender = __1;
- using __legacy_customizations_t = //
- __types<tag_invoke_t(when_all_with_variant_t, _Sender...)>;
-
- template <sender... _Senders>
- requires __domain::__has_common_domain<_Senders...>
- auto operator()(_Senders&&... __sndrs) const -> __well_formed_sender auto
- {
- auto __domain = __domain::__common_domain_t<_Senders...>();
- return stdexec::transform_sender(
- __domain, __make_sexpr<when_all_with_variant_t>(
- __(), static_cast<_Senders&&>(__sndrs)...));
- }
-
- template <class _Sender, class _Env>
- static auto transform_sender(_Sender&& __sndr, const _Env& __env)
- {
- // transform the when_all_with_variant into a regular when_all (looking
- // for early when_all customizations), then transform it again to look
- // for late customizations.
- return __sexpr_apply(
- static_cast<_Sender&&>(__sndr),
- [&]<class... _Child>(__ignore, __ignore, _Child&&... __child) {
- return when_all_t()(
- into_variant(static_cast<_Child&&>(__child))...);
- });
- }
-};
-
-struct __when_all_with_variant_impl : __sexpr_defaults
-{
- static constexpr auto get_attrs = //
- []<class... _Child>(__ignore, const _Child&...) noexcept {
- using _Domain = __domain::__common_domain_t<_Child...>;
- if constexpr (same_as<_Domain, default_domain>)
- {
- return empty_env();
- }
- else
- {
- return __env::__with(_Domain(), get_domain);
- }
- };
-};
-
-struct transfer_when_all_t
-{
- using _Env = __0;
- using _Sender = __1;
- using __legacy_customizations_t = //
- __types<tag_invoke_t(
- transfer_when_all_t,
- get_completion_scheduler_t<set_value_t>(const _Env&), _Sender...)>;
-
- template <scheduler _Scheduler, sender... _Senders>
- requires __domain::__has_common_domain<_Senders...>
- auto operator()(_Scheduler&& __sched, _Senders&&... __sndrs) const
- -> __well_formed_sender auto
- {
- using _Env =
- __t<__schedule_from::__environ<__id<__decay_t<_Scheduler>>>>;
- auto __domain = query_or(get_domain, __sched, default_domain());
- return stdexec::transform_sender(
- __domain, __make_sexpr<transfer_when_all_t>(
- _Env{static_cast<_Scheduler&&>(__sched)},
- static_cast<_Senders&&>(__sndrs)...));
- }
-
- template <class _Sender, class _Env>
- static auto transform_sender(_Sender&& __sndr, const _Env& __env)
- {
- // transform the transfer_when_all into a regular transform | when_all
- // (looking for early customizations), then transform it again to look
- // for late customizations.
- return __sexpr_apply(
- static_cast<_Sender&&>(__sndr),
- [&]<class _Data, class... _Child>(__ignore, _Data&& __data,
- _Child&&... __child) {
- return transfer(when_all_t()(static_cast<_Child&&>(__child)...),
- get_completion_scheduler<set_value_t>(__data));
- });
- }
-};
-
-struct __transfer_when_all_impl : __sexpr_defaults
-{
- static constexpr auto get_attrs = //
- []<class _Data>(const _Data& __data,
- const auto&...) noexcept -> const _Data& {
- return __data;
- };
-};
-
-struct transfer_when_all_with_variant_t
-{
- using _Env = __0;
- using _Sender = __1;
- using __legacy_customizations_t = //
- __types<tag_invoke_t(
- transfer_when_all_with_variant_t,
- get_completion_scheduler_t<set_value_t>(const _Env&), _Sender...)>;
-
- template <scheduler _Scheduler, sender... _Senders>
- requires __domain::__has_common_domain<_Senders...>
- auto operator()(_Scheduler&& __sched, _Senders&&... __sndrs) const
- -> __well_formed_sender auto
- {
- using _Env =
- __t<__schedule_from::__environ<__id<__decay_t<_Scheduler>>>>;
- auto __domain = query_or(get_domain, __sched, default_domain());
- return stdexec::transform_sender(
- __domain, __make_sexpr<transfer_when_all_with_variant_t>(
- _Env{{static_cast<_Scheduler&&>(__sched)}},
- static_cast<_Senders&&>(__sndrs)...));
- }
-
- template <class _Sender, class _Env>
- static auto transform_sender(_Sender&& __sndr, const _Env& __env)
- {
- // transform the transfer_when_all_with_variant into regular
- // transform_when_all and into_variant calls/ (looking for early
- // customizations), then transform it again to look for late
- // customizations.
- return __sexpr_apply(
- static_cast<_Sender&&>(__sndr),
- [&]<class _Data, class... _Child>(__ignore, _Data&& __data,
- _Child&&... __child) {
- return transfer_when_all_t()(
- get_completion_scheduler<set_value_t>(
- static_cast<_Data&&>(__data)),
- into_variant(static_cast<_Child&&>(__child))...);
- });
- }
-};
-
-struct __transfer_when_all_with_variant_impl : __sexpr_defaults
-{
- static constexpr auto get_attrs = //
- []<class _Data>(const _Data& __data,
- const auto&...) noexcept -> const _Data& {
- return __data;
- };
-};
-} // namespace __when_all
-
-using __when_all::when_all_t;
-inline constexpr when_all_t when_all{};
-
-using __when_all::when_all_with_variant_t;
-inline constexpr when_all_with_variant_t when_all_with_variant{};
-
-using __when_all::transfer_when_all_t;
-inline constexpr transfer_when_all_t transfer_when_all{};
-
-using __when_all::transfer_when_all_with_variant_t;
-inline constexpr transfer_when_all_with_variant_t
- transfer_when_all_with_variant{};
-
-template <>
-struct __sexpr_impl<when_all_t> : __when_all::__when_all_impl
-{};
-
-template <>
-struct __sexpr_impl<when_all_with_variant_t> :
- __when_all::__when_all_with_variant_impl
-{};
-
-template <>
-struct __sexpr_impl<transfer_when_all_t> : __when_all::__transfer_when_all_impl
-{};
-
-template <>
-struct __sexpr_impl<transfer_when_all_with_variant_t> :
- __when_all::__transfer_when_all_with_variant_impl
-{};
-
-namespace __read
-{
-template <class _Tag, class _ReceiverId>
-using __result_t = __call_result_t<_Tag, env_of_t<stdexec::__t<_ReceiverId>>>;
-
-template <class _Tag, class _ReceiverId>
-concept __nothrow_t =
- __nothrow_callable<_Tag, env_of_t<stdexec::__t<_ReceiverId>>>;
-
-inline constexpr __mstring __query_failed_diag =
- "The current execution environment doesn't have a value for the given query."_mstr;
-
-template <class _Tag>
-struct _WITH_QUERY_;
-
-template <class _Tag, class _Env>
-using __query_failed_error = //
- __mexception< //
- _NOT_CALLABLE_<"In stdexec::read()..."_mstr, __query_failed_diag>,
- _WITH_QUERY_<_Tag>, _WITH_ENVIRONMENT_<_Env>>;
-
-template <class _Tag, class _Env>
- requires __callable<_Tag, _Env>
-using __completions_t = //
- __if_c<__nothrow_callable<_Tag, _Env>,
- completion_signatures<set_value_t(__call_result_t<_Tag, _Env>)>,
- completion_signatures<set_value_t(__call_result_t<_Tag, _Env>),
- set_error_t(std::exception_ptr)>>;
-
-template <class _Tag, class _Ty>
-struct __state
-{
- using __query = _Tag;
- using __result = _Ty;
- std::optional<_Ty> __result_;
-};
-
-template <class _Tag, class _Ty>
- requires same_as<_Ty, _Ty&&>
-struct __state<_Tag, _Ty>
-{
- using __query = _Tag;
- using __result = _Ty;
-};
-
-struct __read_t
-{
- template <class _Tag>
- constexpr auto operator()(_Tag) const noexcept
- {
- return __make_sexpr<__read_t>(_Tag());
- }
-};
-
-struct __read_impl : __sexpr_defaults
-{
- using is_dependent = void;
-
- template <class _Tag, class _Env>
- using __completions_t = __minvoke<
- __mtry_catch_q<__read::__completions_t, __q<__query_failed_error>>,
- _Tag, _Env>;
-
- static constexpr auto get_completion_signatures = //
- []<class _Self, class _Env>(const _Self&, _Env&&) noexcept //
- -> __completions_t<__data_of<_Self>, _Env> {
- static_assert(sender_expr_for<_Self, __read_t>);
- return {};
- };
-
- static constexpr auto get_state = //
- []<class _Self, class _Receiver>(const _Self&,
- _Receiver& __rcvr) noexcept {
- using __query = __data_of<_Self>;
- using __result = __call_result_t<__query, env_of_t<_Receiver>>;
- return __state<__query, __result>();
- };
-
- static constexpr auto start = //
- []<class _State, class _Receiver>(_State& __state,
- _Receiver& __rcvr) noexcept -> void {
- using __query = typename _State::__query;
- using __result = typename _State::__result;
- if constexpr (same_as<__result, __result&&>)
- {
- // The query returns a reference type; pass it straight through to
- // the receiver.
- stdexec::__set_value_invoke(std::move(__rcvr), __query(),
- stdexec::get_env(__rcvr));
- }
- else
- {
- constexpr bool _Nothrow =
- __nothrow_callable<__query, env_of_t<_Receiver>>;
- auto __query_fn = [&]() noexcept(_Nothrow) -> __result&& {
- __state.__result_.emplace(__conv{[&]() noexcept(_Nothrow) {
- return __query()(stdexec::get_env(__rcvr));
- }});
- return std::move(*__state.__result_);
- };
- stdexec::__set_value_invoke(std::move(__rcvr), __query_fn);
- }
- };
-};
-} // namespace __read
-
-inline constexpr __read::__read_t read{};
-
-template <>
-struct __sexpr_impl<__read::__read_t> : __read::__read_impl
-{};
-
-namespace __queries
-{
-template <class _Tag>
-inline auto get_scheduler_t::operator()() const noexcept
-{
- return read(get_scheduler);
-}
-
-template <class _Env>
- requires tag_invocable<get_scheduler_t, const _Env&>
-inline auto get_scheduler_t::operator()(const _Env& __env) const noexcept
- -> tag_invoke_result_t<get_scheduler_t, const _Env&>
-{
- static_assert(nothrow_tag_invocable<get_scheduler_t, const _Env&>);
- static_assert(scheduler<tag_invoke_result_t<get_scheduler_t, const _Env&>>);
- return tag_invoke(get_scheduler_t{}, __env);
-}
-
-template <class _Tag>
-inline auto get_delegatee_scheduler_t::operator()() const noexcept
-{
- return read(get_delegatee_scheduler);
-}
-
-template <class _Env>
- requires tag_invocable<get_delegatee_scheduler_t, const _Env&>
-inline auto
- get_delegatee_scheduler_t::operator()(const _Env& __t) const noexcept
- -> tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>
-{
- static_assert(
- nothrow_tag_invocable<get_delegatee_scheduler_t, const _Env&>);
- static_assert(
- scheduler<tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>>);
- return tag_invoke(get_delegatee_scheduler_t{}, std::as_const(__t));
-}
-
-template <class _Tag>
-inline auto get_allocator_t::operator()() const noexcept
-{
- return read(get_allocator);
-}
-
-template <class _Tag>
-inline auto get_stop_token_t::operator()() const noexcept
-{
- return read(get_stop_token);
-}
-
-template <__completion_tag _CPO>
-template <__has_completion_scheduler_for<_CPO> _Queryable>
-auto get_completion_scheduler_t<_CPO>::operator()(
- const _Queryable& __queryable) const noexcept
- -> tag_invoke_result_t<get_completion_scheduler_t<_CPO>, const _Queryable&>
-{
- static_assert(nothrow_tag_invocable<get_completion_scheduler_t<_CPO>,
- const _Queryable&>,
- "get_completion_scheduler<_CPO> should be noexcept");
- static_assert(
- scheduler<tag_invoke_result_t<get_completion_scheduler_t<_CPO>,
- const _Queryable&>>);
- return tag_invoke(*this, __queryable);
-}
-} // namespace __queries
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.adaptors.on]
-namespace __on_v2
-{
-inline constexpr __mstring __on_context =
- "In stdexec::on(Scheduler, Sender)..."_mstr;
-inline constexpr __mstring __no_scheduler_diag =
- "stdexec::on() requires a scheduler to transition back to."_mstr;
-inline constexpr __mstring __no_scheduler_details =
- "The provided environment lacks a value for the get_scheduler() query."_mstr;
-
-template <__mstring _Context = __on_context,
- __mstring _Diagnostic = __no_scheduler_diag,
- __mstring _Details = __no_scheduler_details>
-struct _CANNOT_RESTORE_EXECUTION_CONTEXT_AFTER_ON_
-{};
-
-struct on_t;
-
-STDEXEC_PRAGMA_PUSH()
-STDEXEC_PRAGMA_IGNORE_GNU("-Wunused-local-typedefs")
-
-struct __no_scheduler_in_environment
-{
- // Issue a custom diagnostic if the environment doesn't provide a scheduler.
- template <class _Sender, class _Env>
- static auto transform_sender(_Sender&&, const _Env&)
- {
- struct __no_scheduler_in_environment
- {
- using sender_concept = sender_t;
- using completion_signatures = //
- __mexception<_CANNOT_RESTORE_EXECUTION_CONTEXT_AFTER_ON_<>,
- _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
- };
-
- return __no_scheduler_in_environment{};
- }
-};
-
-STDEXEC_PRAGMA_POP()
-
-struct on_t : __no_scheduler_in_environment
-{
- template <scheduler _Scheduler, sender _Sender>
- auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
- -> __well_formed_sender auto
- {
- // BUGBUG __get_early_domain, or get_domain(__sched), or ...?
- auto __domain = __get_early_domain(__sndr);
- return stdexec::transform_sender(
- __domain, __make_sexpr<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)));
- }
-
- using __no_scheduler_in_environment::transform_sender;
-
- template <class _Sender, class _Env>
- requires __callable<get_scheduler_t, const _Env&>
- static auto transform_sender(_Sender&& __sndr, const _Env& __env)
- {
- return __sexpr_apply(
- static_cast<_Sender&&>(__sndr),
- [&]<class _Scheduler, class _Child>(__ignore, _Scheduler __sched,
- _Child&& __child) {
- auto __old = get_scheduler(__env);
- return transfer(
- let_value(transfer_just(std::move(__sched)),
- __detail::__always{static_cast<_Child&&>(__child)}),
- std::move(__old));
- });
- }
-};
-
-template <class _Scheduler, class _Closure>
-struct __continue_on_data
-{
- _Scheduler __sched_;
- _Closure __clsur_;
-};
-template <class _Scheduler, class _Closure>
-__continue_on_data(_Scheduler, _Closure)
- -> __continue_on_data<_Scheduler, _Closure>;
-
-template <class _Scheduler>
-struct __with_sched
-{
- _Scheduler __sched_;
-
- friend auto tag_invoke(get_scheduler_t, const __with_sched& __self) noexcept
- -> _Scheduler
- {
- return __self.__sched_;
- }
-
- friend auto tag_invoke(get_domain_t, const __with_sched& __self) noexcept
- {
- return query_or(get_domain, __self.__sched_, default_domain());
- }
-};
-
-template <class _Scheduler>
-__with_sched(_Scheduler) -> __with_sched<_Scheduler>;
-
-struct continue_on_t : __no_scheduler_in_environment
-{
- template <sender _Sender, scheduler _Scheduler,
- __sender_adaptor_closure_for<_Sender> _Closure>
- auto operator()(_Sender&& __sndr, _Scheduler&& __sched,
- _Closure&& __clsur) const -> __well_formed_sender auto
- {
- auto __domain = __get_early_domain(__sndr);
- return stdexec::transform_sender(
- __domain, __make_sexpr<continue_on_t>(
- __continue_on_data{static_cast<_Scheduler&&>(__sched),
- static_cast<_Closure&&>(__clsur)},
- static_cast<_Sender&&>(__sndr)));
- }
-
- template <scheduler _Scheduler, __sender_adaptor_closure _Closure>
- STDEXEC_ATTRIBUTE((always_inline))
- auto operator()(_Scheduler&& __sched, _Closure&& __clsur) const
- -> __binder_back<continue_on_t, __decay_t<_Scheduler>,
- __decay_t<_Closure>>
- {
- return {{},
- {},
- {static_cast<_Scheduler&&>(__sched),
- static_cast<_Closure&&>(__clsur)}};
- }
-
- using __no_scheduler_in_environment::transform_sender;
-
- template <class _Sender, class _Env>
- requires __callable<get_scheduler_t, const _Env&>
- static auto transform_sender(_Sender&& __sndr, const _Env& __env)
- {
- auto __old = get_scheduler(__env);
- return __sexpr_apply(static_cast<_Sender&&>(__sndr),
- [&]<class _Data, class _Child>(
- __ignore, _Data&& __data, _Child&& __child) {
- auto&& [__sched, __clsur] = static_cast<_Data&&>(__data);
- using _Closure = decltype(__clsur);
- return __write(transfer(static_cast<_Closure&&>(__clsur)(transfer(
- __write(static_cast<_Child&&>(__child),
- __with_sched{__old}),
- __sched)),
- __old),
- __with_sched{__sched});
- });
- }
-};
-} // namespace __on_v2
-
-namespace v2
-{
-using __on_v2::on_t;
-inline constexpr on_t on{};
-
-using __on_v2::continue_on_t;
-inline constexpr continue_on_t continue_on{};
-} // namespace v2
-
-template <>
-struct __sexpr_impl<v2::on_t> : __sexpr_defaults
-{
- using is_dependent = void;
-};
-
-template <>
-struct __sexpr_impl<v2::continue_on_t> : __sexpr_defaults
-{
- using is_dependent = void;
-};
-
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.consumers.sync_wait]
-// [execution.senders.consumers.sync_wait_with_variant]
-namespace __sync_wait
-{
-inline auto __make_env(run_loop& __loop) noexcept
-{
- return __env::__with(__loop.get_scheduler(), get_scheduler,
- get_delegatee_scheduler);
-}
-
-struct __env : __result_of<__make_env, run_loop&>
-{
- __env();
-
- explicit __env(run_loop& __loop) noexcept :
- __result_of<__make_env, run_loop&>{__sync_wait::__make_env(__loop)}
- {}
-};
-
-// What should sync_wait(just_stopped()) return?
-template <class _Sender, class _Continuation>
-using __sync_wait_result_impl = //
- __try_value_types_of_t<_Sender, __env,
- __transform<__q<__decay_t>, _Continuation>,
- __q<__msingle>>;
-
-template <class _Sender>
-using __sync_wait_result_t =
- __mtry_eval<__sync_wait_result_impl, _Sender, __q<std::tuple>>;
-
-template <class _Sender>
-using __sync_wait_with_variant_result_t =
- __mtry_eval<__sync_wait_result_impl, __result_of<into_variant, _Sender>,
- __q<__midentity>>;
-
-template <class... _Values>
-struct __state
-{
- using _Tuple = std::tuple<_Values...>;
- std::variant<std::monostate, _Tuple, std::exception_ptr, set_stopped_t>
- __data_{};
-};
-
-template <class... _Values>
-struct __receiver
-{
- struct __t
- {
- using receiver_concept = receiver_t;
- using __id = __receiver;
- __state<_Values...>* __state_;
- run_loop* __loop_;
-
- template <class _Error>
- void __set_error(_Error __err) noexcept
- {
- if constexpr (__decays_to<_Error, std::exception_ptr>)
- __state_->__data_.template emplace<2>(
- static_cast<_Error&&>(__err));
- else if constexpr (__decays_to<_Error, std::error_code>)
- __state_->__data_.template emplace<2>(
- std::make_exception_ptr(std::system_error(__err)));
- else
- __state_->__data_.template emplace<2>(
- std::make_exception_ptr(static_cast<_Error&&>(__err)));
- __loop_->finish();
- }
-
- template <same_as<set_value_t> _Tag, class... _As>
- requires constructible_from<std::tuple<_Values...>, _As...>
- friend void tag_invoke(_Tag, __t&& __rcvr, _As&&... __as) noexcept
- {
- try
- {
- __rcvr.__state_->__data_.template emplace<1>(
- static_cast<_As&&>(__as)...);
- __rcvr.__loop_->finish();
- }
- catch (...)
- {
- __rcvr.__set_error(std::current_exception());
- }
- }
-
- template <same_as<set_error_t> _Tag, class _Error>
- friend void tag_invoke(_Tag, __t&& __rcvr, _Error __err) noexcept
- {
- __rcvr.__set_error(static_cast<_Error&&>(__err));
- }
-
- friend void tag_invoke(set_stopped_t __d, __t&& __rcvr) noexcept
- {
- __rcvr.__state_->__data_.template emplace<3>(__d);
- __rcvr.__loop_->finish();
- }
-
- friend auto tag_invoke(get_env_t, const __t& __rcvr) noexcept -> __env
- {
- return __env(*__rcvr.__loop_);
- }
- };
-};
-
-template <class _Sender>
-using __receiver_t = __t<__sync_wait_result_impl<_Sender, __q<__receiver>>>;
-
-// These are for hiding the metaprogramming in diagnostics
-template <class _Sender>
-struct __sync_receiver_for
-{
- using __t = __receiver_t<_Sender>;
-};
-template <class _Sender>
-using __sync_receiver_for_t = __t<__sync_receiver_for<_Sender>>;
-
-template <class _Sender>
-struct __value_tuple_for
-{
- using __t = __sync_wait_result_t<_Sender>;
-};
-template <class _Sender>
-using __value_tuple_for_t = __t<__value_tuple_for<_Sender>>;
-
-template <class _Sender>
-struct __variant_for
-{
- using __t = __sync_wait_with_variant_result_t<_Sender>;
-};
-template <class _Sender>
-using __variant_for_t = __t<__variant_for<_Sender>>;
-
-inline constexpr __mstring __sync_wait_context_diag = //
- "In stdexec::sync_wait()..."_mstr;
-inline constexpr __mstring __too_many_successful_completions_diag =
- "The argument to stdexec::sync_wait() is a sender that can complete successfully in more "
- "than one way. Use stdexec::sync_wait_with_variant() instead."_mstr;
-
-template <__mstring _Context, __mstring _Diagnostic>
-struct _INVALID_ARGUMENT_TO_SYNC_WAIT_;
-
-template <__mstring _Diagnostic>
-using __invalid_argument_to_sync_wait =
- _INVALID_ARGUMENT_TO_SYNC_WAIT_<__sync_wait_context_diag, _Diagnostic>;
-
-template <__mstring _Diagnostic, class _Sender, class _Env = __env>
-using __sync_wait_error =
- __mexception<__invalid_argument_to_sync_wait<_Diagnostic>,
- _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
-
-template <class _Sender, class>
-using __too_many_successful_completions_error =
- __sync_wait_error<__too_many_successful_completions_diag, _Sender>;
-
-template <class _Sender>
-concept __valid_sync_wait_argument =
- __ok<__minvoke<__mtry_catch_q<__single_value_variant_sender_t,
- __q<__too_many_successful_completions_error>>,
- _Sender, __env>>;
-
-#if STDEXEC_NVHPC()
-// It requires some hoop-jumping to get the NVHPC compiler to report a
-// meaningful diagnostic for SFINAE failures.
-template <class _Sender>
-auto __diagnose_error()
-{
- if constexpr (!sender_in<_Sender, __env>)
- {
- using _Completions = __completion_signatures_of_t<_Sender, __env>;
- if constexpr (__merror<_Completions>)
- {
- return _Completions();
- }
- else
- {
- constexpr __mstring __diag =
- "The stdexec::sender_in<Sender, Environment> concept check has failed."_mstr;
- return __sync_wait_error<__diag, _Sender>();
- }
- }
- else if constexpr (!__valid_sync_wait_argument<_Sender>)
- {
- return __sync_wait_error<__too_many_successful_completions_diag,
- _Sender>();
- }
- else if constexpr (!sender_to<_Sender, __sync_receiver_for_t<_Sender>>)
- {
- constexpr __mstring __diag =
- "Failed to connect the given sender to sync_wait's internal receiver. "
- "The stdexec::connect(Sender, Receiver) expression is ill-formed."_mstr;
- return __sync_wait_error<__diag, _Sender>();
- }
- else
- {
- constexpr __mstring __diag = "Unknown concept check failure."_mstr;
- return __sync_wait_error<__diag, _Sender>();
- }
-}
-
-template <class _Sender>
-using __error_description_t =
- decltype(__sync_wait::__diagnose_error<_Sender>());
-#endif
-
-////////////////////////////////////////////////////////////////////////////
-// [execution.senders.consumers.sync_wait]
-struct sync_wait_t
-{
- template <sender_in<__env> _Sender>
- requires __valid_sync_wait_argument<_Sender> &&
- __has_implementation_for<sync_wait_t,
- __early_domain_of_t<_Sender>, _Sender>
- auto operator()(_Sender&& __sndr) const
- -> std::optional<__value_tuple_for_t<_Sender>>
- {
- auto __domain = __get_early_domain(__sndr);
- return stdexec::apply_sender(__domain, *this,
- static_cast<_Sender&&>(__sndr));
- }
-
-#if STDEXEC_NVHPC()
- // 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
- -> std::optional<std::tuple<int>> = delete;
-#endif
-
- using _Sender = __0;
- using __legacy_customizations_t = __types<
- // For legacy reasons:
- tag_invoke_t(
- sync_wait_t,
- get_completion_scheduler_t<set_value_t>(get_env_t(const _Sender&)),
- _Sender),
- tag_invoke_t(sync_wait_t, _Sender)>;
-
- // The default implementation goes here:
- template <class _Sender>
- requires sender_to<_Sender, __sync_receiver_for_t<_Sender>>
- auto apply_sender(_Sender&& __sndr) const
- -> std::optional<__sync_wait_result_t<_Sender>>
- {
- using state_t = __sync_wait_result_impl<_Sender, __q<__state>>;
- state_t __state{};
- run_loop __loop;
-
- // Launch the sender with a continuation that will fill in a variant
- // and notify a condition variable.
- auto __op_state = connect(static_cast<_Sender&&>(__sndr),
- __receiver_t<_Sender>{&__state, &__loop});
- start(__op_state);
-
- // Wait for the variant to be filled in.
- __loop.run();
-
- if (__state.__data_.index() == 2)
- std::rethrow_exception(std::get<2>(__state.__data_));
-
- if (__state.__data_.index() == 3)
- return std::nullopt;
-
- return std::move(std::get<1>(__state.__data_));
- }
-};
-
-////////////////////////////////////////////////////////////////////////////
-// [execution.senders.consumers.sync_wait_with_variant]
-struct sync_wait_with_variant_t
-{
- struct __impl;
-
- template <sender_in<__env> _Sender>
- requires __callable<apply_sender_t, __early_domain_of_t<_Sender>,
- sync_wait_with_variant_t, _Sender>
- auto operator()(_Sender&& __sndr) const
- -> std::optional<__variant_for_t<_Sender>>
- {
- auto __domain = __get_early_domain(__sndr);
- return stdexec::apply_sender(__domain, *this,
- static_cast<_Sender&&>(__sndr));
- }
-
-#if STDEXEC_NVHPC()
- template <class _Sender, class _Error = __error_description_t<
- __result_of<into_variant, _Sender>>>
- auto operator()(_Sender&&, [[maybe_unused]] _Error __diagnostic = {}) const
- -> std::optional<std::tuple<std::variant<std::tuple<>>>> = delete;
-#endif
-
- using _Sender = __0;
- using __legacy_customizations_t = __types<
- // For legacy reasons:
- tag_invoke_t(
- sync_wait_with_variant_t,
- get_completion_scheduler_t<set_value_t>(get_env_t(const _Sender&)),
- _Sender),
- tag_invoke_t(sync_wait_with_variant_t, _Sender)>;
-
- template <class _Sender>
- requires __callable<sync_wait_t, __result_of<into_variant, _Sender>>
- auto apply_sender(_Sender&& __sndr) const
- -> std::optional<__variant_for_t<_Sender>>
- {
- if (auto __opt_values =
- sync_wait_t()(into_variant(static_cast<_Sender&&>(__sndr))))
- {
- return std::move(std::get<0>(*__opt_values));
- }
- return std::nullopt;
- }
-};
-} // namespace __sync_wait
-
-using __sync_wait::sync_wait_t;
-inline constexpr sync_wait_t sync_wait{};
-
-using __sync_wait::sync_wait_with_variant_t;
-inline constexpr sync_wait_with_variant_t sync_wait_with_variant{};
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-struct __ignore_sender
-{
- using sender_concept = sender_t;
-
- template <sender _Sender>
- constexpr __ignore_sender(_Sender&&) noexcept
- {}
-};
-
-template <auto _Reason = "You cannot pipe one sender into another."_mstr>
-struct _CANNOT_PIPE_INTO_A_SENDER_
-{};
-
-template <class _Sender>
-using __bad_pipe_sink_t =
- __mexception<_CANNOT_PIPE_INTO_A_SENDER_<>, _WITH_SENDER_<_Sender>>;
-} // namespace stdexec
-
-#if STDEXEC_MSVC()
-namespace stdexec
-{
-// MSVCBUG
-// https://developercommunity.visualstudio.com/t/Incorrect-codegen-in-await_suspend-aroun/10454102
-
-// MSVC incorrectly allocates the return buffer for await_suspend calls within
-// the suspended coroutine frame. When the suspended coroutine is destroyed
-// within await_suspend, the continuation coroutine handle is not only used
-// after free, but also overwritten by the debug malloc implementation when NRVO
-// is in play.
-
-// This workaround delays the destruction of the suspended coroutine by wrapping
-// the continuation in another coroutine which destroys the former and transfers
-// execution to the original continuation.
-
-// The wrapping coroutine is thread-local and is reused within the thread for
-// each destroy-and-continue sequence. The wrapping coroutine itself is
-// destroyed at thread exit.
-
-namespace __destroy_and_continue_msvc
-{
-struct __task
-{
- struct promise_type
- {
- __task get_return_object() noexcept
- {
- return {
- __coro::coroutine_handle<promise_type>::from_promise(*this)};
- }
-
- static std::suspend_never initial_suspend() noexcept
- {
- return {};
- }
-
- static std::suspend_never final_suspend() noexcept
- {
- STDEXEC_ASSERT(!"Should never get here");
- return {};
- }
-
- static void return_void() noexcept
- {
- STDEXEC_ASSERT(!"Should never get here");
- }
-
- static void unhandled_exception() noexcept
- {
- STDEXEC_ASSERT(!"Should never get here");
- }
- };
-
- __coro::coroutine_handle<> __coro_;
-};
-
-struct __continue_t
-{
- static constexpr bool await_ready() noexcept
- {
- return false;
- }
-
- __coro::coroutine_handle<>
- await_suspend(__coro::coroutine_handle<>) noexcept
- {
- return __continue_;
- }
-
- static void await_resume() noexcept {}
-
- __coro::coroutine_handle<> __continue_;
-};
-
-struct __context
-{
- __coro::coroutine_handle<> __destroy_;
- __coro::coroutine_handle<> __continue_;
-};
-
-inline __task __co_impl(__context& __c)
-{
- while (true)
- {
- co_await __continue_t{__c.__continue_};
- __c.__destroy_.destroy();
- }
-}
-
-struct __context_and_coro
-{
- __context_and_coro()
- {
- __context_.__continue_ = __coro::noop_coroutine();
- __coro_ = __co_impl(__context_).__coro_;
- }
-
- ~__context_and_coro()
- {
- __coro_.destroy();
- }
-
- __context __context_;
- __coro::coroutine_handle<> __coro_;
-};
-
-inline __coro::coroutine_handle<> __impl(__coro::coroutine_handle<> __destroy,
- __coro::coroutine_handle<> __continue)
-{
- static thread_local __context_and_coro __c;
- __c.__context_.__destroy_ = __destroy;
- __c.__context_.__continue_ = __continue;
- return __c.__coro_;
-}
-} // namespace __destroy_and_continue_msvc
-} // namespace stdexec
-
-#define STDEXEC_DESTROY_AND_CONTINUE(__destroy, __continue) \
- (::stdexec::__destroy_and_continue_msvc::__impl(__destroy, __continue))
-#else
-#define STDEXEC_DESTROY_AND_CONTINUE(__destroy, __continue) \
- (__destroy.destroy(), __continue)
-#endif
-
// For issuing a meaningful diagnostic for the erroneous `snd1 | snd2`.
-template <stdexec::sender _Sender>
+template <stdexec::sender _Ignore, stdexec::sender _Sender>
requires stdexec::__ok<stdexec::__bad_pipe_sink_t<_Sender>>
-auto operator|(stdexec::__ignore_sender, _Sender&&) noexcept
- -> stdexec::__ignore_sender;
-
-#include "__detail/__p2300.hpp"
-
-STDEXEC_PRAGMA_POP()
+auto operator|(_Ignore&&, _Sender&&) noexcept;
diff --git a/include/sdbusplus/async/stdexec/functional.hpp b/include/sdbusplus/async/stdexec/functional.hpp
index 3b4c989..4dbc910 100644
--- a/include/sdbusplus/async/stdexec/functional.hpp
+++ b/include/sdbusplus/async/stdexec/functional.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
@@ -17,6 +17,7 @@
#include "__detail/__config.hpp"
#include "__detail/__meta.hpp"
+#include "__detail/__tag_invoke.hpp"
#include "concepts.hpp"
#include <cstddef>
@@ -24,59 +25,8 @@
#include <tuple>
#include <type_traits>
-namespace stdexec::__std_concepts
-{
-#if STDEXEC_HAS_STD_CONCEPTS_HEADER()
-using std::invocable;
-#else
-template <class _Fun, class... _As>
-concept invocable = //
- requires(_Fun&& __f, _As&&... __as) {
- std::invoke(static_cast<_Fun&&>(__f), static_cast<_As&&>(__as)...);
- };
-#endif
-} // namespace stdexec::__std_concepts
-
-namespace std
-{
-using namespace stdexec::__std_concepts;
-} // namespace std
-
namespace stdexec
{
-template <auto _Fun>
-struct __function_constant
-{
- using _FunT = decltype(_Fun);
-
- template <class... _Args>
- requires __callable<_FunT, _Args...>
- STDEXEC_ATTRIBUTE((always_inline)) auto operator()(_Args&&... __args) const
- noexcept(noexcept(_Fun(static_cast<_Args&&>(__args)...)))
- -> decltype(_Fun(static_cast<_Args&&>(__args)...))
- {
- return _Fun(static_cast<_Args&&>(__args)...);
- }
-};
-
-template <class _Ty, class _Cl, _Ty _Cl::*_MemPtr>
-struct __function_constant<_MemPtr>
-{
- using _FunT = _Ty _Cl::*;
-
- template <class _Arg>
- requires requires(_Arg&& __arg) { static_cast<_Arg&&>(__arg).*_MemPtr; }
- STDEXEC_ATTRIBUTE((always_inline)) constexpr auto
- operator()(_Arg&& __arg) const noexcept
- -> decltype(((static_cast<_Arg&&>(__arg)).*_MemPtr))
- {
- return static_cast<_Arg&&>(__arg).*_MemPtr;
- }
-};
-
-template <auto _Fun>
-inline constexpr __function_constant<_Fun> __function_constant_v{};
-
template <class _Fun0, class _Fun1>
struct __composed
{
@@ -313,11 +263,11 @@
template <std::size_t... _Is, class _Fn, class _Tup>
STDEXEC_ATTRIBUTE((always_inline))
-constexpr auto __impl(__indices<_Is...>, _Fn&& __fn, _Tup&& __tup) noexcept(
- noexcept(__invoke(static_cast<_Fn&&>(__fn),
- get<_Is>(static_cast<_Tup&&>(__tup))...)))
- -> decltype(__invoke(static_cast<_Fn&&>(__fn),
- get<_Is>(static_cast<_Tup&&>(__tup))...))
+constexpr auto __impl(__indices<_Is...>, _Fn&& __fn, _Tup&& __tup) //
+ noexcept(noexcept(__invoke(static_cast<_Fn&&>(__fn),
+ get<_Is>(static_cast<_Tup&&>(__tup))...)))
+ -> decltype(__invoke(static_cast<_Fn&&>(__fn),
+ get<_Is>(static_cast<_Tup&&>(__tup))...))
{
return __invoke(static_cast<_Fn&&>(__fn),
get<_Is>(static_cast<_Tup&&>(__tup))...);
@@ -337,8 +287,9 @@
template <class _Fn, class _Tup>
concept __nothrow_applicable = __applicable<_Fn, _Tup> //
- && noexcept(__apply_::__impl(__apply_::__tuple_indices<_Tup>(),
- __declval<_Fn>(), __declval<_Tup>()));
+ && //
+ noexcept(__apply_::__impl(__apply_::__tuple_indices<_Tup>(),
+ __declval<_Fn>(), __declval<_Tup>()));
template <class _Fn, class _Tup>
requires __applicable<_Fn, _Tup>
@@ -386,87 +337,4 @@
template <class _Tag>
inline constexpr __mkfield_<_Tag> __mkfield{};
-
-// [func.tag_invoke], tag_invoke
-namespace __tag_invoke
-{
-void tag_invoke();
-
-// NOT TO SPEC: Don't require tag_invocable to subsume invocable.
-// std::invoke is more expensive at compile time than necessary,
-// and results in diagnostics that are more verbose than necessary.
-template <class _Tag, class... _Args>
-concept tag_invocable = //
- requires(_Tag __tag, _Args&&... __args) {
- tag_invoke(static_cast<_Tag&&>(__tag), static_cast<_Args&&>(__args)...);
- };
-
-template <class _Ret, class _Tag, class... _Args>
-concept __tag_invocable_r = //
- requires(_Tag __tag, _Args&&... __args) {
- {
- static_cast<_Ret>(tag_invoke(static_cast<_Tag&&>(__tag),
- static_cast<_Args&&>(__args)...))
- };
- };
-
-// NOT TO SPEC: nothrow_tag_invocable subsumes tag_invocable
-template <class _Tag, class... _Args>
-concept nothrow_tag_invocable =
- tag_invocable<_Tag, _Args...> && //
- requires(_Tag __tag, _Args&&... __args) {
- {
- tag_invoke(static_cast<_Tag&&>(__tag),
- static_cast<_Args&&>(__args)...)
- } noexcept;
- };
-
-template <class _Tag, class... _Args>
-using tag_invoke_result_t =
- decltype(tag_invoke(__declval<_Tag>(), __declval<_Args>()...));
-
-template <class _Tag, class... _Args>
-struct tag_invoke_result
-{};
-
-template <class _Tag, class... _Args>
- requires tag_invocable<_Tag, _Args...>
-struct tag_invoke_result<_Tag, _Args...>
-{
- using type = tag_invoke_result_t<_Tag, _Args...>;
-};
-
-struct tag_invoke_t
-{
- template <class _Tag, class... _Args>
- requires tag_invocable<_Tag, _Args...>
- STDEXEC_ATTRIBUTE((always_inline)) constexpr auto
- operator()(_Tag __tag, _Args&&... __args) const
- noexcept(nothrow_tag_invocable<_Tag, _Args...>)
- -> tag_invoke_result_t<_Tag, _Args...>
- {
- return tag_invoke(static_cast<_Tag&&>(__tag),
- static_cast<_Args&&>(__args)...);
- }
-};
-
-} // namespace __tag_invoke
-
-using __tag_invoke::tag_invoke_t;
-
-namespace __ti
-{
-inline constexpr tag_invoke_t tag_invoke{};
-} // namespace __ti
-
-using namespace __ti;
-
-template <auto& _Tag>
-using tag_t = __decay_t<decltype(_Tag)>;
-
-using __tag_invoke::__tag_invocable_r;
-using __tag_invoke::nothrow_tag_invocable;
-using __tag_invoke::tag_invocable;
-using __tag_invoke::tag_invoke_result;
-using __tag_invoke::tag_invoke_result_t;
} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/inline_scheduler.hpp b/include/sdbusplus/async/stdexec/inline_scheduler.hpp
index f100590..4634e9e 100644
--- a/include/sdbusplus/async/stdexec/inline_scheduler.hpp
+++ b/include/sdbusplus/async/stdexec/inline_scheduler.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
diff --git a/include/sdbusplus/async/stdexec/sequence_senders.hpp b/include/sdbusplus/async/stdexec/sequence_senders.hpp
index b1bfaab..fda8b84 100644
--- a/include/sdbusplus/async/stdexec/sequence_senders.hpp
+++ b/include/sdbusplus/async/stdexec/sequence_senders.hpp
@@ -34,7 +34,7 @@
struct __mall_contained_in_impl
{
template <class... _Needles>
- using __f = __mand<__mapply<__contains<_Needles>, _Haystack>...>;
+ using __f = __mand<__mapply<__mcontains<_Needles>, _Haystack>...>;
};
template <class _Needles, class _Haystack>
@@ -42,7 +42,7 @@
__mapply<__mall_contained_in_impl<_Haystack>, _Needles>;
template <class _Needles, class _Haystack>
-concept __all_contained_in = __mall_contained_in<_Needles, _Haystack>::value;
+concept __all_contained_in = __v<__mall_contained_in<_Needles, _Haystack>>;
// This concept checks if a given sender satisfies the requirements to be
// returned from `set_next`.
@@ -57,7 +57,7 @@
// sender and it returns a next-sender. `set_next` is usually called in a
// context where a sender will be connected to a receiver. Since calling
// `set_next` usually involves constructing senders it is allowed to throw an
-// exception, which needs to be handled by a calling sequence-operation. The
+// excpetion, which needs to be handled by a calling sequence-operation. The
// returned object is a sender that can complete with `set_value_t()` or
// `set_stopped_t()`.
struct set_next_t
@@ -100,43 +100,37 @@
STDEXEC_ATTRIBUTE((no_unique_address))
_Receiver __rcvr_;
- template <same_as<get_env_t> _GetEnv, same_as<__t> _Self>
- friend auto tag_invoke(_GetEnv, const _Self& __self) noexcept
- -> env_of_t<_Receiver>
+ auto get_env() const noexcept -> env_of_t<_Receiver>
{
- return stdexec::get_env(__self.__rcvr_);
+ return stdexec::get_env(__rcvr_);
}
- template <same_as<set_value_t> _SetValue, same_as<__t> _Self>
- requires __callable<set_value_t, _Receiver&&>
- friend void tag_invoke(_SetValue, _Self&& __self) noexcept
+ void set_value() noexcept
+ requires __callable<set_value_t, _Receiver>
{
- return stdexec::set_value(static_cast<_Receiver&&>(__self.__rcvr_));
+ return stdexec::set_value(static_cast<_Receiver&&>(__rcvr_));
}
- template <same_as<set_stopped_t> _SetStopped, same_as<__t> _Self>
- requires __callable<set_value_t, _Receiver&&> &&
+ void set_stopped() noexcept
+ requires __callable<set_value_t, _Receiver> &&
(unstoppable_token<_Token> ||
- __callable<set_stopped_t, _Receiver &&>)
- friend void tag_invoke(_SetStopped, _Self&& __self) noexcept
+ __callable<set_stopped_t, _Receiver>)
{
if constexpr (unstoppable_token<_Token>)
{
- stdexec::set_value(static_cast<_Receiver&&>(__self.__rcvr_));
+ stdexec::set_value(static_cast<_Receiver&&>(__rcvr_));
}
else
{
auto __token =
- stdexec::get_stop_token(stdexec::get_env(__self.__rcvr_));
+ stdexec::get_stop_token(stdexec::get_env(__rcvr_));
if (__token.stop_requested())
{
- stdexec::set_stopped(
- static_cast<_Receiver&&>(__self.__rcvr_));
+ stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr_));
}
else
{
- stdexec::set_value(
- static_cast<_Receiver&&>(__self.__rcvr_));
+ stdexec::set_value(static_cast<_Receiver&&>(__rcvr_));
}
}
}
@@ -230,7 +224,7 @@
}
template <class _Sender, class _Env = empty_env>
- constexpr auto operator()(_Sender&&, const _Env&) const noexcept
+ constexpr auto operator()(_Sender&&, _Env&& = {}) const noexcept
-> decltype(__impl<_Sender, _Env>()())
{
return {};
@@ -241,27 +235,27 @@
using __sequence_sndr::get_item_types_t;
inline constexpr get_item_types_t get_item_types{};
-template <class _Sender, class _Env>
+template <class _Sender, class... _Env>
using item_types_of_t = decltype(get_item_types(stdexec::__declval<_Sender>(),
- stdexec::__declval<_Env>()));
+ stdexec::__declval<_Env>()...));
-template <class _Sender, class _Env>
-concept sequence_sender = //
- stdexec::sender_in<_Sender, _Env> && //
+template <class _Sender, class... _Env>
+concept sequence_sender = //
+ stdexec::sender_in<_Sender, _Env...> && //
enable_sequence_sender<stdexec::__decay_t<_Sender>>;
-template <class _Sender, class _Env>
+template <class _Sender, class... _Env>
concept has_sequence_item_types =
- requires(_Sender&& __sndr, _Env&& __env) {
+ requires(_Sender&& __sndr, _Env&&... __env) {
get_item_types(static_cast<_Sender&&>(__sndr),
- static_cast<_Env&&>(__env));
+ static_cast<_Env&&>(__env)...);
};
-template <class _Sender, class _Env>
-concept sequence_sender_in = //
- stdexec::sender_in<_Sender, _Env> && //
- has_sequence_item_types<_Sender, _Env> && //
- sequence_sender<_Sender, _Env>;
+template <class _Sender, class... _Env>
+concept sequence_sender_in = //
+ stdexec::sender_in<_Sender, _Env...> && //
+ has_sequence_item_types<_Sender, _Env...> && //
+ sequence_sender<_Sender, _Env...>;
template <class _Receiver>
struct _WITH_RECEIVER_
@@ -272,7 +266,7 @@
{};
template <class _Receiver, class _Item>
-auto __try_item(_Item*)
+auto __try_item(_Item*) //
-> stdexec::__mexception<_MISSING_SET_NEXT_OVERLOAD_FOR_ITEM_<_Item>,
_WITH_RECEIVER_<_Receiver>>;
@@ -281,7 +275,7 @@
auto __try_item(_Item*) -> stdexec::__msuccess;
template <class _Receiver, class... _Items>
-auto __try_items(exec::item_types<_Items...>*)
+auto __try_items(exec::item_types<_Items...>*) //
-> decltype((stdexec::__msuccess(), ...,
exec::__try_item<_Receiver>(static_cast<_Items*>(nullptr))));
@@ -298,44 +292,39 @@
stdexec::receiver<_Receiver> && //
__sequence_receiver_of<_Receiver, _SequenceItems>;
-template <class _Items, class _Env>
-using __concat_item_signatures_t = stdexec::__mapply<
- stdexec::__q<stdexec::__concat_completion_signatures_t>,
- stdexec::__mapply<stdexec::__transform<stdexec::__mbind_back_q<
- stdexec::completion_signatures_of_t, _Env>>,
- _Items>>;
-
template <class _Completions>
-using __gather_error_signals =
- stdexec::__only_gather_signal<stdexec::set_error_t, _Completions>;
+using __to_sequence_completions_t = //
+ stdexec::__transform_completion_signatures<
+ _Completions,
+ stdexec::__mconst<
+ stdexec::completion_signatures<stdexec::set_value_t()>>::__f,
+ stdexec::__sigs::__default_set_error,
+ stdexec::completion_signatures<stdexec::set_stopped_t()>,
+ stdexec::__concat_completion_signatures>;
-template <class _Completions>
-using __gather_stopped_signals =
- stdexec::__only_gather_signal<stdexec::set_stopped_t, _Completions>;
+template <class _Sender, class... _Env>
+using __item_completion_signatures = //
+ stdexec::transform_completion_signatures<
+ stdexec::__completion_signatures_of_t<_Sender, _Env...>,
+ stdexec::completion_signatures<stdexec::set_value_t()>,
+ stdexec::__mconst<stdexec::completion_signatures<>>::__f>;
-template <class _Completions>
-using __to_sequence_completions_t = stdexec::__concat_completion_signatures_t<
- stdexec::completion_signatures<stdexec::set_value_t()>,
- __gather_error_signals<_Completions>,
- __gather_stopped_signals<_Completions>>;
+template <class _Sequence, class... _Env>
+using __sequence_completion_signatures = //
+ stdexec::transform_completion_signatures<
+ stdexec::__completion_signatures_of_t<_Sequence, _Env...>,
+ stdexec::completion_signatures<stdexec::set_value_t()>,
+ stdexec::__mconst<stdexec::completion_signatures<>>::__f>;
-template <class _Sender, class _Env>
-using __to_sequence_completion_signatures = stdexec::make_completion_signatures<
- _Sender, _Env, stdexec::completion_signatures<stdexec::set_value_t()>,
- stdexec::__mconst<stdexec::completion_signatures<>>::__f>;
-
-template <class _Sequence, class _Env>
-using __sequence_completion_signatures_of_t =
- stdexec::__concat_completion_signatures_t<
- stdexec::__try_make_completion_signatures<
- _Sequence, _Env,
- stdexec::completion_signatures<stdexec::set_value_t()>,
- stdexec::__mconst<stdexec::completion_signatures<>>>,
- stdexec::__mapply<
- stdexec::__q<stdexec::__concat_completion_signatures_t>,
- stdexec::__mapply<stdexec::__transform<stdexec::__mbind_back_q<
- __to_sequence_completion_signatures, _Env>>,
- item_types_of_t<_Sequence, _Env>>>>;
+template <class _Sequence, class... _Env>
+using __sequence_completion_signatures_of_t = //
+ stdexec::__mapply<
+ stdexec::__mtransform<
+ stdexec::__mbind_back_q<__item_completion_signatures, _Env...>,
+ stdexec::__mbind_back<
+ stdexec::__mtry_q<stdexec::__concat_completion_signatures>,
+ __sequence_completion_signatures<_Sequence, _Env...>>>,
+ item_types_of_t<_Sequence, _Env...>>;
template <class _Receiver, class _Sender>
concept sequence_receiver_from = //
@@ -363,17 +352,14 @@
completion_signatures<set_value_t(), set_stopped_t()>>;
template <class _Sender, class _Receiver>
-concept __next_connectable_with_tag_invoke =
+concept __next_connectable =
receiver<_Receiver> && //
sender_in<_Sender, env_of_t<_Receiver>> && //
!sequence_sender_in<_Sender, env_of_t<_Receiver>> && //
sequence_receiver_of<_Receiver,
item_types<stdexec::__decay_t<_Sender>>> && //
- __receiver_from<__stopped_means_break_t<_Receiver>,
- next_sender_of_t<_Receiver, _Sender>> && //
- __connect::__connectable_with_tag_invoke<
- next_sender_of_t<_Receiver, _Sender>&&,
- __stopped_means_break_t<_Receiver>>;
+ sender_to<next_sender_of_t<_Receiver, _Sender>,
+ __stopped_means_break_t<_Receiver>>;
template <class _Sender, class _Receiver>
concept __subscribeable_with_tag_invoke =
@@ -392,19 +378,16 @@
{
using _Domain = __late_domain_of_t<_Sender, env_of_t<_Receiver&>>;
constexpr bool _NothrowTfxSender =
- __nothrow_callable<get_env_t, _Receiver&> &&
__nothrow_callable<transform_sender_t, _Domain, _Sender,
env_of_t<_Receiver&>>;
using _TfxSender = __tfx_sndr<_Sender, _Receiver>;
- if constexpr (__next_connectable_with_tag_invoke<_TfxSender, _Receiver>)
+ if constexpr (__next_connectable<_TfxSender, _Receiver>)
{
using _Result =
- tag_invoke_result_t<connect_t,
- next_sender_of_t<_Receiver, _TfxSender>,
- __stopped_means_break_t<_Receiver>>;
+ connect_result_t<next_sender_of_t<_Receiver, _TfxSender>,
+ __stopped_means_break_t<_Receiver>>;
constexpr bool _Nothrow =
- nothrow_tag_invocable<connect_t,
- next_sender_of_t<_Receiver, _TfxSender>,
+ __nothrow_connectable<next_sender_of_t<_Receiver, _TfxSender>,
__stopped_means_break_t<_Receiver>>;
return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
}
@@ -429,8 +412,8 @@
using __select_impl_t = decltype(__select_impl<_Sender, _Receiver>());
template <sender _Sender, receiver _Receiver>
- requires __next_connectable_with_tag_invoke<
- __tfx_sndr<_Sender, _Receiver>, _Receiver> ||
+ requires __next_connectable<__tfx_sndr<_Sender, _Receiver>,
+ _Receiver> ||
__subscribeable_with_tag_invoke<__tfx_sndr<_Sender, _Receiver>,
_Receiver> ||
__is_debug_env<env_of_t<_Receiver>>
@@ -441,19 +424,18 @@
using _TfxSender = __tfx_sndr<_Sender, _Receiver>;
auto&& __env = get_env(__rcvr);
auto __domain = __get_late_domain(__sndr, __env);
- if constexpr (__next_connectable_with_tag_invoke<_TfxSender, _Receiver>)
+ if constexpr (__next_connectable<_TfxSender, _Receiver>)
{
static_assert(
- operation_state<tag_invoke_result_t<
- connect_t, next_sender_of_t<_Receiver, _TfxSender>,
- __stopped_means_break_t<_Receiver>>>,
+ operation_state<
+ connect_result_t<next_sender_of_t<_Receiver, _TfxSender>,
+ __stopped_means_break_t<_Receiver>>>,
"stdexec::connect(sender, receiver) must return a type that "
"satisfies the operation_state concept");
next_sender_of_t<_Receiver, _TfxSender> __next = set_next(
__rcvr, transform_sender(
__domain, static_cast<_Sender&&>(__sndr), __env));
- return tag_invoke(
- connect_t{},
+ return stdexec::connect(
static_cast<next_sender_of_t<_Receiver, _TfxSender>&&>(__next),
__stopped_means_break_t<_Receiver>{
static_cast<_Receiver&&>(__rcvr)});
@@ -496,8 +478,7 @@
}
}
- friend constexpr auto tag_invoke(forwarding_query_t, subscribe_t) noexcept
- -> bool
+ static constexpr auto query(stdexec::forwarding_query_t) noexcept -> bool
{
return false;
}
@@ -517,10 +498,8 @@
template <class _Sender, class _Receiver>
concept sequence_sender_to = sequence_receiver_from<_Receiver, _Sender> && //
requires(_Sender&& __sndr, _Receiver&& __rcvr) {
- {
- subscribe(static_cast<_Sender&&>(__sndr),
- static_cast<_Receiver&&>(__rcvr))
- };
+ subscribe(static_cast<_Sender&&>(__sndr),
+ static_cast<_Receiver&&>(__rcvr));
};
template <class _Receiver>
diff --git a/include/sdbusplus/async/stdexec/stop_token.hpp b/include/sdbusplus/async/stdexec/stop_token.hpp
index 6c00940..7456ad9 100644
--- a/include/sdbusplus/async/stdexec/stop_token.hpp
+++ b/include/sdbusplus/async/stdexec/stop_token.hpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2021-2022 Facebook, Inc. and its affiliates
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
@@ -16,8 +16,7 @@
*/
#pragma once
-#include "__detail/__config.hpp"
-#include "concepts.hpp"
+#include "__detail/__stop_token.hpp"
#include <atomic>
#include <concepts>
@@ -33,19 +32,9 @@
namespace stdexec
{
-// [stoptoken.inplace], class in_place_stop_token
-class in_place_stop_token;
-
-// [stopsource.inplace], class in_place_stop_source
-class in_place_stop_source;
-
-// [stopcallback.inplace], class template in_place_stop_callback
-template <class _Callback>
-class in_place_stop_callback;
-
namespace __stok
{
-struct __in_place_stop_callback_base
+struct __inplace_stop_callback_base
{
void __execute() noexcept
{
@@ -53,10 +42,10 @@
}
protected:
- using __execute_fn_t = void(__in_place_stop_callback_base*) noexcept;
+ using __execute_fn_t = void(__inplace_stop_callback_base*) noexcept;
- explicit __in_place_stop_callback_base( //
- const in_place_stop_source* __source, //
+ explicit __inplace_stop_callback_base( //
+ const inplace_stop_source* __source, //
__execute_fn_t* __execute) noexcept :
__source_(__source),
__execute_(__execute)
@@ -64,12 +53,12 @@
void __register_callback_() noexcept;
- friend in_place_stop_source;
+ friend inplace_stop_source;
- const in_place_stop_source* __source_;
+ const inplace_stop_source* __source_;
__execute_fn_t* __execute_;
- __in_place_stop_callback_base* __next_ = nullptr;
- __in_place_stop_callback_base** __prev_ptr_ = nullptr;
+ __inplace_stop_callback_base* __next_ = nullptr;
+ __inplace_stop_callback_base** __prev_ptr_ = nullptr;
bool* __removed_during_callback_ = nullptr;
std::atomic<bool> __callback_completed_{false};
};
@@ -96,9 +85,6 @@
static constexpr uint32_t __yield_threshold_ = 20;
uint32_t __count_ = 0;
};
-
-template <template <class> class>
-struct __check_type_alias_exists;
} // namespace __stok
// [stoptoken.never], class never_stop_token
@@ -128,17 +114,17 @@
};
template <class _Callback>
-class in_place_stop_callback;
+class inplace_stop_callback;
-// [stopsource.inplace], class in_place_stop_source
-class in_place_stop_source
+// [stopsource.inplace], class inplace_stop_source
+class inplace_stop_source
{
public:
- in_place_stop_source() noexcept = default;
- ~in_place_stop_source();
- in_place_stop_source(in_place_stop_source&&) = delete;
+ inplace_stop_source() noexcept = default;
+ ~inplace_stop_source();
+ inplace_stop_source(inplace_stop_source&&) = delete;
- auto get_token() const noexcept -> in_place_stop_token;
+ auto get_token() const noexcept -> inplace_stop_token;
auto request_stop() noexcept -> bool;
@@ -149,10 +135,10 @@
}
private:
- friend in_place_stop_token;
- friend __stok::__in_place_stop_callback_base;
+ friend inplace_stop_token;
+ friend __stok::__inplace_stop_callback_base;
template <class>
- friend class in_place_stop_callback;
+ friend class inplace_stop_callback;
auto __lock_() const noexcept -> uint8_t;
void __unlock_(uint8_t) const noexcept;
@@ -160,39 +146,38 @@
auto __try_lock_unless_stop_requested_(bool) const noexcept -> bool;
auto __try_add_callback_(
- __stok::__in_place_stop_callback_base*) const noexcept -> bool;
+ __stok::__inplace_stop_callback_base*) const noexcept -> bool;
void __remove_callback_(
- __stok::__in_place_stop_callback_base*) const noexcept;
+ __stok::__inplace_stop_callback_base*) const noexcept;
static constexpr uint8_t __stop_requested_flag_ = 1;
static constexpr uint8_t __locked_flag_ = 2;
mutable std::atomic<uint8_t> __state_{0};
- mutable __stok::__in_place_stop_callback_base* __callbacks_ = nullptr;
+ mutable __stok::__inplace_stop_callback_base* __callbacks_ = nullptr;
std::thread::id __notifying_thread_;
};
-// [stoptoken.inplace], class in_place_stop_token
-class in_place_stop_token
+// [stoptoken.inplace], class inplace_stop_token
+class inplace_stop_token
{
public:
template <class _Fun>
- using callback_type = in_place_stop_callback<_Fun>;
+ using callback_type = inplace_stop_callback<_Fun>;
- in_place_stop_token() noexcept : __source_(nullptr) {}
+ inplace_stop_token() noexcept : __source_(nullptr) {}
- in_place_stop_token(const in_place_stop_token& __other) noexcept = default;
+ inplace_stop_token(const inplace_stop_token& __other) noexcept = default;
- in_place_stop_token(in_place_stop_token&& __other) noexcept :
+ inplace_stop_token(inplace_stop_token&& __other) noexcept :
__source_(std::exchange(__other.__source_, {}))
{}
- auto operator=(const in_place_stop_token& __other) noexcept
- -> in_place_stop_token& = default;
+ auto operator=(const inplace_stop_token& __other) noexcept
+ -> inplace_stop_token& = default;
- auto operator=(in_place_stop_token&& __other) noexcept
- -> in_place_stop_token&
+ auto operator=(inplace_stop_token&& __other) noexcept -> inplace_stop_token&
{
__source_ = std::exchange(__other.__source_, nullptr);
return *this;
@@ -208,51 +193,49 @@
return __source_ != nullptr;
}
- void swap(in_place_stop_token& __other) noexcept
+ void swap(inplace_stop_token& __other) noexcept
{
std::swap(__source_, __other.__source_);
}
- auto operator==(const in_place_stop_token&) const noexcept
- -> bool = default;
+ auto operator==(const inplace_stop_token&) const noexcept -> bool = default;
private:
- friend in_place_stop_source;
+ friend inplace_stop_source;
template <class>
- friend class in_place_stop_callback;
+ friend class inplace_stop_callback;
- explicit in_place_stop_token(const in_place_stop_source* __source) noexcept
- :
+ explicit inplace_stop_token(const inplace_stop_source* __source) noexcept :
__source_(__source)
{}
- const in_place_stop_source* __source_;
+ const inplace_stop_source* __source_;
};
-inline auto in_place_stop_source::get_token() const noexcept
- -> in_place_stop_token
+inline auto inplace_stop_source::get_token() const noexcept
+ -> inplace_stop_token
{
- return in_place_stop_token{this};
+ return inplace_stop_token{this};
}
-// [stopcallback.inplace], class template in_place_stop_callback
+// [stopcallback.inplace], class template inplace_stop_callback
template <class _Fun>
-class in_place_stop_callback : __stok::__in_place_stop_callback_base
+class inplace_stop_callback : __stok::__inplace_stop_callback_base
{
public:
template <class _Fun2>
requires constructible_from<_Fun, _Fun2>
- explicit in_place_stop_callback(in_place_stop_token __token,
- _Fun2&& __fun) //
+ explicit inplace_stop_callback(inplace_stop_token __token,
+ _Fun2&& __fun) //
noexcept(__nothrow_constructible_from<_Fun, _Fun2>) :
- __stok::__in_place_stop_callback_base(
- __token.__source_, &in_place_stop_callback::__execute_impl_),
+ __stok::__inplace_stop_callback_base(
+ __token.__source_, &inplace_stop_callback::__execute_impl_),
__fun_(static_cast<_Fun2&&>(__fun))
{
__register_callback_();
}
- ~in_place_stop_callback()
+ ~inplace_stop_callback()
{
if (__source_ != nullptr)
__source_->__remove_callback_(this);
@@ -260,9 +243,9 @@
private:
static void
- __execute_impl_(__stok::__in_place_stop_callback_base* cb) noexcept
+ __execute_impl_(__stok::__inplace_stop_callback_base* cb) noexcept
{
- std::move(static_cast<in_place_stop_callback*>(cb)->__fun_)();
+ std::move(static_cast<inplace_stop_callback*>(cb)->__fun_)();
}
STDEXEC_ATTRIBUTE((no_unique_address))
@@ -271,7 +254,7 @@
namespace __stok
{
-inline void __in_place_stop_callback_base::__register_callback_() noexcept
+inline void __inplace_stop_callback_base::__register_callback_() noexcept
{
if (__source_ != nullptr)
{
@@ -286,14 +269,14 @@
}
} // namespace __stok
-inline in_place_stop_source::~in_place_stop_source()
+inline inplace_stop_source::~inplace_stop_source()
{
STDEXEC_ASSERT(
(__state_.load(std::memory_order_relaxed) & __locked_flag_) == 0);
STDEXEC_ASSERT(__callbacks_ == nullptr);
}
-inline auto in_place_stop_source::request_stop() noexcept -> bool
+inline auto inplace_stop_source::request_stop() noexcept -> bool
{
if (!__try_lock_unless_stop_requested_(true))
return true;
@@ -330,7 +313,7 @@
return false;
}
-inline auto in_place_stop_source::__lock_() const noexcept -> uint8_t
+inline auto inplace_stop_source::__lock_() const noexcept -> uint8_t
{
__stok::__spin_wait __spin;
auto __old_state = __state_.load(std::memory_order_relaxed);
@@ -348,12 +331,12 @@
return __old_state;
}
-inline void in_place_stop_source::__unlock_(uint8_t __old_state) const noexcept
+inline void inplace_stop_source::__unlock_(uint8_t __old_state) const noexcept
{
(void)__state_.store(__old_state, std::memory_order_release);
}
-inline auto in_place_stop_source::__try_lock_unless_stop_requested_(
+inline auto inplace_stop_source::__try_lock_unless_stop_requested_(
bool __set_stop_requested) const noexcept -> bool
{
__stok::__spin_wait __spin;
@@ -387,8 +370,8 @@
return true;
}
-inline auto in_place_stop_source::__try_add_callback_(
- __stok::__in_place_stop_callback_base* __callbk) const noexcept -> bool
+inline auto inplace_stop_source::__try_add_callback_(
+ __stok::__inplace_stop_callback_base* __callbk) const noexcept -> bool
{
if (!__try_lock_unless_stop_requested_(false))
{
@@ -408,8 +391,8 @@
return true;
}
-inline void in_place_stop_source::__remove_callback_(
- __stok::__in_place_stop_callback_base* __callbk) const noexcept
+inline void inplace_stop_source::__remove_callback_(
+ __stok::__inplace_stop_callback_base* __callbk) const noexcept
{
auto __old_state = __lock_();
@@ -452,50 +435,16 @@
}
}
-template <class _Token>
-concept stoppable_token = copy_constructible<_Token> && //
- move_constructible<_Token> && //
- std::is_nothrow_copy_constructible_v<_Token> && //
- std::is_nothrow_move_constructible_v<_Token> && //
- equality_comparable<_Token> && //
- requires(const _Token& __token) {
- {
- __token.stop_requested()
- } noexcept -> __boolean_testable_;
- {
- __token.stop_possible()
- } noexcept -> __boolean_testable_;
- // workaround ICE in appleclang 13.1
-#if !defined(__clang__)
- typename __stok::__check_type_alias_exists<
- _Token::template callback_type>;
-#endif
- };
+using in_place_stop_token
+ [[deprecated("in_place_stop_token has been renamed inplace_stop_token")]] =
+ inplace_stop_token;
-template <class _Token, typename _Callback, typename _Initializer = _Callback>
-concept stoppable_token_for =
- stoppable_token<_Token> && __callable<_Callback> && //
- requires { typename _Token::template callback_type<_Callback>; } && //
- constructible_from<_Callback, _Initializer> && //
- constructible_from< //
- typename _Token::template callback_type<_Callback>, _Token,
- _Initializer> && //
- constructible_from< //
- typename _Token::template callback_type<_Callback>, _Token&,
- _Initializer> && //
- constructible_from< //
- typename _Token::template callback_type<_Callback>, const _Token,
- _Initializer> && //
- constructible_from<typename _Token::template callback_type<_Callback>,
- const _Token&, _Initializer>;
+using in_place_stop_source
+ [[deprecated("in_place_stop_token has been renamed inplace_stop_source")]] =
+ inplace_stop_source;
-template <class _Token>
-concept unstoppable_token = //
- stoppable_token<_Token> && //
- requires {
- {
- _Token::stop_possible()
- } -> __boolean_testable_;
- } && //
- (!_Token::stop_possible());
+template <class _Fun>
+using in_place_stop_callback [[deprecated(
+ "in_place_stop_callback has been renamed inplace_stop_callback")]] =
+ inplace_stop_callback<_Fun>;
} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/task.hpp b/include/sdbusplus/async/stdexec/task.hpp
index 60f9e49..2e9cf90 100644
--- a/include/sdbusplus/async/stdexec/task.hpp
+++ b/include/sdbusplus/async/stdexec/task.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022 NVIDIA Corporation
+ * 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
@@ -16,6 +16,8 @@
#pragma once
#include "../stdexec/__detail/__meta.hpp"
+#include "../stdexec/__detail/__optional.hpp"
+#include "../stdexec/__detail/__variant.hpp"
#include "../stdexec/coroutine.hpp"
#include "../stdexec/execution.hpp"
#include "any_sender_of.hpp"
@@ -27,7 +29,6 @@
#include <cassert>
#include <exception>
#include <utility>
-#include <variant>
STDEXEC_PRAGMA_PUSH()
STDEXEC_PRAGMA_IGNORE_GNU("-Wundefined-inline")
@@ -68,16 +69,17 @@
};
template <class _ParentPromise>
-void __check_parent_promise_has_scheduler() noexcept
+constexpr bool __check_parent_promise_has_scheduler() noexcept
{
static_assert(__indirect_scheduler_provider<_ParentPromise>,
"exec::task<T> cannot be co_await-ed in a coroutine that "
"does not have an associated scheduler.");
+ return __indirect_scheduler_provider<_ParentPromise>;
}
struct __forward_stop_request
{
- in_place_stop_source& __stop_source_;
+ inplace_stop_source& __stop_source_;
void operator()() noexcept
{
@@ -97,6 +99,9 @@
__sticky
};
+struct __parent_promise_t
+{};
+
template <__scheduler_affinity _SchedulerAffinity =
__scheduler_affinity::__sticky>
class __default_task_context_impl
@@ -110,31 +115,39 @@
STDEXEC_ATTRIBUTE((no_unique_address))
__if_c<__with_scheduler, __any_scheduler, __ignore> //
__scheduler_{exec::inline_scheduler{}};
- in_place_stop_token __stop_token_;
-
- friend auto tag_invoke(get_scheduler_t,
- const __default_task_context_impl& __self) noexcept
- -> const __any_scheduler&
- requires(__with_scheduler)
- {
- return __self.__scheduler_;
- }
-
- friend auto tag_invoke(get_stop_token_t,
- const __default_task_context_impl& __self) noexcept
- -> in_place_stop_token
- {
- return __self.__stop_token_;
- }
+ inplace_stop_token __stop_token_;
public:
- __default_task_context_impl() = default;
+ template <class _ParentPromise>
+ explicit __default_task_context_impl(__parent_promise_t,
+ _ParentPromise& __parent) noexcept
+ {
+ if constexpr (_SchedulerAffinity == __scheduler_affinity::__sticky)
+ {
+ if constexpr (__check_parent_promise_has_scheduler<
+ _ParentPromise>())
+ {
+ __scheduler_ = get_scheduler(get_env(__parent));
+ }
+ }
+ }
template <scheduler _Scheduler>
explicit __default_task_context_impl(_Scheduler&& __scheduler) :
__scheduler_{static_cast<_Scheduler&&>(__scheduler)}
{}
+ auto query(get_scheduler_t) const noexcept -> const __any_scheduler&
+ requires(__with_scheduler)
+ {
+ return __scheduler_;
+ }
+
+ auto query(get_stop_token_t) const noexcept -> inplace_stop_token
+ {
+ return __stop_token_;
+ }
+
[[nodiscard]] auto stop_requested() const noexcept -> bool
{
return __stop_token_.stop_requested();
@@ -151,6 +164,8 @@
using promise_context_t = __default_task_context_impl;
template <class _ThisPromise, class _ParentPromise = void>
+ requires(!__with_scheduler) ||
+ __indirect_scheduler_provider<_ParentPromise>
using awaiter_context_t = __default_awaiter_context<_ParentPromise>;
};
@@ -171,19 +186,13 @@
explicit __default_awaiter_context(
__default_task_context_impl<_Affinity>& __self,
_ParentPromise& __parent) noexcept
- {
- if constexpr (_Affinity == __scheduler_affinity::__sticky)
- {
- __check_parent_promise_has_scheduler<_ParentPromise>();
- __self.__scheduler_ = get_scheduler(get_env(__parent));
- }
- }
+ {}
};
////////////////////////////////////////////////////////////////////////////////
// This is the context to be associated with basic_task's awaiter when
// the parent coroutine's promise type is known, is a __stop_token_provider,
-// and its stop token type is neither in_place_stop_token nor unstoppable.
+// and its stop token type is neither inplace_stop_token nor unstoppable.
template <__indirect_stop_token_provider _ParentPromise>
struct __default_awaiter_context<_ParentPromise>
{
@@ -202,25 +211,20 @@
__stop_callback_{get_stop_token(get_env(__parent)),
__forward_stop_request{__stop_source_}}
{
- if constexpr (_Affinity == __scheduler_affinity::__sticky)
- {
- __check_parent_promise_has_scheduler<_ParentPromise>();
- __self.__scheduler_ = get_scheduler(get_env(__parent));
- }
static_assert(
std::is_nothrow_constructible_v<__stop_callback_t, __stop_token_t,
__forward_stop_request>);
__self.__stop_token_ = __stop_source_.get_token();
}
- in_place_stop_source __stop_source_{};
+ inplace_stop_source __stop_source_{};
__stop_callback_t __stop_callback_;
};
-// If the parent coroutine's type has a stop token of type in_place_stop_token,
+// If the parent coroutine's type has a stop token of type inplace_stop_token,
// we don't need to register a stop callback.
template <__indirect_stop_token_provider _ParentPromise>
- requires std::same_as<in_place_stop_token,
+ requires std::same_as<inplace_stop_token,
stop_token_of_t<env_of_t<_ParentPromise>>>
struct __default_awaiter_context<_ParentPromise>
{
@@ -229,11 +233,6 @@
__default_task_context_impl<_Affinity>& __self,
_ParentPromise& __parent) noexcept
{
- if constexpr (_Affinity == __scheduler_affinity::__sticky)
- {
- __check_parent_promise_has_scheduler<_ParentPromise>();
- __self.__scheduler_ = get_scheduler(get_env(__parent));
- }
__self.__stop_token_ = get_stop_token(get_env(__parent));
}
};
@@ -245,16 +244,9 @@
struct __default_awaiter_context<_ParentPromise>
{
template <__scheduler_affinity _Affinity>
- explicit __default_awaiter_context(
- __default_task_context_impl<_Affinity>& __self,
- _ParentPromise& __parent) noexcept
- {
- if constexpr (_Affinity == __scheduler_affinity::__sticky)
- {
- __check_parent_promise_has_scheduler<_ParentPromise>();
- __self.__scheduler_ = get_scheduler(get_env(__parent));
- }
- }
+ explicit __default_awaiter_context(__default_task_context_impl<_Affinity>&,
+ _ParentPromise&) noexcept
+ {}
};
// Finally, if we don't know the parent coroutine's promise type, assume the
@@ -266,13 +258,7 @@
explicit __default_awaiter_context(
__default_task_context_impl<_Affinity>& __self,
_ParentPromise& __parent) noexcept
- {
- if constexpr (_Affinity == __scheduler_affinity::__sticky)
- {
- __check_parent_promise_has_scheduler<_ParentPromise>();
- __self.__scheduler_ = get_scheduler(get_env(__parent));
- }
- }
+ {}
template <__scheduler_affinity _Affinity,
__indirect_stop_token_provider _ParentPromise>
@@ -280,20 +266,14 @@
__default_task_context_impl<_Affinity>& __self,
_ParentPromise& __parent)
{
- if constexpr (_Affinity == __scheduler_affinity::__sticky)
- {
- __check_parent_promise_has_scheduler<_ParentPromise>();
- __self.__scheduler_ = get_scheduler(get_env(__parent));
- }
// Register a callback that will request stop on this basic_task's
// stop_source when stop is requested on the parent coroutine's stop
// token.
using __stop_token_t = stop_token_of_t<env_of_t<_ParentPromise>>;
using __stop_callback_t =
- typename __stop_token_t::template callback_type<
- __forward_stop_request>;
+ stop_callback_for_t<__stop_token_t, __forward_stop_request>;
- if constexpr (std::same_as<__stop_token_t, in_place_stop_token>)
+ if constexpr (std::same_as<__stop_token_t, inplace_stop_token>)
{
__self.__stop_token_ = get_stop_token(get_env(__parent));
}
@@ -306,7 +286,7 @@
}
}
- in_place_stop_source __stop_source_{};
+ inplace_stop_source __stop_source_{};
std::any __stop_callback_{};
};
@@ -322,10 +302,10 @@
{
void return_value(_Ty value)
{
- __data_.template emplace<1>(std::move(value));
+ __data_.template emplace<0>(std::move(value));
}
- std::variant<std::monostate, _Ty, std::exception_ptr> __data_{};
+ __variant_for<_Ty, std::exception_ptr> __data_{};
};
template <>
@@ -336,10 +316,10 @@
void return_void()
{
- __data_.template emplace<1>(__void{});
+ __data_.template emplace<0>(__void{});
}
- std::variant<std::monostate, __void, std::exception_ptr> __data_{};
+ __variant_for<__void, std::exception_ptr> __data_{};
};
enum class disposition : unsigned
@@ -404,8 +384,14 @@
static void await_resume() noexcept {}
};
+ using __promise_context_t =
+ typename _Context::template promise_context_t<__promise>;
+
struct __promise : __promise_base<_Ty>, with_awaitable_senders<__promise>
{
+ using __t = __promise;
+ using __id = __promise;
+
auto get_return_object() noexcept -> basic_task
{
return basic_task(
@@ -424,12 +410,20 @@
[[nodiscard]] auto disposition() const noexcept -> __task::disposition
{
- return static_cast<__task::disposition>(this->__data_.index());
+ switch (this->__data_.index())
+ {
+ case 0:
+ return __task::disposition::succeeded;
+ case 1:
+ return __task::disposition::failed;
+ default:
+ return __task::disposition::stopped;
+ }
}
void unhandled_exception() noexcept
{
- this->__data_.template emplace<2>(std::current_exception());
+ this->__data_.template emplace<1>(std::current_exception());
}
template <sender _Awaitable>
@@ -440,7 +434,7 @@
// 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_)),
+ get_scheduler(*__context_)),
*this);
}
@@ -454,7 +448,7 @@
{
// Create a cleanup action that transitions back onto the
// current scheduler:
- auto __sched = get_scheduler(__context_);
+ auto __sched = get_scheduler(*__context_);
auto __cleanup_task = at_coroutine_exit(schedule,
std::move(__sched));
// Insert the cleanup action into the head of the continuation
@@ -465,7 +459,7 @@
__coro::coroutine_handle<__promise>::from_promise(*this));
(void)__cleanup_task.await_resume();
}
- __context_.set_scheduler(__box.__sched_);
+ __context_->set_scheduler(__box.__sched_);
return as_awaitable(schedule(__box.__sched_), *this);
}
@@ -477,16 +471,12 @@
static_cast<_Awaitable&&>(__awaitable));
}
- using __context_t =
- typename _Context::template promise_context_t<__promise>;
-
- friend auto tag_invoke(get_env_t, const __promise& __self) noexcept
- -> const __context_t&
+ auto get_env() const noexcept -> const __promise_context_t&
{
- return __self.__context_;
+ return *__context_;
}
- __context_t __context_;
+ __optional<__promise_context_t> __context_{};
bool __rescheduled_{false};
};
@@ -494,8 +484,7 @@
struct __task_awaitable
{
__coro::coroutine_handle<__promise> __coro_;
- std::optional<awaiter_context_t<__promise, _ParentPromise>>
- __context_{};
+ __optional<awaiter_context_t<__promise, _ParentPromise>> __context_{};
~__task_awaitable()
{
@@ -514,7 +503,9 @@
-> __coro::coroutine_handle<>
{
static_assert(__one_of<_ParentPromise, _ParentPromise2, void>);
- __context_.emplace(__coro_.promise().__context_,
+ __coro_.promise().__context_.emplace(__parent_promise_t(),
+ __parent.promise());
+ __context_.emplace(*__coro_.promise().__context_,
__parent.promise());
__coro_.promise().set_continuation(__parent);
if constexpr (requires {
@@ -532,21 +523,22 @@
__context_.reset();
scope_guard __on_exit{
[this]() noexcept { std::exchange(__coro_, {}).destroy(); }};
- if (__coro_.promise().__data_.index() == 2)
+ if (__coro_.promise().__data_.index() == 1)
std::rethrow_exception(
- std::get<2>(std::move(__coro_.promise().__data_)));
+ std::move(__coro_.promise().__data_.template get<1>()));
if constexpr (!std::is_void_v<_Ty>)
- return std::get<1>(std::move(__coro_.promise().__data_));
+ return std::move(__coro_.promise().__data_.template get<0>());
}
};
+ public:
// Make this task awaitable within a particular context:
template <class _ParentPromise>
requires constructible_from<
- awaiter_context_t<__promise, _ParentPromise>, __promise&,
+ awaiter_context_t<__promise, _ParentPromise>, __promise_context_t&,
_ParentPromise&>
- friend auto tag_invoke(as_awaitable_t, basic_task&& __self,
- _ParentPromise&) noexcept
+ STDEXEC_MEMFN_DECL(auto as_awaitable)(this basic_task&& __self,
+ _ParentPromise&) noexcept
-> __task_awaitable<_ParentPromise>
{
return __task_awaitable<_ParentPromise>{
@@ -554,18 +546,18 @@
}
// Make this task generally awaitable:
- friend auto operator co_await(basic_task&& __self) noexcept
- -> __task_awaitable<>
+ auto operator co_await() && noexcept -> __task_awaitable<>
requires __mvalid<awaiter_context_t, __promise>
{
- return __task_awaitable<>{std::exchange(__self.__coro_, {})};
+ return __task_awaitable<>{std::exchange(__coro_, {})};
}
// From the list of types [_Ty], remove any types that are void, and send
// the resulting list to __qf<set_value_t>, which uses the list of types
// as arguments of a function type. In other words, set_value_t() if _Ty
// is void, and set_value_t(_Ty) otherwise.
- using __set_value_sig_t = __minvoke<__remove<void, __qf<set_value_t>>, _Ty>;
+ using __set_value_sig_t =
+ __minvoke<__mremove<void, __qf<set_value_t>>, _Ty>;
// Specify basic_task's completion signatures
// This is only necessary when basic_task is not generally awaitable
@@ -574,8 +566,7 @@
completion_signatures<__set_value_sig_t,
set_error_t(std::exception_ptr), set_stopped_t()>;
- friend auto tag_invoke(get_completion_signatures_t, const basic_task&, auto)
- -> __task_traits_t
+ auto get_completion_signatures(__ignore = {}) const -> __task_traits_t
{
return {};
}
@@ -606,4 +597,10 @@
inline constexpr __task::__reschedule_coroutine_on reschedule_coroutine_on{};
} // namespace exec
+namespace stdexec
+{
+template <class _Ty, class _Context>
+inline constexpr bool enable_sender<exec::basic_task<_Ty, _Context>> = true;
+} // namespace stdexec
+
STDEXEC_PRAGMA_POP()