blob: 36c5013c14ace33a65c0d148f2dcb91efcc23b1b [file] [log] [blame]
/*
* 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()