| /* |
| * Copyright (c) 2021-2022 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 "__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 "__detail/__intrusive_ptr.hpp" |
| #include "__detail/__meta.hpp" |
| #include "__detail/__scope.hpp" |
| #include "__detail/__type_traits.hpp" |
| #include "__detail/__utility.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{}, (_Receiver&&)__rcvr, (_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{}, (_Receiver&&)__rcvr, (_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{}, (_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...>) |
| { |
| ((_Fun&&)__fun)((_Args&&)__args...); |
| } |
| else |
| { |
| try |
| { |
| ((_Fun&&)__fun)((_Args&&)__args...); |
| } |
| catch (...) |
| { |
| set_error((_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 |
| { |
| #if STDEXEC_NVHPC() |
| template <class _Ty = __q<__types>, class... _Args> |
| __types<__minvoke<_Ty, _Args...>> __test(set_value_t (*)(_Args...), |
| set_value_t = {}, _Ty = {}); |
| template <class _Ty = __q<__types>, class _Error> |
| __types<__minvoke<_Ty, _Error>> __test(set_error_t (*)(_Error), |
| set_error_t = {}, _Ty = {}); |
| template <class _Ty = __q<__types>> |
| __types<__minvoke<_Ty>> __test(set_stopped_t (*)(), set_stopped_t = {}, |
| _Ty = {}); |
| __types<> __test(__ignore, __ignore, __ignore = {}); |
| |
| 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; |
| |
| #else |
| |
| template <same_as<set_value_t> _Tag, class _Ty = __q<__types>, class... _Args> |
| __types<__minvoke<_Ty, _Args...>> __test(_Tag (*)(_Args...)); |
| template <same_as<set_error_t> _Tag, class _Ty = __q<__types>, class _Error> |
| __types<__minvoke<_Ty, _Error>> __test(_Tag (*)(_Error)); |
| template <same_as<set_stopped_t> _Tag, class _Ty = __q<__types>> |
| __types<__minvoke<_Ty>> __test(_Tag (*)()); |
| template <class, class = void> |
| __types<> __test(...); |
| template <class _Tag, class _Ty = void, class... _Args> |
| void __test(_Tag (*)(_Args...) noexcept) = delete; |
| #endif |
| |
| #if STDEXEC_NVHPC() |
| template <class _Sig> |
| concept __completion_signature = __compl_sigs::__is_compl_sig<_Sig>; |
| |
| template <class _Sig, class _Tag, class _Ty = __q<__types>> |
| using __signal_args_t = |
| decltype(__compl_sigs::__test((_Sig*)nullptr, _Tag{}, _Ty{})); |
| #else |
| template <class _Sig> |
| concept __completion_signature = |
| __typename<decltype(__compl_sigs::__test((_Sig*)nullptr))>; |
| |
| template <class _Sig, class _Tag, class _Ty = __q<__types>> |
| using __signal_args_t = |
| decltype(__compl_sigs::__test<_Tag, _Ty>((_Sig*)nullptr)); |
| #endif |
| } // namespace __compl_sigs |
| |
| using __compl_sigs::__completion_signature; |
| |
| template <__compl_sigs::__completion_signature... _Sigs> |
| struct completion_signatures |
| { |
| // Uncomment this to see where completion_signatures is |
| // erroneously getting instantiated: |
| // static_assert(sizeof...(_Sigs) == -1u); |
| }; |
| |
| namespace __compl_sigs |
| { |
| template <class _TaggedTuple, __completion_tag _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_( |
| (_Sig*)nullptr, (_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_( // |
| (_Completions*)nullptr, (_TaggedTuple*)nullptr, |
| (_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...> |
| __msuccess __try_completion(_Tag (*)(_Args...)); |
| |
| template <class _Receiver, class... _Sigs> |
| auto __try_completions(completion_signatures<_Sigs...>*) |
| -> decltype((__msuccess(), ..., |
| stdexec::__try_completion<_Receiver>((_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>; |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [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 bool tag_invoke(forwarding_query_t, |
| const __is_debug_env_t&) noexcept |
| { |
| 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 __debug_env_t<_Env> tag_invoke(_Tag, __debug_receiver) noexcept |
| { |
| 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((_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((_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((_Sender&&)__sndr, (_Env&&)__env); |
| } |
| else |
| { |
| return default_domain().transform_env((_Sender&&)__sndr, |
| (_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((_Sender&&)__sndr, __env...); |
| } |
| else |
| { |
| return default_domain().transform_sender((_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, (_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, (_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*/ decltype(auto) operator()(_Domain __dom, _Sender&& __sndr, |
| const _Env& __env) const |
| { |
| static_assert(__none_of<_Domain, dependent_domain>); |
| return __transform_sender()( |
| __dom, |
| dependent_domain().transform_sender((_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> |
| decltype(auto) dependent_domain::transform_sender(_Sender&& __sndr, |
| const _Env& __env) const |
| { |
| // apply any algorithm-specific transformation to the environment |
| const auto& __env2 = transform_env(*this, (_Sender&&)__sndr, __env); |
| |
| // recursively transform the sender to determine the domain |
| return __sexpr_apply((_Sender&&)__sndr, |
| [&]<class _Tag, class _Data, class... _Childs>( |
| _Tag, _Data&& __data, _Childs&&... __childs) { |
| // TODO: propagate meta-exceptions here: |
| auto __sndr2 = __make_sexpr<_Tag>( |
| (_Data&&)__data, __domain::__transform_sender()( |
| *this, (_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); |
| } |
| STDEXEC_UNREACHABLE(); |
| }); |
| } |
| |
| // A helper for use when building sender trees where each node must be |
| // transformed. |
| template <class _Domain, class _Env> |
| auto __make_transformer(_Domain, const _Env& __env) |
| { |
| return [&]<class _Tag>(_Tag) { |
| return [&]<class... _Args>(_Args&&... __args) -> decltype(auto) { |
| return stdexec::transform_sender(_Domain(), |
| _Tag()((_Args&&)__args...), __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(), (_Sender&&)__sndr, |
| (_Args&&)__args...); |
| } |
| else |
| { |
| return default_domain().apply_sender(_Tag(), (_Sender&&)__sndr, |
| (_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 (_TfxSender(*)()) nullptr; |
| } |
| else if constexpr (__with_tag_invoke<_Sender, _Env>) |
| { |
| using _Result = tag_invoke_result_t<get_completion_signatures_t, |
| _TfxSender, _Env>; |
| return (_Result(*)()) nullptr; |
| } |
| else if constexpr (__with_member_alias<_Sender, _Env>) |
| { |
| using _Result = __member_alias_t<_Sender, _Env>; |
| return (_Result(*)()) nullptr; |
| } |
| else if constexpr (__awaitable<_Sender, __env::__promise<_Env>>) |
| { |
| using _Result = __await_result_t<_Sender, __env::__promise<_Env>>; |
| return (completion_signatures< |
| // set_value_t() or set_value_t(T) |
| __minvoke<__remove<void, __qf<set_value_t>>, _Result>, |
| set_error_t(std::exception_ptr), |
| set_stopped_t()>(*)()) 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 (__debug::__completion_signatures(*)()) nullptr; |
| } |
| else |
| { |
| using _Result = |
| __mexception<_UNRECOGNIZED_SENDER_TYPE_<>, |
| _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>; |
| return (_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((_Sender&&)__sndr, (_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>((_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{}, (_Scheduler&&)__sched); |
| } |
| |
| friend constexpr bool tag_invoke(forwarding_query_t, schedule_t) |
| { |
| 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((_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((_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 |
| { |
| __coro::suspend_always initial_suspend() noexcept |
| { |
| return {}; |
| } |
| |
| [[noreturn]] __coro::suspend_always final_suspend() noexcept |
| { |
| 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) {} |
| |
| __coro::coroutine_handle<> unhandled_stopped() noexcept |
| { |
| set_stopped((_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(); |
| } |
| |
| stdexec::__t<__operation<_ReceiverId>> get_return_object() noexcept |
| { |
| return stdexec::__t<__operation<_ReceiverId>>{ |
| __coro::coroutine_handle<__t>::from_promise(*this)}; |
| } |
| |
| template <class _Awaitable> |
| _Awaitable&& await_transform(_Awaitable&& __awaitable) noexcept |
| { |
| return (_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, (_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((_Ts&&)__as...); }; |
| |
| struct __awaiter |
| { |
| decltype(__fn) __fn_; |
| |
| static constexpr bool await_ready() noexcept |
| { |
| 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 __operation_t<_Receiver> |
| __co_impl(_Awaitable __awaitable, _Receiver __rcvr) |
| { |
| 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 (_Awaitable&&) __awaitable, |
| __co_call(set_value, (_Receiver&&)__rcvr)); |
| else |
| co_await __co_call(set_value, (_Receiver&&)__rcvr, |
| co_await (_Awaitable&&) __awaitable); |
| } |
| catch (...) |
| { |
| __eptr = std::current_exception(); |
| } |
| co_await __co_call(set_error, (_Receiver&&)__rcvr, |
| (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>> |
| __operation_t<_Receiver> operator()(_Awaitable&& __awaitable, |
| _Receiver __rcvr) const |
| { |
| return __co_impl((_Awaitable&&)__awaitable, (_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 bool __check_signatures() |
| { |
| 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, (_Sender&&)__sndr, __env), |
| (_Receiver&&)__rcvr); |
| } |
| else if constexpr (__connectable_with_co_await<_Sender, _Receiver>) |
| { |
| return __connect_awaitable( // |
| transform_sender(__domain, (_Sender&&)__sndr, __env), |
| (_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, (_Sender&&)__sndr, __env), |
| (_Receiver&&)__rcvr); |
| } |
| } |
| |
| friend constexpr bool tag_invoke(forwarding_query_t, connect_t) noexcept |
| { |
| 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((_Sender&&)__sndr, (_Receiver&&)__rcvr); |
| }; |
| |
| template <class _Tag, class... _Args> |
| _Tag __tag_of_sig_(_Tag (*)(_Args...)); |
| template <class _Sig> |
| using __tag_of_sig_t = decltype(stdexec::__tag_of_sig_((_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>((_Us&&)__us...); |
| __self.__continuation_.resume(); |
| } |
| catch (...) |
| { |
| set_error((__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>((_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((_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 env_of_t<_Promise&> tag_invoke(get_env_t, |
| const __t& __self) noexcept |
| { |
| 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<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 |
| { |
| bool await_ready() const noexcept |
| { |
| return false; |
| } |
| |
| _Value await_resume() |
| { |
| 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 (_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((_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 |
| { |
| __unspecified get_return_object() noexcept; |
| __unspecified initial_suspend() noexcept; |
| __unspecified final_suspend() noexcept; |
| void unhandled_exception() noexcept; |
| void return_void() noexcept; |
| __coro::coroutine_handle<> unhandled_stopped() noexcept; |
| }; |
| |
| 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); |
| } |
| else if constexpr (__awaitable<_Tp, __unspecified>) |
| { // NOT __awaitable<_Tp, _Promise> !! |
| return static_cast < _Tp && (*)() 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 |
| { |
| return static_cast < _Tp && (*)() 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, (_Tp&&)__t, __promise); |
| } |
| else if constexpr (__awaitable<_Tp, __unspecified>) |
| { // NOT __awaitable<_Tp, _Promise> !! |
| return (_Tp&&)__t; |
| } |
| else if constexpr (__awaitable_sender<_Tp, _Promise>) |
| { |
| auto __hcoro = |
| __coro::coroutine_handle<_Promise>::from_promise(__promise); |
| return __sender_awaitable_t<_Promise, _Tp>{(_Tp&&)__t, __hcoro}; |
| } |
| else |
| { |
| return (_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. |
| } |
| |
| __coro::coroutine_handle<> handle() const noexcept |
| { |
| return __coro_; |
| } |
| |
| __coro::coroutine_handle<> unhandled_stopped() const noexcept |
| { |
| 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} |
| {} |
| |
| __coro::coroutine_handle<_Promise> handle() const noexcept |
| { |
| return __coro::coroutine_handle<_Promise>::from_address( |
| __continuation_.handle().address()); |
| } |
| |
| __coro::coroutine_handle<> unhandled_stopped() const noexcept |
| { |
| 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; |
| } |
| |
| __continuation_handle<> continuation() const noexcept |
| { |
| return __continuation_; |
| } |
| |
| __coro::coroutine_handle<> unhandled_stopped() noexcept |
| { |
| 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((_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); }; |
| }; |
| } |
| |
| 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((_Receiver&&)__self.__opref_().__rcvr_, (_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 receiever 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_((_Receiver&&)__rcvr), |
| __op_state_(connect((_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, (_Sender&&)__sndr, (_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>>{ |
| (_Sender&&)__sndr, (_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 forward_progress_guarantee |
| tag_invoke(get_forward_progress_guarantee_t, __scheduler) noexcept |
| { |
| return forward_progress_guarantee::weakly_parallel; |
| } |
| |
| bool operator==(const __scheduler&) const noexcept = 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((_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 const _Env& tag_invoke(get_env_t, const __t& __self) noexcept |
| { |
| // 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, (_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, (_Sender&&)__sndr, |
| (_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((_Sender&&)__sndr, __detached_receiver_t<_Env>{(_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{(_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{(_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((__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 empty_env tag_invoke(get_env_t, const __as_receiver&) noexcept |
| { |
| 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{}, (_Scheduler&&)__sched, (_Fun&&)__fun); |
| } |
| else |
| { |
| auto __domain = query_or(get_domain, __sched, default_domain()); |
| stdexec::apply_sender(__domain, *this, |
| schedule((_Scheduler&&)__sched), |
| (_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((_Sender&&)__sndr, __as_receiver<_Fun>{(_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; |
| } |
| |
| 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 ((_T1&&)__t1_)(((_T0&&)__t0_)((_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_((_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 ((_Closure&&)__clsur)((_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 {{}, (_T0&&)__t0, (_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 ((_Fun&&)__fun_)((_Sender&&)__sndr, (_As&&)__as...); |
| }, |
| __as_); |
| } |
| |
| template <sender _Sender> |
| requires __callable<const _Fun&, _Sender, const _As&...> |
| STDEXEC_ATTRIBUTE((host, device)) |
| __call_result_t<const _Fun&, _Sender, const _As&...> |
| operator()(_Sender&& __sndr) const& // |
| noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>) |
| { |
| return __apply( |
| [&__sndr, this](const _As&... __as) |
| -> __call_result_t<const _Fun&, _Sender, const _As&...> { |
| return __fun_((_Sender&&)__sndr, __as...); |
| }, |
| __as_); |
| } |
| }; |
| } // namespace __closure |
| |
| using __closure::__binder_back; |
| |
| namespace __adaptors |
| { |
| // 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)) |
| __copy_cvref_t<_Up&&, _Tp> __c_cast(_Up&& u) noexcept |
| 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>)(_Up&&)u; |
| } |
| |
| 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; |
| empty_env tag_invoke(get_env_t, __receiver) noexcept; |
| } // 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_((_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 (_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 _DISPATCH_MEMBER(_TAG) \ |
| template <class _Self, class... _Ts> \ |
| STDEXEC_ATTRIBUTE((host, device, always_inline)) \ |
| static auto __call_##_TAG(_Self&& __self, _Ts&&... __ts) noexcept \ |
| -> decltype(((_Self&&)__self)._TAG((_Ts&&)__ts...)) \ |
| { \ |
| static_assert(noexcept(((_Self&&)__self)._TAG((_Ts&&)__ts...))); \ |
| return ((_Self&&)__self)._TAG((_Ts&&)__ts...); \ |
| } /**/ |
| #define _CALL_MEMBER(_TAG, ...) __call_##_TAG(__VA_ARGS__) |
| |
| #if STDEXEC_CLANG() |
| // Only clang gets this right. |
| #define _MISSING_MEMBER(_Dp, _TAG) requires { typename _Dp::_TAG; } |
| #define _DEFINE_MEMBER(_TAG) _DISPATCH_MEMBER(_TAG) using _TAG = void |
| #else |
| #define _MISSING_MEMBER(_Dp, _TAG) (__missing_##_TAG<_Dp>()) |
| #define _DEFINE_MEMBER(_TAG) \ |
| template <class _Dp> \ |
| static constexpr bool __missing_##_TAG() noexcept \ |
| { \ |
| return requires { requires bool(int(_Dp::_TAG)); }; \ |
| } \ |
| _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; |
| _DEFINE_MEMBER(set_value); |
| _DEFINE_MEMBER(set_error); |
| _DEFINE_MEMBER(set_stopped); |
| _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 __base_t<_Dp> __get_base(_Dp&& __self) noexcept |
| { |
| if constexpr (__has_base) |
| { |
| return __c_cast<receiver_adaptor>((_Dp&&)__self).base(); |
| } |
| else |
| { |
| return ((_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(_CALL_MEMBER(set_value, (_Derived&&)__self, |
| (_As&&)__as...))> |
| { |
| static_assert(noexcept( |
| _CALL_MEMBER(set_value, (_Derived&&)__self, (_As&&)__as...))); |
| _CALL_MEMBER(set_value, (_Derived&&)__self, (_As&&)__as...); |
| } |
| |
| template <same_as<set_value_t> _SetValue, class _Dp = _Derived, |
| class... _As> |
| requires _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((_Dp&&)__self), (_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(_CALL_MEMBER(set_error, (_Derived&&)__self, |
| (_Error&&)__err))> |
| { |
| static_assert(noexcept( |
| _CALL_MEMBER(set_error, (_Derived&&)__self, (_Error&&)__err))); |
| _CALL_MEMBER(set_error, (_Derived&&)__self, (_Error&&)__err); |
| } |
| |
| template <same_as<set_error_t> _SetError, class _Error, |
| class _Dp = _Derived> |
| requires _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((_Derived&&)__self), (_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(_CALL_MEMBER(set_stopped, (_Dp&&)__self))> |
| { |
| static_assert(noexcept(_CALL_MEMBER(set_stopped, (_Derived&&)__self))); |
| _CALL_MEMBER(set_stopped, (_Derived&&)__self); |
| } |
| |
| template <same_as<set_stopped_t> _SetStopped, class _Dp = _Derived> |
| requires _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((_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(_CALL_MEMBER(get_env, (const _Dp&)__self)) |
| { |
| static_assert(noexcept(_CALL_MEMBER(get_env, __self))); |
| return _CALL_MEMBER(get_env, __self); |
| } |
| |
| template <same_as<get_env_t> _GetEnv, class _Dp = _Derived> |
| requires _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((_Fun&&)__fun, (_As&&)__as...); |
| set_value((_Receiver&&)__rcvr); |
| } |
| else |
| { |
| set_value((_Receiver&&)__rcvr, |
| __invoke((_Fun&&)__fun, (_As&&)__as...)); |
| } |
| } |
| else |
| { |
| try |
| { |
| stdexec::__set_value_invoke<true>((_Receiver&&)__rcvr, |
| (_Fun&&)__fun, (_As&&)__as...); |
| } |
| catch (...) |
| { |
| set_error((_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 |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<then_t>((_Fun&&)__fun, (_Sender&&)__sndr)); |
| } |
| |
| template <__movable_value _Fun> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<then_t, _Fun> operator()(_Fun __fun) const |
| { |
| return {{}, {}, {(_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), |
| (_Args&&)__args...); |
| } |
| else |
| { |
| _Tag()(std::move(__rcvr), (_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 |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<upon_error_t>((_Fun&&)__fun, (_Sender&&)__sndr)); |
| } |
| |
| template <__movable_value _Fun> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<upon_error_t, _Fun> operator()(_Fun __fun) const |
| { |
| return {{}, {}, {(_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), |
| (_Args&&)__args...); |
| } |
| else |
| { |
| _Tag()(std::move(__rcvr), (_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 |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<upon_stopped_t>((_Fun&&)__fun, (_Sender&&)__sndr)); |
| } |
| |
| template <__movable_value _Fun> |
| requires __callable<_Fun> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<upon_stopped_t, _Fun> |
| operator()(_Fun __fun) const |
| { |
| return {{}, {}, {(_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), |
| (_Args&&)__args...); |
| } |
| else |
| { |
| _Tag()(std::move(__rcvr), (_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 |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<bulk_t>(__data{__shape, (_Fun&&)__fun}, |
| (_Sender&&)__sndr)); |
| } |
| |
| template <integral _Shape, class _Fun> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<bulk_t, _Shape, _Fun> operator()(_Shape __shape, |
| _Fun __fun) const |
| { |
| return {{}, {}, {(_Shape&&)__shape, (_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), (_Args&&)__args...); |
| } |
| else |
| { |
| try |
| { |
| for (__shape_t __i{}; __i != __state.__shape_; ++__i) |
| { |
| __state.__fun_(__i, __args...); |
| } |
| _Tag()(std::move(__rcvr), (_Args&&)__args...); |
| } |
| catch (...) |
| { |
| set_error(std::move(__rcvr), std::current_exception()); |
| } |
| } |
| } |
| else |
| { |
| _Tag()(std::move(__rcvr), (_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(std::move(__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((_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 |
| { |
| __local_state* 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, |
| (_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 const __env_t<_Env>& tag_invoke(get_env_t, |
| const __t& __self) noexcept |
| { |
| 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), |
| (_Env&&)__env)), |
| __op_state2_(connect((_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); |
| __local_state_base* __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>{(_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((_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 |
| { |
| auto __domain = __get_late_domain(__sndr, __env); |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<split_t>((_Env&&)__env, (_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((_Sender&&)__sndr, |
| [&]<class _Env, class _Child>( |
| __ignore, _Env&& __env, _Child&& __child) { |
| auto __state = |
| __make_intrusive<__shared_state<_Child, __decay_t<_Env>>>( |
| (_Child&&)__child, (_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; |
| __data& operator=(__data&&) noexcept = 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 |
| { |
| if constexpr (sender_expr_for<_Sender, __ensure_started_t>) |
| { |
| return (_Sender&&)__sndr; |
| } |
| else |
| { |
| auto __domain = __get_late_domain(__sndr, __env); |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<ensure_started_t>((_Env&&)__env, |
| (_Sender&&)__sndr)); |
| } |
| STDEXEC_UNREACHABLE(); |
| } |
| |
| 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((_Sender&&)__sndr, |
| [&]<class _Env, class _Child>( |
| __ignore, _Env&& __env, _Child&& __child) { |
| auto __state = |
| __make_intrusive<__shared_state<_Child, __decay_t<_Env>>>( |
| (_Child&&)__child, (_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_; |
| |
| _Receiver&& base() && noexcept |
| { |
| return static_cast<_Receiver&&>(__op_state_->*_ReceiverPtr); |
| } |
| |
| __env_t get_env() const noexcept |
| { |
| 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()((_Receiver&&)__self.__rcvr_, (_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)); |
| } |
| } |
| STDEXEC_UNREACHABLE(); |
| }; |
| } |
| |
| 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>>((_Fun&&)__fun, |
| (_Child&&)__child); |
| } |
| else |
| { |
| static_assert(!same_as<_Domain, __none_such>); |
| return __make_sexpr<__let_t<_Set, _Domain>>((_Fun&&)__fun, |
| (_Child&&)__child); |
| } |
| } |
| STDEXEC_UNREACHABLE(); |
| }; |
| } |
| |
| 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...>; |
| |
| decltype(auto) __get_result_receiver(_Receiver&& __rcvr) |
| { |
| if constexpr (__unknown_context<_Sched>) |
| { |
| return (_Receiver&&)__rcvr; |
| } |
| else |
| { |
| return __receiver_with_sched{(_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 |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<__let_t<_Set>>((_Fun&&)__fun, (_Sender&&)__sndr)); |
| } |
| |
| template <class _Fun> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<__let_t, _Fun> operator()(_Fun __fun) const |
| { |
| return {{}, {}, {(_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 decltype(auto) transform_env(_Sender&& __sndr, const _Env& __env) |
| { |
| return __sexpr_apply((_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 decltype(auto) transform_sender(_Sender&& __sndr, const _Env& __env) |
| { |
| return __sexpr_apply((_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((_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...>>( |
| (_As&&)__as...); |
| auto __sndr2 = __apply(std::move(__state.__fun_), __args); |
| auto __rcvr2 = __state.__get_result_receiver((_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, (_As&&)__as...); |
| } |
| else |
| { |
| _Tag()((_Receiver&&)__rcvr, (_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>(__(), (_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( |
| (_Receiver&&)__rcvr, |
| std::optional<__t<_State>>{(_Args&&)__args...}); |
| } |
| catch (...) |
| { |
| stdexec::set_error((_Receiver&&)__rcvr, |
| std::current_exception()); |
| } |
| } |
| else if constexpr (same_as<_Tag, set_error_t>) |
| { |
| stdexec::set_error((_Receiver&&)__rcvr, (_Args&&)__args...); |
| } |
| else |
| { |
| stdexec::set_value((_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 (_Sender&&)__sndr | |
| let_stopped( |
| [__err2 = (_Error&&)__err]() mutable // |
| noexcept(std::is_nothrow_move_constructible_v<_Error>) { |
| return just_error((_Error&&)__err2); |
| }); |
| } |
| |
| template <__movable_value _Error> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| auto operator()(_Error __err) const |
| -> __binder_back<stopped_as_error_t, _Error> |
| { |
| return {{}, {}, {(_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 = ((__t*)__p)->__rcvr_; |
| try |
| { |
| if (get_stop_token(get_env(__rcvr)).stop_requested()) |
| { |
| set_stopped((_Receiver&&)__rcvr); |
| } |
| else |
| { |
| set_value((_Receiver&&)__rcvr); |
| } |
| } |
| catch (...) |
| { |
| set_error((_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_{(_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; |
| bool operator==(const __scheduler&) const noexcept = 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 __operation<_Receiver> |
| tag_invoke(connect_t, const __schedule_task& __self, |
| _Receiver __rcvr) |
| { |
| return __self.__connect_((_Receiver&&)__rcvr); |
| } |
| |
| template <class _Receiver> |
| __operation<_Receiver> __connect_(_Receiver&& __rcvr) const |
| { |
| return {&__loop_->__head_, __loop_, (_Receiver&&)__rcvr}; |
| } |
| |
| struct __env |
| { |
| run_loop* __loop_; |
| |
| template <class _CPO> |
| friend __scheduler tag_invoke(get_completion_scheduler_t<_CPO>, |
| const __env& __self) noexcept |
| { |
| return __self.__loop_->get_scheduler(); |
| } |
| }; |
| |
| friend __env tag_invoke(get_env_t, |
| const __schedule_task& __self) noexcept |
| { |
| 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 __schedule_task tag_invoke(schedule_t, |
| const __scheduler& __self) noexcept |
| { |
| return __self.__schedule(); |
| } |
| |
| friend stdexec::forward_progress_guarantee |
| tag_invoke(get_forward_progress_guarantee_t, |
| const __scheduler&) noexcept |
| { |
| return stdexec::forward_progress_guarantee::parallel; |
| } |
| |
| // BUGBUG NOT TO SPEC |
| friend bool tag_invoke(execute_may_block_caller_t, |
| const __scheduler&) noexcept |
| { |
| return false; |
| } |
| |
| __schedule_task __schedule() const noexcept |
| { |
| return __schedule_task{__loop_}; |
| } |
| |
| run_loop* __loop_; |
| }; |
| |
| __scheduler get_scheduler() noexcept |
| { |
| return __scheduler{this}; |
| } |
| |
| void run(); |
| |
| void finish(); |
| |
| private: |
| void __push_back_(__task* __task); |
| __task* __pop_front_(); |
| |
| 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((_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 __task* run_loop::__pop_front_() |
| { |
| 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} |
| {} |
| |
| _Receiver&& base() && noexcept |
| { |
| return std::move(__state_->__receiver()); |
| } |
| |
| const _Receiver& base() const& noexcept |
| { |
| 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()), (_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 |
| { |
| using _Env = __t<__environ<__id<__decay_t<_Scheduler>>>>; |
| auto __env = _Env{{(_Scheduler&&)__sched}}; |
| auto __domain = query_or(get_domain, __sched, default_domain()); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<schedule_from_t>(std::move(__env), (_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(), (_Args&&)__args...); |
| } |
| else |
| { |
| try |
| { |
| __state.__data_.template emplace<__async_result>( |
| _Tag(), (_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 |
| { |
| auto __domain = __get_early_domain(__sndr); |
| using _Env = __t<__environ<__id<__decay_t<_Scheduler>>>>; |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<transfer_t>(_Env{{(_Scheduler&&)__sched}}, |
| (_Sender&&)__sndr)); |
| } |
| |
| template <scheduler _Scheduler> |
| STDEXEC_ATTRIBUTE((always_inline)) // |
| __binder_back<transfer_t, __decay_t<_Scheduler>> |
| operator()(_Scheduler&& __sched) const |
| { |
| return {{}, {}, {(_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), (_Child&&)__child); |
| }; |
| } |
| |
| template <class _Sender, class _Env> |
| static auto transform_sender(_Sender&& __sndr, const _Env& __env) |
| { |
| return __sexpr_apply((_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, (_Ts&&)__ts...); |
| }; |
| } |
| |
| template <class _Env> |
| auto __make_transform_fn(const _Env& __env) |
| { |
| return [&]<class _Scheduler, class... _Values>(_Scheduler&& __sched, |
| _Values&&... __vals) { |
| return transfer(just((_Values&&)__vals...), (_Scheduler&&)__sched); |
| }; |
| } |
| |
| template <class _Env> |
| auto __transform_sender_fn(const _Env& __env) |
| { |
| return [&]<class _Data>(__ignore, _Data&& __data) { |
| return __apply(__make_transform_fn(__env), (_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 |
| { |
| auto __domain = query_or(get_domain, __sched, default_domain()); |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<transfer_just_t>(std::tuple{ |
| (_Scheduler&&)__sched, (_Values&&)__vals...})); |
| } |
| |
| template <class _Sender, class _Env> |
| static auto transform_sender(_Sender&& __sndr, const _Env& __env) |
| { |
| return __sexpr_apply((_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)...), |
| (_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, (_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((_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((_Env&&)__env, get_domain)); |
| using _Env2 = decltype(__env2); |
| |
| struct __env_t : _Env2 |
| {}; |
| |
| return __env_t{(_Env2&&)__env2}; |
| } |
| |
| template <class _Ty, class = __name_of<__decay_t<_Ty>>> |
| struct __always |
| { |
| _Ty __val_; |
| |
| _Ty operator()() noexcept |
| { |
| 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 |
| { |
| auto __domain = query_or(get_domain, __sched, default_domain()); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<on_t>((_Scheduler&&)__sched, (_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((_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((_Env&&)__env)); |
| } |
| |
| template <class _Sender, class _Env> |
| static auto transform_sender(_Sender&& __sndr, const _Env&) |
| { |
| return __sexpr_apply((_Sender&&)__sndr, |
| []<class _Data, class _Child>( |
| __ignore, _Data&& __data, _Child&& __child) { |
| return let_value(schedule(__data), |
| __detail::__always{(_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 |
| { |
| 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( |
| (_Receiver&&)__rcvr, |
| __variant_t{std::tuple<_Args&&...>{(_Args&&)__args...}}); |
| } |
| catch (...) |
| { |
| set_error((_Receiver&&)__rcvr, std::current_exception()); |
| } |
| } |
| else |
| { |
| _Tag()((_Receiver&&)__rcvr, (_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), |
| (_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> |
| std::tuple<_Ty&...> operator()(_Ty&... __vals) noexcept |
| { |
| 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()((_Receiver&&)__rcvr, (_Ts&&)__ts...); |
| } |
| }; |
| } |
| |
| template <class _Receiver, class _ValuesTuple> |
| void __set_values(_Receiver& __rcvr, _ValuesTuple& __values) noexcept |
| { |
| __apply( |
| [&](auto&... __opt_vals) noexcept -> void { |
| __apply(__complete_fn(set_value, __rcvr), // |
| std::tuple_cat(__apply(__tie_fn{}, *__opt_vals)...)); |
| }, |
| __values); |
| } |
| |
| template <class _Env, class _Sender> |
| using __values_opt_tuple_t = // |
| __value_types_of_t<_Sender, __env_t<_Env>, |
| __mcompose<__q<std::optional>, __q<__decayed_tuple>>, |
| __q<__msingle>>; |
| |
| 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<std::tuple>>, |
| __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((_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...)>; |
| |
| // TODO: improve diagnostic when senders have different domains |
| template <sender... _Senders> |
| requires __domain::__has_common_domain<_Senders...> |
| auto operator()(_Senders&&... __sndrs) const |
| { |
| auto __domain = __domain::__common_domain_t<_Senders...>(); |
| return stdexec::transform_sender( |
| __domain, __make_sexpr<when_all_t>(__(), (_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); |
| } |
| STDEXEC_UNREACHABLE(); |
| }; |
| |
| 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((_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>>( |
| (_Error&&)__err); |
| } |
| else |
| { |
| try |
| { |
| __state.__errors_.template emplace<__decay_t<_Error>>( |
| (_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, (_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 = std::get<__v<_Index>>(__state.__values_); |
| static_assert( |
| same_as<decltype(*__opt_values), |
| __decayed_tuple<_Args...>&>, |
| "One of the senders in this when_all() is fibbing about what types it sends"); |
| if constexpr ((__nothrow_decay_copyable<_Args> && ...)) |
| { |
| __opt_values.emplace((_Args&&)__args...); |
| } |
| else |
| { |
| try |
| { |
| __opt_values.emplace((_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 |
| { |
| auto __domain = __domain::__common_domain_t<_Senders...>(); |
| return stdexec::transform_sender(__domain, |
| __make_sexpr<when_all_with_variant_t>( |
| __(), (_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( |
| (_Sender&&)__sndr, |
| [&]<class... _Child>(__ignore, __ignore, _Child&&... __child) { |
| return when_all_t()(into_variant((_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); |
| } |
| STDEXEC_UNREACHABLE(); |
| }; |
| }; |
| |
| 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 |
| { |
| 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{(_Scheduler&&)__sched}, (_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( |
| (_Sender&&)__sndr, |
| [&]<class _Data, class... _Child>(__ignore, _Data&& __data, |
| _Child&&... __child) { |
| return transfer(when_all_t()((_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 |
| { |
| 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{{(_Scheduler&&)__sched}}, (_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( |
| (_Sender&&)__sndr, |
| [&]<class _Data, class... _Child>(__ignore, _Data&& __data, |
| _Child&&... __child) { |
| return transfer_when_all_t()( |
| get_completion_scheduler<set_value_t>((_Data&&)__data), |
| into_variant((_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 |
| { |
| 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 |
| { |
| 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); |
| } |
| |
| 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)); |
| } |
| |
| inline auto get_allocator_t::operator()() const noexcept |
| { |
| return read(get_allocator); |
| } |
| |
| 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 |
| { |
| // BUGBUG __get_early_domain, or get_domain(__sched), or ...? |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<on_t>((_Scheduler&&)__sched, (_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((_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((_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( |
| (_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{(_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 _Scheduler tag_invoke(get_scheduler_t, |
| const __with_sched& __self) noexcept |
| { |
| 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 |
| { |
| auto __domain = __get_early_domain(__sndr); |
| return stdexec::transform_sender( |
| __domain, |
| __make_sexpr<continue_on_t>( |
| __continue_on_data{(_Scheduler&&)__sched, (_Closure&&)__clsur}, |
| (_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 {{}, {}, {(_Scheduler&&)__sched, (_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((_Sender&&)__sndr, |
| [&]<class _Data, class _Child>( |
| __ignore, _Data&& __data, _Child&& __child) { |
| auto&& [__sched, __clsur] = (_Data&&)__data; |
| using _Closure = decltype(__clsur); |
| return __write( |
| transfer(((_Closure&&)__clsur)(transfer( |
| __write((_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 |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // [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>((_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((_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>((_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((_Error&&)__err); |
| } |
| |
| friend void tag_invoke(set_stopped_t __d, __t&& __rcvr) noexcept |
| { |
| __rcvr.__state_->__data_.template emplace<3>(__d); |
| __rcvr.__loop_->finish(); |
| } |
| |
| friend __env tag_invoke(get_env_t, const __t& __rcvr) noexcept |
| { |
| 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>(); |
| } |
| STDEXEC_UNREACHABLE(); |
| } |
| |
| 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, (_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((_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, (_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((_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> |
| 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() |