blob: 1a815531a73b417e4a8fcd1eb017dcdd3f6a753d [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/__execution_fwd.hpp"
#include "__detail/__intrusive_ptr.hpp"
#include "__detail/__meta.hpp"
#include "__detail/__scope.hpp"
#include "concepts.hpp"
#include "coroutine.hpp"
#include "functional.hpp"
#include "stop_token.hpp"
#include <atomic>
#include <cassert>
#include <concepts>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <optional>
#include <stdexcept>
#include <system_error>
#include <tuple>
#include <type_traits>
#include <variant>
#ifdef __EDG__
#pragma diagnostic push
#pragma diag_suppress 1302
#pragma diag_suppress 497
#pragma diag_suppress type_qualifiers_ignored_on_reference
#endif
#ifndef STDEXEC_DISABLE_R5_DEPRECATION_WARNINGS
#define STDEXEC_R5_SENDER_DEPRECATION_WARNING \
[[deprecated( \
"Deprecated sender type detected. " \
"Please give the type a nested `is_sender` type alias, or " \
"specialize stdexec::enable_sender<your-sender-type> to be `true`. " \
"To suppress this deprecation warning, define `STDEXEC_DISABLE_R5_DEPRECATIONS`.")]]
#define STDEXEC_R5_RECEIVER_DEPRECATION_WARNING \
[[deprecated( \
"Deprecated receiver type detected. " \
"Please give the type a nested `is_receiver` type alias, or " \
"specialize stdexec::enable_receiver<your-receiver-type> to be `true`." \
"To suppress this deprecation warning, define `STDEXEC_DISABLE_R5_DEPRECATIONS`.")]]
#define STDEXEC_R5_DEPENDENT_COMPLETION_SIGNATURES_DEPRECATION_WARNING \
[[deprecated( \
"The `dependent_completion_signatures<>` type is deprecated. There is " \
"no need to define a customization of `get_completion_signatures` for " \
"sender types whose completions are dependent on the receiver's environment. " \
"Give your sender type a nested `is_sender` type alias instead, " \
"specialize stdexec::enable_sender<your-sender-type> to be `true`. " \
"To suppress this deprecation warning, define `STDEXEC_DISABLE_R5_DEPRECATIONS`.")]]
#define STDEXEC_R5_NO_ENV_DEPRECATION_WARNING \
[[deprecated( \
"The `no_env` type is deprecated. The stdexec library no longer needs to use " \
"it to probe a type for satisfaction of the `sender` concept. " \
"Give your sender type a nested `is_sender` type alias instead, or " \
"specialize stdexec::enable_sender<your-sender-type> to be `true`. " \
"To suppress this deprecation warning, define `STDEXEC_DISABLE_R5_DEPRECATIONS`.")]]
#else
#define STDEXEC_R5_SENDER_DEPRECATION_WARNING
#define STDEXEC_R5_RECEIVER_DEPRECATION_WARNING
#define STDEXEC_R5_DEPENDENT_COMPLETION_SIGNATURES_DEPRECATION_WARNING
#define STDEXEC_R5_NO_ENV_DEPRECATION_WARNING
#endif
#define STDEXEC_LEGACY_R5_CONCEPTS() 1
STDEXEC_PRAGMA_PUSH()
STDEXEC_PRAGMA_IGNORE("-Wundefined-inline")
STDEXEC_PRAGMA_IGNORE("-Wundefined-internal")
namespace stdexec
{
// [exec.queries.queryable]
template <class T>
concept queryable = destructible<T>;
template <class Tag>
struct __query
{
template <class Sig>
static inline constexpr Tag (*signature)(Sig) = nullptr;
};
//////////////////////////////////////////////////////////////////////////////////////////////////
// [exec.queries]
namespace __queries
{
struct forwarding_query_t
{
template <class _Query>
constexpr bool operator()(_Query __query) const noexcept
{
if constexpr (tag_invocable<forwarding_query_t, _Query>)
{
return tag_invoke(*this, (_Query&&)__query);
}
else if constexpr (std::derived_from<_Query, forwarding_query_t>)
{
return true;
}
else
{
return false;
}
}
};
struct execute_may_block_caller_t : __query<execute_may_block_caller_t>
{
template <class _Tp>
requires tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>>
constexpr bool operator()(_Tp&& __t) const noexcept
{
static_assert(
same_as<bool, tag_invoke_result_t<execute_may_block_caller_t,
__cref_t<_Tp>>>);
static_assert(
nothrow_tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>>);
return tag_invoke(execute_may_block_caller_t{}, std::as_const(__t));
}
constexpr bool operator()(auto&&) const noexcept
{
return true;
}
};
struct get_forward_progress_guarantee_t :
__query<get_forward_progress_guarantee_t>
{
template <class _Tp>
requires tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>>
constexpr auto operator()(_Tp&& __t) const noexcept(
nothrow_tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>>)
-> tag_invoke_result_t<get_forward_progress_guarantee_t, __cref_t<_Tp>>
{
return tag_invoke(get_forward_progress_guarantee_t{},
std::as_const(__t));
}
constexpr stdexec::forward_progress_guarantee
operator()(auto&&) const noexcept
{
return stdexec::forward_progress_guarantee::weakly_parallel;
}
};
struct __has_algorithm_customizations_t :
__query<__has_algorithm_customizations_t>
{
template <class _Tp>
using __result_t =
tag_invoke_result_t<__has_algorithm_customizations_t, __cref_t<_Tp>>;
template <class _Tp>
requires tag_invocable<__has_algorithm_customizations_t, __cref_t<_Tp>>
constexpr __result_t<_Tp> operator()(_Tp&&) const
noexcept(noexcept(__result_t<_Tp>{}))
{
using _Boolean = tag_invoke_result_t<__has_algorithm_customizations_t,
__cref_t<_Tp>>;
static_assert(_Boolean{}
? true
: true); // must be contextually convertible to bool
return _Boolean{};
}
constexpr std::false_type operator()(auto&&) const noexcept
{
return {};
}
};
// TODO: implement allocator concept
template <class _T0>
concept __allocator = true;
struct get_scheduler_t : __query<get_scheduler_t>
{
friend constexpr bool tag_invoke(forwarding_query_t,
const get_scheduler_t&) noexcept
{
return true;
}
template <class _Env>
requires tag_invocable<get_scheduler_t, const _Env&>
auto operator()(const _Env& __env) const noexcept
-> tag_invoke_result_t<get_scheduler_t, const _Env&>;
auto operator()() const noexcept;
};
struct get_delegatee_scheduler_t : __query<get_delegatee_scheduler_t>
{
friend constexpr bool tag_invoke(forwarding_query_t,
const get_delegatee_scheduler_t&) noexcept
{
return true;
}
template <class _Env>
requires tag_invocable<get_delegatee_scheduler_t, const _Env&>
auto operator()(const _Env& __t) const noexcept
-> tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>;
auto operator()() const noexcept;
};
struct get_allocator_t : __query<get_allocator_t>
{
friend constexpr bool tag_invoke(forwarding_query_t,
const get_allocator_t&) noexcept
{
return true;
}
template <class _Env>
requires tag_invocable<get_allocator_t, const _Env&>
auto operator()(const _Env& __env) const noexcept
-> tag_invoke_result_t<get_allocator_t, const _Env&>
{
static_assert(nothrow_tag_invocable<get_allocator_t, const _Env&>);
static_assert(
__allocator<tag_invoke_result_t<get_allocator_t, const _Env&>>);
return tag_invoke(get_allocator_t{}, __env);
}
auto operator()() const noexcept;
};
struct get_stop_token_t : __query<get_stop_token_t>
{
friend constexpr bool tag_invoke(forwarding_query_t,
const get_stop_token_t&) noexcept
{
return true;
}
template <class _Env>
never_stop_token operator()(const _Env&) const noexcept
{
return {};
}
template <class _Env>
requires tag_invocable<get_stop_token_t, const _Env&>
auto operator()(const _Env& __env) const noexcept
-> tag_invoke_result_t<get_stop_token_t, const _Env&>
{
static_assert(nothrow_tag_invocable<get_stop_token_t, const _Env&>);
static_assert(stoppable_token<
tag_invoke_result_t<get_stop_token_t, const _Env&>>);
return tag_invoke(get_stop_token_t{}, __env);
}
auto operator()() const noexcept;
};
template <class _Queryable, class _CPO>
concept __has_completion_scheduler_for =
queryable<_Queryable> && //
tag_invocable<get_completion_scheduler_t<_CPO>, const _Queryable&>;
template <__completion_tag _CPO>
struct get_completion_scheduler_t : __query<get_completion_scheduler_t<_CPO>>
{
friend constexpr bool
tag_invoke(forwarding_query_t,
const get_completion_scheduler_t<_CPO>&) noexcept
{
return true;
}
template <__has_completion_scheduler_for<_CPO> _Queryable>
auto operator()(const _Queryable& __queryable) const noexcept
-> tag_invoke_result_t<get_completion_scheduler_t<_CPO>,
const _Queryable&>;
};
} // namespace __queries
using __queries::__has_algorithm_customizations_t;
using __queries::execute_may_block_caller_t;
using __queries::forwarding_query_t;
using __queries::get_allocator_t;
using __queries::get_completion_scheduler_t;
using __queries::get_delegatee_scheduler_t;
using __queries::get_forward_progress_guarantee_t;
using __queries::get_scheduler_t;
using __queries::get_stop_token_t;
inline constexpr forwarding_query_t forwarding_query{};
inline constexpr execute_may_block_caller_t execute_may_block_caller{};
inline constexpr __has_algorithm_customizations_t
__has_algorithm_customizations{};
inline constexpr get_forward_progress_guarantee_t
get_forward_progress_guarantee{};
inline constexpr get_scheduler_t get_scheduler{};
inline constexpr get_delegatee_scheduler_t get_delegatee_scheduler{};
inline constexpr get_allocator_t get_allocator{};
inline constexpr get_stop_token_t get_stop_token{};
#if !STDEXEC_GCC() || defined(__OPTIMIZE__)
template <__completion_tag _CPO>
inline constexpr get_completion_scheduler_t<_CPO> get_completion_scheduler{};
#else
template <>
inline constexpr get_completion_scheduler_t<set_value_t>
get_completion_scheduler<set_value_t>{};
template <>
inline constexpr get_completion_scheduler_t<set_error_t>
get_completion_scheduler<set_error_t>{};
template <>
inline constexpr get_completion_scheduler_t<set_stopped_t>
get_completion_scheduler<set_stopped_t>{};
#endif
template <class _Tag>
concept __forwarding_query = forwarding_query(_Tag{});
/////////////////////////////////////////////////////////////////////////////
// env_of
namespace __env
{
struct no_env
{
using __t = no_env;
using __id = no_env;
template <class _Tag, same_as<no_env> _Self, class... _Ts>
friend void tag_invoke(_Tag, _Self, _Ts&&...) = delete;
};
struct empty_env
{
using __t = empty_env;
using __id = empty_env;
};
template <class _Tag>
struct __deleted
{};
template <__nothrow_move_constructible _Fun>
struct __env_fn
{
using __t = __env_fn;
using __id = __env_fn;
STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
template <class _Tag>
requires __callable<const _Fun&, _Tag>
friend auto tag_invoke(_Tag, const __env_fn& __self) //
noexcept(__nothrow_callable<const _Fun&, _Tag>)
-> __call_result_t<const _Fun&, _Tag>
{
return __self.__fun_(_Tag());
}
};
template <class _Fun>
__env_fn(_Fun) -> __env_fn<_Fun>;
template <__nothrow_move_constructible _Env>
struct __env_fwd
{
using __t = __env_fwd;
using __id = __env_fwd;
STDEXEC_NO_UNIQUE_ADDRESS _Env __env_;
template <__forwarding_query _Tag>
requires tag_invocable<_Tag, const _Env&>
friend auto tag_invoke(_Tag, const __env_fwd& __self) //
noexcept(nothrow_tag_invocable<_Tag, const _Env&>)
-> tag_invoke_result_t<_Tag, const _Env&>
{
return _Tag()(__self.__env_);
}
};
template <__nothrow_move_constructible _Env,
__nothrow_move_constructible _Base = empty_env>
struct __env_join : __env_fwd<_Base>
{
using __t = __env_join;
using __id = __env_join;
STDEXEC_NO_UNIQUE_ADDRESS _Env __env_;
const _Base& base() const noexcept
{
return this->__env_fwd<_Base>::__env_;
}
template <class _Tag>
requires tag_invocable<_Tag, const _Env&>
friend auto tag_invoke(_Tag, const __env_join& __self) //
noexcept(nothrow_tag_invocable<_Tag, const _Env&>)
-> tag_invoke_result_t<_Tag, const _Env&>
{
return _Tag()(__self.__env_);
}
};
template <class _Tag, class _Base>
struct __env_join<__env_fn<__deleted<_Tag>>, _Base> : __env_fwd<_Base>
{
using __t = __env_join;
using __id = __env_join;
STDEXEC_NO_UNIQUE_ADDRESS __env_fn<__deleted<_Tag>> __env_;
friend void tag_invoke(_Tag, const __env_join&) noexcept = delete;
};
template <class _Env>
_Env __join_env(_Env&& __env) noexcept
{
return (_Env&&)__env;
}
template <class _Env, class _Base>
__env_join<_Env, _Base> __join_env(_Env&& __env, _Base&& __base) noexcept
{
static_assert(!same_as<__decay_t<_Env>, no_env>);
return {{{(_Base&&)__base}}, (_Env&&)__env};
}
template <class _Base>
_Base __join_env(empty_env, _Base&& __base) noexcept
{
return (_Base&&)__base;
}
template <class _Env>
_Env __join_env(_Env&& __env, empty_env) noexcept
{
static_assert(!same_as<__decay_t<_Env>, no_env>);
return (_Env&&)__env;
}
inline empty_env __join_env(empty_env, empty_env) noexcept
{
return {};
}
template <class _Env>
no_env __join_env(_Env&&, no_env) noexcept
requires true
{
static_assert(!same_as<__decay_t<_Env>, no_env>);
return {};
}
template <class _Env0, class _Env1, class _Env2, class... _Envs>
auto __join_env(_Env0&& __env0, _Env1&& __env1, _Env2&& __env2,
_Envs&&... __envs) noexcept
{
return __env::__join_env(
(_Env0&&)__env0,
__env::__join_env(
(_Env1&&)__env1,
__env::__join_env((_Env2&&)__env2, (_Envs&&)__envs...)));
}
template <class... _Envs>
using __env_join_t = decltype(__env::__join_env(__declval<_Envs>()...));
// To be kept in sync with the promise type used in __connect_awaitable
template <class _Env>
struct __env_promise
{
template <class _Ty>
_Ty&& await_transform(_Ty&& __value) noexcept
{
return (_Ty&&)__value;
}
template <class _Ty>
requires tag_invocable<as_awaitable_t, _Ty, __env_promise&>
auto await_transform(_Ty&& __value) //
noexcept(nothrow_tag_invocable<as_awaitable_t, _Ty, __env_promise&>)
-> tag_invoke_result_t<as_awaitable_t, _Ty, __env_promise&>
{
return tag_invoke(as_awaitable, (_Ty&&)__value, *this);
}
friend auto tag_invoke(get_env_t, const __env_promise&) noexcept
-> const _Env&;
};
template <class _Tag, __nothrow_move_constructible _Value>
constexpr auto __with_(_Tag, _Value __val) noexcept
{
return __env_fn{
[__val = std::move(__val)](_Tag) noexcept(
__nothrow_copy_constructible<_Value>) { return __val; }};
}
template <class _Tag>
__env_fn<__deleted<_Tag>> __with_(_Tag) noexcept
{
return {};
}
template <class... _Ts>
using __with = decltype(__env::__with_(__declval<_Ts>()...));
// For making an environment from key/value pairs and optionally
// another environment.
struct __make_env_t
{
template <__nothrow_move_constructible _Base,
__nothrow_move_constructible _Env>
auto operator()(_Base&& __base, _Env&& __env) const noexcept
-> __env_join_t<_Env, _Base>
{
return stdexec::__env::__join_env((_Env&&)__env, (_Base&&)__base);
}
template <__nothrow_move_constructible _Env>
_Env operator()(_Env&& __env) const noexcept
{
return (_Env&&)__env;
}
};
// For getting an evaluation environment from a receiver
struct get_env_t
{
template <class _EnvProvider>
requires tag_invocable<get_env_t, const _EnvProvider&>
constexpr auto operator()(const _EnvProvider& __with_env) const noexcept
-> tag_invoke_result_t<get_env_t, const _EnvProvider&>
{
static_assert(
queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>);
static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>);
return tag_invoke(*this, __with_env);
}
// NOT TO SPEC: The overload below checks the non-standard
// enable_sender to determine whether to provide backwards
// compatible behavior for R5 version sender types. When we
// deprecate R5 support, we can bring this overload in line with
// P2300R7.
template <class _EnvProvider>
constexpr decltype(auto)
operator()(const _EnvProvider & __with_env) const noexcept
{
if constexpr (!enable_sender<_EnvProvider>)
{
return __with_env;
}
else
{
return empty_env{};
}
}
};
} // namespace __env
using __env::empty_env;
using __env::no_env;
using __empty_env
[[deprecated("Please use stdexec::empty_env now.")]] = empty_env;
using __env::__env_promise;
using __env::__with;
using __env::__with_;
using no_env_promise = __env_promise<no_env>;
inline constexpr __env::__make_env_t __make_env{};
inline constexpr __env::get_env_t get_env{};
template <class... _Ts>
using __make_env_t = decltype(__make_env(__declval<_Ts>()...));
#if STDEXEC_LEGACY_R5_CONCEPTS()
using __default_env = no_env;
#else
using __default_env = empty_env;
#endif
template <class _EnvProvider>
concept environment_provider = //
requires(_EnvProvider& __ep) {
{
get_env(std::as_const(__ep))
} -> queryable;
// NOT TO SPEC: Remove the following line when we deprecate all
// R5 entities.
{
get_env(std::as_const(__ep))
} -> __none_of<no_env, void>;
};
/////////////////////////////////////////////////////////////////////////////
// [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_DETAIL_CUDACC_HOST_DEVICE //
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_DETAIL_CUDACC_HOST_DEVICE //
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_DETAIL_CUDACC_HOST_DEVICE //
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{};
/////////////////////////////////////////////////////////////////////////////
// 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
// BUGBUG not to spec!
struct __dependent
{
#if !STDEXEC_STD_NO_COROUTINES_
bool await_ready();
template <class _Env>
void await_suspend(__coro::coroutine_handle<__env_promise<_Env>>);
__dependent await_resume();
#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 <same_as<no_env>>
using dependent_completion_signatures = __compl_sigs::__dependent;
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 _Ty>
concept __is_completion_signatures =
__is_instance_of<_Ty, completion_signatures>;
template <class...>
auto __concat_completion_signatures_impl() //
-> dependent_completion_signatures<no_env>;
template <__is_completion_signatures... _Completions>
auto __concat_completion_signatures_impl()
-> __minvoke<__mconcat<__munique<__q<completion_signatures>>>,
_Completions...>;
template <class... _Completions>
using __concat_completion_signatures_impl_t = //
decltype(__concat_completion_signatures_impl<_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...>>;
template <class _Completions, class _Env>
inline constexpr bool __expecting_completion_signatures = false;
template <class... _Sigs, class _Env>
inline constexpr bool
__expecting_completion_signatures<completion_signatures<_Sigs...>, _Env> =
true;
template <>
inline constexpr bool __expecting_completion_signatures<
dependent_completion_signatures<no_env>, no_env> = true;
template <class _Completions, class _Env>
concept __valid_completion_signatures =
__expecting_completion_signatures<_Completions, _Env>;
/////////////////////////////////////////////////////////////////////////////
// [execution.receivers]
template <class _Receiver>
struct _WITH_RECEIVER_
{};
template <class _Sig>
struct _MISSING_COMPLETION_SIGNAL_
{};
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)));
/////////////////////////////////////////////////////////////////////////////
// [execution.receivers]
struct __receiver_base
{};
template <class _Receiver>
concept __enable_receiver = //
requires { typename _Receiver::is_receiver; } ||
STDEXEC_IS_BASE_OF(__receiver_base, _Receiver);
template <class _Receiver>
inline constexpr bool enable_receiver =
__enable_receiver<_Receiver>; // NOT TO SPEC
// NOT TO SPEC:
// As we upgrade the receiver related entities from R5 to R7,
// we allow types that do not yet satisfy enable_receiver to
// still satisfy the receiver concept if the type provides an
// explicit get_env. All R5 receivers provided an explicit get_env,
// so this is backwards compatible.
template <class _Receiver>
concept __receiver_r5_or_r7 = //
enable_receiver<_Receiver> //
|| tag_invocable<get_env_t, _Receiver>;
template <class _Receiver>
concept __receiver = //
// Nested requirement here is to make this an atomic
// constraint
requires { requires __receiver_r5_or_r7<__decay_t<_Receiver>>; };
template <class _Receiver>
concept receiver = __receiver<_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 = __make_env_t<_Env, __with<__is_debug_env_t, bool>>;
template <class _Env>
concept __is_debug_env = tag_invocable<__debug::__is_debug_env_t, _Env>;
struct __completion_signatures
{};
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>);
template <class... _Sigs>
struct __valid_completions
{
template <derived_from<__valid_completions> _Self, class _Tag,
class... _Args>
requires __one_of<_Tag (*)(_Args&&...), _Sigs...>
STDEXEC_DETAIL_CUDACC_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 is_receiver = void;
};
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 is_receiver = void;
template <same_as<get_env_t> _Tag>
STDEXEC_DETAIL_CUDACC_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_DETAIL_CUDACC_HOST_DEVICE //
void
_ATTENTION_() noexcept
{}
template <class _Sig>
struct __invalid_completion
{
struct __t
{
template <class _CvrefSenderId, class _Env, class... _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_<_Sender>>;
__debug::_ATTENTION_<_What>();
}
};
};
template <__completion_tag _Tag, class... _Args>
STDEXEC_DETAIL_CUDACC_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> && !same_as<_Env, no_env>)
{
if (sizeof(_Sender) == ~0)
{ // 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> && !same_as<_Env, no_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;
/////////////////////////////////////////////////////////////////////////////
// [execution.sndtraits]
namespace __get_completion_signatures
{
template <class _Sender, class _Env>
concept __r7_style_sender = same_as<_Env, no_env> &&
enable_sender<__decay_t<_Sender>>;
template <class _Sender, class _Env>
concept __with_tag_invoke =
__valid<tag_invoke_result_t, get_completion_signatures_t, _Sender, _Env>;
template <class _Sender, class...>
using __member_alias_t = typename __decay_t<_Sender>::completion_signatures;
template <class _Sender>
concept __with_member_alias = __valid<__member_alias_t, _Sender>;
struct get_completion_signatures_t
{
template <class _Sender, class _Env>
static auto __impl()
{
static_assert(STDEXEC_LEGACY_R5_CONCEPTS() || !same_as<_Env, no_env>);
static_assert(sizeof(_Sender),
"Incomplete type used with get_completion_signatures");
static_assert(sizeof(_Env),
"Incomplete type used with get_completion_signatures");
if constexpr (__with_tag_invoke<_Sender, _Env>)
{
using _Result =
tag_invoke_result_t<get_completion_signatures_t, _Sender, _Env>;
if constexpr (same_as<_Env, no_env> && __merror<_Result>)
{
return (dependent_completion_signatures<no_env>(*)()) nullptr;
}
else
{
return (_Result(*)()) nullptr;
}
}
else if constexpr (__with_member_alias<_Sender>)
{
return (__member_alias_t<_Sender, _Env>(*)()) nullptr;
}
else if constexpr (__awaitable<_Sender, __env_promise<_Env>>)
{
using _Result = __await_result_t<_Sender, __env_promise<_Env>>;
if constexpr (same_as<_Result,
dependent_completion_signatures<no_env>>)
{
return (dependent_completion_signatures<no_env>(*)()) nullptr;
}
else
{
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 STDEXEC_LEGACY_R5_CONCEPTS()
if constexpr (__r7_style_sender<_Sender, _Env>)
{
return (dependent_completion_signatures<no_env>(*)()) nullptr;
}
else
#endif
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
{
return (void (*)()) nullptr;
}
}
template <class _Sender, class _Env = __default_env>
requires(__with_tag_invoke<_Sender, _Env> || //
__with_member_alias<_Sender> || //
__awaitable<_Sender, __env_promise<_Env>> || //
#if STDEXEC_LEGACY_R5_CONCEPTS() //
__r7_style_sender<_Sender, _Env> || //
#endif //
__is_debug_env<_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]
template <class _Sender>
concept __enable_sender = //
requires { typename _Sender::is_sender; } || //
__awaitable<_Sender, __env_promise<empty_env>>;
template <class _Sender>
inline constexpr bool enable_sender = __enable_sender<_Sender>;
// NOT TO SPEC (YET)
#if !STDEXEC_LEGACY_R5_CONCEPTS()
// Here is the R7 sender concepts, not yet enabled.
template <class _Sender>
concept sender = //
enable_sender<__decay_t<_Sender>> && //
environment_provider<__cref_t<_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<_Env>;
};
template <class _Sender, class _Env = empty_env>
requires sender_in<_Sender, _Env>
using completion_signatures_of_t = __completion_signatures_of_t<_Sender, _Env>;
#else
template <class _Sender, class _Env = no_env>
concept __sender = //
requires(_Sender&& __sndr, _Env&& __env) {
get_completion_signatures((_Sender&&)__sndr, (_Env&&)__env);
} && //
__valid_completion_signatures<__completion_signatures_of_t<_Sender, _Env>,
_Env>;
template <class _Sender, class _Env = no_env>
concept sender =
// NOT TO SPEC
// The sender related concepts are temporarily "in flight" being
// upgraded from P2300R5 to the get_env / enable_sender aware version
// in P2300R7.
__sender<_Sender> && //
__sender<_Sender, _Env> && //
environment_provider<__cref_t<_Sender>> && //
move_constructible<__decay_t<_Sender>> && //
constructible_from<__decay_t<_Sender>, _Sender>;
template <class _Sender, class _Env = empty_env>
concept sender_in = //
__sender<_Sender, _Env> && //
sender<_Sender, _Env>;
// __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 _WithEnv = __completion_signatures_of_t<_Sender, _Env>;
using _WithoutEnv = __completion_signatures_of_t<_Sender, no_env>;
static_assert(__one_of<_WithoutEnv, _WithEnv,
dependent_completion_signatures<no_env>>);
stdexec::__debug_sender<_WithEnv>((_Sender&&)__sndr, __env);
return _WithEnv{};
}
template <class _Sender, class _Env = no_env>
requires sender_in<_Sender, _Env>
using completion_signatures_of_t =
decltype(stdexec::__checked_completion_signatures(__declval<_Sender>(),
__declval<_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 = __default_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 = __default_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 = __default_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 = __default_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 = __default_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 = __default_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 = __default_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 = __default_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 = __default_env>
requires __valid<__count_of, _Tag, _Sender, _Env>
inline constexpr bool __sends = (__v<__count_of<_Tag, _Sender, _Env>> != 0);
template <class _Sender, class _Env = __default_env>
requires __valid<__count_of, set_stopped_t, _Sender, _Env>
inline constexpr bool sends_stopped = __sends<set_stopped_t, _Sender, _Env>;
template <class _Sender, class _Env = __default_env>
using __single_sender_value_t =
__value_types_of_t<_Sender, _Env, __msingle_or<void>, __q<__msingle>>;
template <class _Sender, class _Env = __default_env>
using __single_value_variant_sender_t =
value_types_of_t<_Sender, _Env, __types, __msingle>;
template <class _Sender, class _Env = __default_env>
concept __single_typed_sender = sender_in<_Sender, _Env> &&
__valid<__single_sender_value_t, _Sender, _Env>;
template <class _Sender, class _Env = __default_env>
concept __single_value_variant_sender =
sender_in<_Sender, _Env> &&
__valid<__single_value_variant_sender_t, _Sender, _Env>;
template <class... Errs>
using __nofail = __mbool<sizeof...(Errs) == 0>;
template <class _Sender, class _Env = __default_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 <__is_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 __valid<__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 <bool>
struct __make_compl_sigs
{
template <class _Sender, class _Env, class _Sigs, class _SetVal,
class _SetErr, class _SetStp>
using __f = __compl_sigs_t<_Sender, _Env, _Sigs, _SetVal, _SetErr, _SetStp>;
};
template <>
struct __make_compl_sigs<true>
{
template <class _Sender, class _Env, class _Sigs, class _SetVal,
class _SetErr, class _SetStp>
using __f = //
__msuccess_or_t<
__compl_sigs_t<_Sender, _Env, _Sigs, _SetVal, _SetErr, _SetStp>,
dependent_completion_signatures<_Env>>;
};
template < //
class _Sender, //
class _Env = __default_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 = //
__minvoke<__make_compl_sigs<same_as<_Env, no_env>>, _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 = __default_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 = __default_env, //
__valid_completion_signatures<_Env> _Sigs = completion_signatures<>, //
template <class...> class _SetValue = __compl_sigs::__default_set_value, //
template <class> class _SetError = __compl_sigs::__default_set_error, //
__valid_completion_signatures<_Env> _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_DETAIL_CUDACC_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,
const 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&>
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_)
__coro_.destroy();
}
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&& __await) noexcept
{
return (_Awaitable&&)__await;
}
template <class _Awaitable>
requires tag_invocable<as_awaitable_t, _Awaitable, __t&>
auto await_transform(_Awaitable&& __await) //
noexcept(nothrow_tag_invocable<as_awaitable_t, _Awaitable, __t&>)
-> tag_invoke_result_t<as_awaitable_t, _Awaitable, __t&>
{
return tag_invoke(as_awaitable, (_Awaitable&&)__await, *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>
static __operation_t<_Receiver> __co_impl(_Awaitable __await,
_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&&) __await,
__co_call(set_value, (_Receiver&&)__rcvr));
else
co_await __co_call(set_value, (_Receiver&&)__rcvr,
co_await (_Awaitable&&) __await);
}
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&& __await,
_Receiver __rcvr) const
{
return __co_impl((_Awaitable&&)__await, (_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 _Tp>
STDEXEC_R5_SENDER_DEPRECATION_WARNING //
void
_PLEASE_UPDATE_YOUR_SENDER_TYPE()
{}
template <class _Tp>
STDEXEC_R5_RECEIVER_DEPRECATION_WARNING //
void
_PLEASE_UPDATE_YOUR_RECEIVER_TYPE()
{}
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>;
struct connect_t
{
template <class _Sender, class _Receiver>
static constexpr auto __select_impl() noexcept
{
// Report that 2300R5-style senders and receivers are deprecated:
if constexpr (!enable_sender<__decay_t<_Sender>>)
_PLEASE_UPDATE_YOUR_SENDER_TYPE<__decay_t<_Sender>>();
if constexpr (!enable_receiver<__decay_t<_Receiver>>)
_PLEASE_UPDATE_YOUR_RECEIVER_TYPE<__decay_t<_Receiver>>();
if constexpr (__connectable_with_tag_invoke<_Sender, _Receiver>)
{
using _Result = tag_invoke_result_t<connect_t, _Sender, _Receiver>;
constexpr bool _Nothrow =
nothrow_tag_invocable<connect_t, _Sender, _Receiver>;
return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
}
else if constexpr (__callable<__connect_awaitable_t, _Sender,
_Receiver>)
{
using _Result =
__call_result_t<__connect_awaitable_t, _Sender, _Receiver>;
return static_cast<_Result (*)()>(nullptr);
}
else
{
return static_cast<__debug::__debug_operation (*)() noexcept>(
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> ||
__callable<__connect_awaitable_t, _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>>
{
if constexpr (__connectable_with_tag_invoke<_Sender, _Receiver>)
{
static_assert(
operation_state<
tag_invoke_result_t<connect_t, _Sender, _Receiver>>,
"stdexec::connect(sender, receiver) must return a type that "
"satisfies the operation_state concept");
return tag_invoke(connect_t{}, (_Sender&&)__sndr,
(_Receiver&&)__rcvr);
}
else if constexpr (__callable<__connect_awaitable_t, _Sender,
_Receiver>)
{
return __connect_awaitable((_Sender&&)__sndr, (_Receiver&&)__rcvr);
}
else
{
// This should generate an instantiate backtrace that contains
// useful debugging information.
using __tag_invoke::tag_invoke;
tag_invoke(*this, (_Sender&&)__sndr, (_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<_Sender,
env_of_t<_Receiver>> && // NOT TO SPEC: for simpler diagnostics
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 = __default_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>>>;
template <class _Fun, class _CPO, class _Sender, class... _As>
concept __tag_invocable_with_completion_scheduler =
__has_completion_scheduler<_Sender, _CPO> &&
tag_invocable<_Fun, __completion_scheduler_for<_Sender, _CPO>, _Sender,
_As...>;
#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 is_receiver = void;
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());
}
};
};
template <class _Sender, class _Promise>
using __value_t =
__decay_t<__single_sender_value_t<_Sender, env_of_t<_Promise&>>>;
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 =
__single_typed_sender<_Sender, env_of_t<_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
/////////////////////////////////////////////////////////////////////////////
// NOT TO SPEC: __submit
namespace __submit_
{
template <class _ReceiverId>
struct __operation_base;
template <class _ReceiverId>
struct __operation_base
{
using _Receiver = __t<_ReceiverId>;
_Receiver __rcvr_;
using __delete_fn_t = void(__operation_base<_ReceiverId>*) noexcept;
__delete_fn_t* __delete_;
};
template <class _ReceiverId>
struct __receiver
{
using _Receiver = stdexec::__t<_ReceiverId>;
struct __t
{
using is_receiver = void;
using __id = __receiver;
__operation_base<_ReceiverId>* __op_state_;
// 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, __t&& __self,
_As&&... __as) noexcept(__nothrow_callable<_Tag, _Receiver, _As...>)
{
// Delete the state as cleanup:
auto __g = __scope_guard{__self.__op_state_->__delete_,
__self.__op_state_};
return __tag((_Receiver&&)__self.__op_state_->__rcvr_,
(_As&&)__as...);
}
// Forward all receiever queries.
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
-> env_of_t<_Receiver>
{
return get_env(__self.__op_state_->__rcvr_);
}
};
};
template <class _ReceiverId>
using __receiver_t = __t<__receiver<_ReceiverId>>;
template <class _SenderId, class _ReceiverId>
struct __operation : __operation_base<_ReceiverId>
{
using _Sender = stdexec::__t<_SenderId>;
using _Receiver = stdexec::__t<_ReceiverId>;
connect_result_t<_Sender, __receiver_t<_ReceiverId>> __op_state_;
template <__decays_to<_Receiver> _CvrefReceiver>
__operation(_Sender&& __sndr, _CvrefReceiver&& __rcvr) :
__operation_base<_ReceiverId>{
(_CvrefReceiver&&)__rcvr,
[](__operation_base<_ReceiverId>* __self) noexcept {
delete static_cast<__operation*>(__self);
}},
__op_state_(connect((_Sender&&)__sndr, __receiver_t<_ReceiverId>{this}))
{}
};
struct __submit_t
{
template <receiver _Receiver, sender_to<_Receiver> _Sender>
void operator()(_Sender&& __sndr, _Receiver __rcvr) const noexcept(false)
{
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 __scheduler
{
using __t = __scheduler;
using __id = __scheduler;
template <class _Receiver>
struct __op : __immovable
{
_Receiver __recv_;
friend void tag_invoke(start_t, __op& __self) noexcept
{
set_value((_Receiver&&)__self.__recv_);
}
};
struct __sender
{
using __t = __sender;
using __id = __sender;
using is_sender = void;
using completion_signatures =
stdexec::completion_signatures<set_value_t()>;
template <receiver_of<completion_signatures> _Receiver>
friend __op<_Receiver> tag_invoke(connect_t, __sender, _Receiver __rcvr)
{
return {{}, (_Receiver&&)__rcvr};
}
friend auto tag_invoke(get_env_t, __sender) noexcept
{
return __env::__env_fn{
[](get_completion_scheduler_t<set_value_t>) noexcept {
return __scheduler{};
}};
}
};
friend __sender tag_invoke(schedule_t, __scheduler)
{
return {};
}
bool operator==(const __scheduler&) const noexcept = default;
};
} // namespace __inln
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.consumer.start_detached]
namespace __start_detached
{
template <class _EnvId>
struct __detached_receiver
{
using _Env = stdexec::__t<_EnvId>;
struct __t
{
using is_receiver = void;
using __id = __detached_receiver;
STDEXEC_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>
using __detached_receiver_t = __t<__detached_receiver<__id<__decay_t<_Env>>>>;
struct start_detached_t;
// When looking for user-defined customizations of start_detached, these
// are the signatures to test against, in order:
using _Sender = __0;
using _Env = __1;
using __cust_sigs = //
__types<tag_invoke_t(start_detached_t, _Sender),
tag_invoke_t(start_detached_t, _Sender, _Env),
tag_invoke_t(start_detached_t, get_scheduler_t(_Env&), _Sender),
tag_invoke_t(start_detached_t, get_scheduler_t(_Env&), _Sender,
_Env)>;
template <class _Sender, class _Env>
inline constexpr bool __is_start_detached_customized =
__minvocable<__which<__cust_sigs>, _Sender, _Env>;
struct __submit_detached
{
template <class _Sender, class _Env>
void operator()(_Sender&& __sndr, _Env&& __env) const
{
__submit((_Sender&&)__sndr, __detached_receiver_t<_Env>{(_Env&&)__env});
}
};
template <class _Sender, class _Env>
using __dispatcher_for =
__make_dispatcher<__cust_sigs, __mconst<__submit_detached>, _Sender, _Env>;
struct start_detached_t
{
template <sender _Sender, class _Env = empty_env>
requires sender_to<_Sender, __detached_receiver_t<_Env>> ||
__is_start_detached_customized<_Sender, _Env>
void operator()(_Sender&& __sndr, _Env&& __env = _Env{}) const noexcept(
__nothrow_callable<__dispatcher_for<_Sender, _Env>, _Sender, _Env>)
{
using _Dispatcher = __dispatcher_for<_Sender, _Env>;
// The selected implementation should return void
static_assert(
same_as<void, __call_result_t<_Dispatcher, _Sender, _Env>>);
_Dispatcher{}((_Sender&&)__sndr, (_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 _Tag, class... _Ts>
using __completion_signatures_ = completion_signatures<_Tag(_Ts...)>;
template <class _ReceiverId, class _Tag, class... _Ts>
struct __operation
{
using _Receiver = stdexec::__t<_ReceiverId>;
struct __t : __immovable
{
using __id = __operation;
std::tuple<_Ts...> __vals_;
_Receiver __rcvr_;
friend void tag_invoke(start_t, __t& __op_state) noexcept
{
std::apply(
[&__op_state](_Ts&... __ts) {
_Tag{}((_Receiver&&)__op_state.__rcvr_, (_Ts&&)__ts...);
},
__op_state.__vals_);
}
};
};
template <class _Tag, class... _Ts>
struct __basic_sender
{
template <class _Receiver>
using __operation_t =
stdexec::__t<__operation<stdexec::__id<_Receiver>, _Tag, _Ts...>>;
struct __t
{
using __id = __basic_sender;
using is_sender = void;
using completion_signatures = __completion_signatures_<_Tag, _Ts...>;
std::tuple<_Ts...> __vals_;
template <receiver_of<completion_signatures> _Receiver>
requires(copy_constructible<_Ts> && ...)
friend auto tag_invoke(connect_t, const __t& __sndr,
_Receiver __rcvr) //
noexcept((std::is_nothrow_copy_constructible_v<_Ts> && ...))
-> __operation_t<_Receiver>
{
return {{}, __sndr.__vals_, (_Receiver&&)__rcvr};
}
template <receiver_of<completion_signatures> _Receiver>
friend auto tag_invoke(connect_t, __t&& __sndr, _Receiver __rcvr) //
noexcept((std::is_nothrow_move_constructible_v<_Ts> && ...))
-> __operation_t<_Receiver>
{
return {{}, ((__t&&)__sndr).__vals_, (_Receiver&&)__rcvr};
}
friend empty_env tag_invoke(get_env_t, const __t&) noexcept
{
return {};
}
};
};
template <class... _Values>
struct __sender
{
using __base = stdexec::__t<__basic_sender<set_value_t, _Values...>>;
struct __t : __base
{
using __id = __sender;
};
};
template <class _Error>
struct __error_sender
{
using __base = stdexec::__t<__basic_sender<set_error_t, _Error>>;
struct __t : __base
{
using __id = __error_sender;
};
};
struct __stopped_sender : __t<__basic_sender<set_stopped_t>>
{
using __id = __stopped_sender;
using __t = __stopped_sender;
};
inline constexpr struct __just_t
{
template <__movable_value... _Ts>
STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
__t<__sender<__decay_t<_Ts>...>>
operator()(_Ts&&... __ts) const
noexcept((__nothrow_constructible_from<__decay_t<_Ts>, _Ts> && ...))
{
return {{{(_Ts&&)__ts...}}};
}
} just{};
inline constexpr struct __just_error_t
{
template <__movable_value _Error>
STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
__t<__error_sender<__decay_t<_Error>>>
operator()(_Error&& __err) const
noexcept(__nothrow_constructible_from<__decay_t<_Error>, _Error>)
{
return {{{(_Error&&)__err}}};
}
} just_error{};
inline constexpr struct __just_stopped_t
{
STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
__stopped_sender
operator()() const noexcept
{
return {{}};
}
} just_stopped{};
} // namespace __just
using __just::just;
using __just::just_error;
using __just::just_stopped;
/////////////////////////////////////////////////////////////////////////////
// [execution.execute]
namespace __execute_
{
template <class _Fun>
struct __as_receiver
{
using is_receiver = void;
_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(noexcept(__submit(schedule((_Scheduler&&)__sched),
__as_receiver<_Fun>{(_Fun&&)__fun})))
{
(void)__submit(schedule((_Scheduler&&)__sched),
__as_receiver<_Fun>{(_Fun&&)__fun});
}
template <scheduler _Scheduler, class _Fun>
requires __callable<_Fun&> && move_constructible<_Fun> &&
tag_invocable<execute_t, _Scheduler, _Fun>
void operator()(_Scheduler&& __sched, _Fun __fun) const
noexcept(nothrow_tag_invocable<execute_t, _Scheduler, _Fun>)
{
(void)tag_invoke(execute_t{}, (_Scheduler&&)__sched, (_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_NO_UNIQUE_ADDRESS _T0 __t0_;
STDEXEC_NO_UNIQUE_ADDRESS _T1 __t1_;
template <sender _Sender>
requires __callable<_T0, _Sender> &&
__callable<_T1, __call_result_t<_T0, _Sender>>
__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>>
__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>
__call_result_t<_Closure, _Sender> operator|(_Sender&& __sndr,
_Closure&& __clsur)
{
return ((_Closure&&)__clsur)((_Sender&&)__sndr);
}
template <__sender_adaptor_closure _T0, __sender_adaptor_closure _T1>
__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_NO_UNIQUE_ADDRESS _Fun __fun_;
std::tuple<_As...> __as_;
template <sender _Sender>
requires __callable<_Fun, _Sender, _As...>
STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
__call_result_t<_Fun, _Sender, _As...>
operator()(_Sender&& __sndr) && noexcept(
__nothrow_callable<_Fun, _Sender, _As...>)
{
return std::apply(
[&__sndr, this](_As&... __as) {
return ((_Fun&&)__fun_)((_Sender&&)__sndr, (_As&&)__as...);
},
__as_);
}
template <sender _Sender>
requires __callable<const _Fun&, _Sender, const _As&...>
STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
__call_result_t<const _Fun&, _Sender, const _As&...>
operator()(_Sender&& __sndr) const& //
noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>)
{
return std::apply(
[&__sndr, this](const _As&... __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_DETAIL_CUDACC_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
{};
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
{
struct __t
{
template <class _T1>
requires constructible_from<_Base, _T1>
explicit __t(_T1&& __base) : __base_((_T1&&)__base)
{}
private:
STDEXEC_NO_UNIQUE_ADDRESS _Base __base_;
protected:
STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
_Base&
base() & noexcept
{
return __base_;
}
STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
const _Base&
base() const& noexcept
{
return __base_;
}
STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
_Base&&
base() && noexcept
{
return (_Base&&)__base_;
}
};
};
template <derived_from<__no::__nope> _Base>
struct __adaptor<_Base>
{
struct __t : __no::__nope
{};
};
template <class _Base>
using __adaptor_base = typename __adaptor<_Base>::__t;
// 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_DETAIL_CUDACC_HOST_DEVICE 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>
struct receiver_adaptor
{
class __t : __adaptor_base<_Base>, __receiver_base
{
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_DETAIL_CUDACC_HOST_DEVICE //
static __base_t<_Dp>
__get_base(_Dp&& __self) noexcept
{
if constexpr (__has_base)
{
return __c_cast<__t>((_Dp&&)__self).base();
}
else
{
return ((_Dp&&)__self).base();
}
}
template <same_as<set_value_t> _SetValue, class... _As>
STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
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_DETAIL_CUDACC_HOST_DEVICE //
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_DETAIL_CUDACC_HOST_DEVICE //
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_DETAIL_CUDACC_HOST_DEVICE //
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_DETAIL_CUDACC_HOST_DEVICE //
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_DETAIL_CUDACC_HOST_DEVICE //
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_DETAIL_CUDACC_HOST_DEVICE //
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_DETAIL_CUDACC_HOST_DEVICE //
friend auto tag_invoke(_GetEnv, const _Derived& __self) noexcept
-> env_of_t<__base_t<const _Dp&>>
{
return stdexec::get_env(__get_base(__self));
}
public:
__t() = default;
using __adaptor_base<_Base>::__adaptor_base;
using is_receiver = void;
};
};
} // namespace __adaptors
template <__class _Derived, receiver _Base = __adaptors::__not_a_receiver>
using receiver_adaptor =
typename __adaptors::receiver_adaptor<_Derived, _Base>::__t;
template <class _Receiver, class _Fun, class... _As>
concept __receiver_of_invoke_result = //
receiver_of<_Receiver, completion_signatures<
__minvoke<__remove<void, __qf<set_value_t>>,
std::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, std::invoke_result_t<_Fun, _As...>>)
{
std::invoke((_Fun&&)__fun, (_As&&)__as...);
set_value((_Receiver&&)__rcvr);
}
else
{
set_value((_Receiver&&)__rcvr,
std::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."__csz;
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>>,
std::invoke_result_t<_Fun, _Args...>>>;
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.then]
namespace __then
{
template <class _ReceiverId, class _Fun>
struct __receiver
{
using _Receiver = stdexec::__t<_ReceiverId>;
struct __data
{
_Receiver __rcvr_;
STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
};
struct __t
{
using is_receiver = void;
using __id = __receiver;
__data* __op_;
// Customize set_value by invoking the invocable and passing the result
// to the downstream receiver
template <__same_as<set_value_t> _Tag, class... _As>
requires invocable<_Fun, _As...> &&
__receiver_of_invoke_result<_Receiver, _Fun, _As...>
friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept
{
stdexec::__set_value_invoke((_Receiver&&)__self.__op_->__rcvr_,
(_Fun&&)__self.__op_->__fun_,
(_As&&)__as...);
}
template <__one_of<set_error_t, set_stopped_t> _Tag, class... _As>
requires __callable<_Tag, _Receiver, _As...>
friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept
{
__tag((_Receiver&&)__self.__op_->__rcvr_, (_As&&)__as...);
}
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
-> env_of_t<const _Receiver&>
{
return get_env(__self.__op_->__rcvr_);
}
};
};
template <class _Sender, class _ReceiverId, class _Fun>
struct __operation
{
using _Receiver = stdexec::__t<_ReceiverId>;
using __receiver_id = __receiver<_ReceiverId, _Fun>;
using __receiver_t = stdexec::__t<__receiver_id>;
struct __t : __immovable
{
using __id = __operation;
typename __receiver_id::__data __data_;
connect_result_t<_Sender, __receiver_t> __op_;
__t(_Sender&& __sndr, _Receiver __rcvr, _Fun __fun) //
noexcept(__nothrow_decay_copyable<_Receiver> //
&& __nothrow_decay_copyable<_Fun> //
&& __nothrow_connectable<_Sender, __receiver_t>) :
__data_{(_Receiver&&)__rcvr, (_Fun&&)__fun},
__op_(connect((_Sender&&)__sndr, __receiver_t{&__data_}))
{}
friend void tag_invoke(start_t, __t& __self) noexcept
{
start(__self.__op_);
}
};
};
inline constexpr __mstring __then_context =
"In stdexec::then(Sender, Function)..."__csz;
using __on_not_callable = __callable_error<__then_context>;
template <class _SenderId, class _Fun>
struct __sender
{
using _Sender = stdexec::__t<_SenderId>;
template <class _Receiver>
using __receiver = stdexec::__t<__receiver<stdexec::__id<_Receiver>, _Fun>>;
template <class _Self, class _Receiver>
using __operation =
stdexec::__t<__operation<__copy_cvref_t<_Self, _Sender>,
stdexec::__id<_Receiver>, _Fun>>;
struct __t
{
using __id = __sender;
using is_sender = void;
STDEXEC_NO_UNIQUE_ADDRESS _Sender __sndr_;
STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
template <class _Self, class _Env>
using __completion_signatures = //
__try_make_completion_signatures<
__copy_cvref_t<_Self, _Sender>, _Env,
__with_error_invoke_t<set_value_t, _Fun,
__copy_cvref_t<_Self, _Sender>, _Env,
__on_not_callable>,
__mbind_front<
__mtry_catch_q<__set_value_invoke_t, __on_not_callable>,
_Fun>>;
template <__decays_to<__t> _Self, receiver _Receiver>
requires sender_to<__copy_cvref_t<_Self, _Sender>,
__receiver<_Receiver>>
friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) //
noexcept(__nothrow_constructible_from<
__operation<_Self, _Receiver>,
__copy_cvref_t<_Self, _Sender>, _Receiver&&,
__copy_cvref_t<_Self, _Fun>>)
-> __operation<_Self, _Receiver>
{
return {((_Self&&)__self).__sndr_, (_Receiver&&)__rcvr,
((_Self&&)__self).__fun_};
}
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> dependent_completion_signatures<_Env>;
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> __completion_signatures<_Self, _Env>
requires true;
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
-> env_of_t<const _Sender&>
{
return get_env(__self.__sndr_);
}
};
};
struct then_t
{
template <class _Sender, class _Fun>
using __sender = __t<__sender<stdexec::__id<__decay_t<_Sender>>, _Fun>>;
template <sender _Sender, __movable_value _Fun>
requires(!__tag_invocable_with_completion_scheduler<then_t, set_value_t,
_Sender, _Fun>) &&
(!tag_invocable<then_t, _Sender, _Fun>) &&
sender<__sender<_Sender, _Fun>>
__sender<_Sender, _Fun> operator()(_Sender&& __sndr, _Fun __fun) const
{
return __sender<_Sender, _Fun>{(_Sender&&)__sndr, (_Fun&&)__fun};
}
template <sender _Sender, __movable_value _Fun>
requires __tag_invocable_with_completion_scheduler<then_t, set_value_t,
_Sender, _Fun>
sender auto operator()(_Sender&& __sndr, _Fun __fun) const
noexcept(nothrow_tag_invocable<
then_t, __completion_scheduler_for<_Sender, set_value_t>,
_Sender, _Fun>)
{
auto __sched = get_completion_scheduler<set_value_t>(get_env(__sndr));
return tag_invoke(then_t{}, std::move(__sched), (_Sender&&)__sndr,
(_Fun&&)__fun);
}
template <sender _Sender, __movable_value _Fun>
requires(!__tag_invocable_with_completion_scheduler<then_t, set_value_t,
_Sender, _Fun>) &&
tag_invocable<then_t, _Sender, _Fun>
sender auto operator()(_Sender&& __sndr, _Fun __fun) const
noexcept(nothrow_tag_invocable<then_t, _Sender, _Fun>)
{
return tag_invoke(then_t{}, (_Sender&&)__sndr, (_Fun&&)__fun);
}
template <class _Fun>
__binder_back<then_t, _Fun> operator()(_Fun __fun) const
{
return {{}, {}, {(_Fun&&)__fun}};
}
};
} // namespace __then
using __then::then_t;
inline constexpr then_t then{};
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.upon_error]
namespace __upon_error
{
template <class _ReceiverId, class _Fun>
struct __receiver
{
using _Receiver = stdexec::__t<_ReceiverId>;
struct __t
{
using is_receiver = void;
using __id = __receiver;
_Receiver __rcvr_;
STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
template <__one_of<set_value_t, set_stopped_t> _Tag, same_as<__t> _Self,
class... _Args>
requires __callable<_Tag, _Receiver, _Args...>
friend void tag_invoke(_Tag, _Self&& __self, _Args&&... __args) noexcept
{
_Tag{}((_Receiver&&)__self.__rcvr_, (_Args&&)__args...);
}
// Customize set_error by invoking the invocable and passing the result
// to the base class
template <same_as<set_error_t> _Tag, __decays_to<__t> _Self,
class _Error>
requires invocable<_Fun, _Error> &&
__receiver_of_invoke_result<_Receiver, _Fun, _Error>
friend void tag_invoke(_Tag, _Self&& __self, _Error&& __error) noexcept
{
stdexec::__set_value_invoke((_Receiver&&)__self.__rcvr_,
(_Fun&&)__self.__fun_,
(_Error&&)__error);
}
friend env_of_t<_Receiver> tag_invoke(get_env_t,
const __t& __self) noexcept
{
return get_env(__self.__rcvr_);
}
};
};
inline constexpr __mstring __upon_error_context =
"In stdexec::upon_error(Sender, Function)..."__csz;
using __on_not_callable = __callable_error<__upon_error_context>;
template <class _SenderId, class _Fun>
struct __sender
{
using _Sender = stdexec::__t<_SenderId>;
template <class _Receiver>
using __receiver = stdexec::__t<__receiver<__id<_Receiver>, _Fun>>;
struct __t
{
using __id = __sender;
using is_sender = void;
STDEXEC_NO_UNIQUE_ADDRESS _Sender __sndr_;
STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
template <class _Self, class _Env>
using __completion_signatures = //
__try_make_completion_signatures<
__copy_cvref_t<_Self, _Sender>, _Env,
__with_error_invoke_t<set_error_t, _Fun,
__copy_cvref_t<_Self, _Sender>, _Env,
__on_not_callable>,
__q<__compl_sigs::__default_set_value>,
__mbind_front_q<__set_value_invoke_t, _Fun>>;
template <__decays_to<__t> _Self, receiver _Receiver>
requires sender_to<__copy_cvref_t<_Self, _Sender>,
__receiver<_Receiver>>
friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) //
noexcept(__nothrow_connectable<__copy_cvref_t<_Self, _Sender>,
__receiver<_Receiver>>)
-> connect_result_t<__copy_cvref_t<_Self, _Sender>,
__receiver<_Receiver>>
{
return stdexec::connect(
((_Self&&)__self).__sndr_,
__receiver<_Receiver>{(_Receiver&&)__rcvr,
((_Self&&)__self).__fun_});
}
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> dependent_completion_signatures<_Env>;
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> __completion_signatures<_Self, _Env>
requires true;
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
-> env_of_t<const _Sender&>
{
return get_env(__self.__sndr_);
}
};
};
struct upon_error_t
{
template <class _Sender, class _Fun>
using __sender = __t<__sender<stdexec::__id<__decay_t<_Sender>>, _Fun>>;
template <sender _Sender, __movable_value _Fun>
requires __tag_invocable_with_completion_scheduler<
upon_error_t, set_error_t, _Sender, _Fun>
sender auto operator()(_Sender&& __sndr, _Fun __fun) const
noexcept(nothrow_tag_invocable<
upon_error_t, __completion_scheduler_for<_Sender, set_error_t>,
_Sender, _Fun>)
{
auto __sched = get_completion_scheduler<set_error_t>(
get_env(__sndr)); // TODO ADD TEST!
return tag_invoke(upon_error_t{}, std::move(__sched), (_Sender&&)__sndr,
(_Fun&&)__fun);
}
template <sender _Sender, __movable_value _Fun>
requires(!__tag_invocable_with_completion_scheduler<
upon_error_t, set_error_t, _Sender, _Fun>) &&
tag_invocable<upon_error_t, _Sender, _Fun>
sender auto operator()(_Sender&& __sndr, _Fun __fun) const
noexcept(nothrow_tag_invocable<upon_error_t, _Sender, _Fun>)
{
return tag_invoke(upon_error_t{}, (_Sender&&)__sndr, (_Fun&&)__fun);
}
template <sender _Sender, __movable_value _Fun>
requires(!__tag_invocable_with_completion_scheduler<
upon_error_t, set_error_t, _Sender, _Fun>) &&
(!tag_invocable<upon_error_t, _Sender, _Fun>) &&
sender<__sender<_Sender, _Fun>>
__sender<_Sender, _Fun> operator()(_Sender&& __sndr, _Fun __fun) const
{
return __sender<_Sender, _Fun>{(_Sender&&)__sndr, (_Fun&&)__fun};
}
template <class _Fun>
__binder_back<upon_error_t, _Fun> operator()(_Fun __fun) const
{
return {{}, {}, {(_Fun&&)__fun}};
}
};
} // namespace __upon_error
using __upon_error::upon_error_t;
inline constexpr upon_error_t upon_error{};
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.upon_stopped]
namespace __upon_stopped
{
template <class _ReceiverId, class _Fun>
struct __receiver
{
using _Receiver = stdexec::__t<_ReceiverId>;
struct __t
{
using is_receiver = void;
using __id = __receiver;
_Receiver __rcvr_;
STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
template <__one_of<set_value_t, set_error_t> _Tag, same_as<__t> _Self,
class... _Args>
requires __callable<_Tag, _Receiver, _Args...>
friend void tag_invoke(_Tag, _Self&& __self, _Args&&... __args) noexcept
{
_Tag{}((_Receiver&&)__self.__rcvr_, (_Args&&)__args...);
}
template <same_as<set_stopped_t> _Tag>
friend void tag_invoke(_Tag, __t&& __self) noexcept
{
stdexec::__set_value_invoke((_Receiver&&)__self.__rcvr_,
(_Fun&&)__self.__fun_);
}
friend env_of_t<_Receiver> tag_invoke(get_env_t,
const __t& __self) noexcept
{
return get_env(__self.__rcvr_);
}
};
};
inline constexpr __mstring __upon_stopped_context =
"In stdexec::upon_stopped(Sender, Function)..."__csz;
using __on_not_callable = __callable_error<__upon_stopped_context>;
template <class _SenderId, class _Fun>
struct __sender
{
using _Sender = stdexec::__t<_SenderId>;
template <class _Receiver>
using __receiver = stdexec::__t<__receiver<__id<_Receiver>, _Fun>>;
struct __t
{
using __id = __sender;
using is_sender = void;
STDEXEC_NO_UNIQUE_ADDRESS _Sender __sndr_;
STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
template <class _Self, class _Env>
using __completion_signatures = //
__try_make_completion_signatures<
__copy_cvref_t<_Self, _Sender>, _Env,
__with_error_invoke_t<set_stopped_t, _Fun,
__copy_cvref_t<_Self, _Sender>, _Env,
__on_not_callable>,
__q<__compl_sigs::__default_set_value>,
__q<__compl_sigs::__default_set_error>,
__set_value_invoke_t<_Fun>>;
template <__decays_to<__t> _Self, receiver _Receiver>
requires __receiver_of_invoke_result<_Receiver, _Fun> &&
sender_to<__copy_cvref_t<_Self, _Sender>,
__receiver<_Receiver>>
friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) //
noexcept(__nothrow_connectable<_Sender, __receiver<_Receiver>>)
-> connect_result_t<__copy_cvref_t<_Self, _Sender>,
__receiver<_Receiver>>
{
return stdexec::connect(
((_Self&&)__self).__sndr_,
__receiver<_Receiver>{(_Receiver&&)__rcvr,
((_Self&&)__self).__fun_});
}
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> dependent_completion_signatures<_Env>;
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> __completion_signatures<_Self, _Env>
requires true;
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
-> env_of_t<const _Sender&>
{
return get_env(__self.__sndr_);
}
};
};
struct upon_stopped_t
{
template <class _Sender, class _Fun>
using __sender = __t<__sender<__id<__decay_t<_Sender>>, _Fun>>;
template <sender _Sender, __movable_value _Fun>
requires __tag_invocable_with_completion_scheduler<
upon_stopped_t, set_stopped_t, _Sender, _Fun> &&
__callable<_Fun>
sender auto operator()(_Sender&& __sndr, _Fun __fun) const noexcept(
nothrow_tag_invocable<
upon_stopped_t, __completion_scheduler_for<_Sender, set_stopped_t>,
_Sender, _Fun>)
{
auto __sched = get_completion_scheduler<set_stopped_t>(
get_env(__sndr)); // TODO ADD TEST!
return tag_invoke(upon_stopped_t{}, std::move(__sched),
(_Sender&&)__sndr, (_Fun&&)__fun);
}
template <sender _Sender, __movable_value _Fun>
requires(!__tag_invocable_with_completion_scheduler<
upon_stopped_t, set_stopped_t, _Sender, _Fun>) &&
tag_invocable<upon_stopped_t, _Sender, _Fun> && __callable<_Fun>
sender auto operator()(_Sender&& __sndr, _Fun __fun) const
noexcept(nothrow_tag_invocable<upon_stopped_t, _Sender, _Fun>)
{
return tag_invoke(upon_stopped_t{}, (_Sender&&)__sndr, (_Fun&&)__fun);
}
template <sender _Sender, __movable_value _Fun>
requires(!__tag_invocable_with_completion_scheduler<
upon_stopped_t, set_stopped_t, _Sender, _Fun>) &&
(!tag_invocable<upon_stopped_t, _Sender, _Fun>) &&
__callable<_Fun> && sender<__sender<_Sender, _Fun>>
__sender<_Sender, _Fun> operator()(_Sender&& __sndr, _Fun __fun) const
{
return __sender<_Sender, _Fun>{(_Sender&&)__sndr, (_Fun&&)__fun};
}
template <__callable _Fun>
__binder_back<upon_stopped_t, _Fun> operator()(_Fun __fun) const
{
return {{}, {}, {(_Fun&&)__fun}};
}
};
} // namespace __upon_stopped
using __upon_stopped::upon_stopped_t;
inline constexpr upon_stopped_t upon_stopped{};
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.bulk]
namespace __bulk
{
inline constexpr __mstring __bulk_context =
"In stdexec::bulk(Sender, Shape, Function)..."__csz;
using __on_not_callable = __callable_error<__bulk_context>;
template <class _ReceiverId, integral _Shape, class _Fun>
struct __receiver
{
using _Receiver = stdexec::__t<_ReceiverId>;
class __t : receiver_adaptor<__t, _Receiver>
{
friend receiver_adaptor<__t, _Receiver>;
STDEXEC_NO_UNIQUE_ADDRESS _Shape __shape_;
STDEXEC_NO_UNIQUE_ADDRESS _Fun __f_;
template <class... _As>
void set_value(_As&&... __as) && noexcept
requires __nothrow_callable<_Fun, _Shape, _As&...>
{
for (_Shape __i{}; __i != __shape_; ++__i)
{
__f_(__i, __as...);
}
stdexec::set_value(std::move(this->base()), (_As&&)__as...);
}
template <class... _As>
void set_value(_As&&... __as) && noexcept
requires __callable<_Fun, _Shape, _As&...>
{
try
{
for (_Shape __i{}; __i != __shape_; ++__i)
{
__f_(__i, __as...);
}
stdexec::set_value(std::move(this->base()), (_As&&)__as...);
}
catch (...)
{
stdexec::set_error(std::move(this->base()),
std::current_exception());
}
}
public:
using __id = __receiver;
explicit __t(_Receiver __rcvr, _Shape __shape, _Fun __fun) :
receiver_adaptor<__t, _Receiver>((_Receiver&&)__rcvr),
__shape_(__shape), __f_((_Fun&&)__fun)
{}
};
};
template <class _Ty>
using __decay_ref = __decay_t<_Ty>&;
template <class _SenderId, integral _Shape, class _Fun>
struct __sender
{
using _Sender = stdexec::__t<_SenderId>;
template <receiver _Receiver>
using __receiver =
stdexec::__t<__receiver<stdexec::__id<_Receiver>, _Shape, _Fun>>;
struct __t
{
using __id = __sender;
using is_sender = void;
STDEXEC_NO_UNIQUE_ADDRESS _Sender __sndr_;
STDEXEC_NO_UNIQUE_ADDRESS _Shape __shape_;
STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
template <class _Sender, class _Env, class _Catch>
using __with_error_invoke_t = //
__if<__try_value_types_of_t<
_Sender, _Env,
__transform<
__q<__decay_ref>,
__mbind_front<__mtry_catch_q<__non_throwing_, _Catch>,
_Fun, _Shape>>,
__q<__mand>>,
completion_signatures<>, __with_exception_ptr>;
template <class _Self, class _Env>
using __completion_signatures = //
__try_make_completion_signatures<
__copy_cvref_t<_Self, _Sender>, _Env,
__with_error_invoke_t<__copy_cvref_t<_Self, _Sender>, _Env,
__on_not_callable>>;
template <__decays_to<__t> _Self, receiver _Receiver>
requires sender_to<__copy_cvref_t<_Self, _Sender>,
__receiver<_Receiver>>
friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) //
noexcept(__nothrow_connectable<__copy_cvref_t<_Self, _Sender>,
__receiver<_Receiver>>)
-> connect_result_t<__copy_cvref_t<_Self, _Sender>,
__receiver<_Receiver>>
{
return stdexec::connect(
((_Self&&)__self).__sndr_,
__receiver<_Receiver>{(_Receiver&&)__rcvr, __self.__shape_,
((_Self&&)__self).__fun_});
}
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> dependent_completion_signatures<_Env>;
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> __completion_signatures<_Self, _Env>
requires true;
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
-> env_of_t<const _Sender&>
{
return get_env(__self.__sndr_);
}
};
};
struct bulk_t
{
template <sender _Sender, integral _Shape, class _Fun>
using __sender =
__t<__sender<stdexec::__id<__decay_t<_Sender>>, _Shape, _Fun>>;
template <sender _Sender, integral _Shape, __movable_value _Fun>
requires __tag_invocable_with_completion_scheduler<
bulk_t, set_value_t, _Sender, _Shape, _Fun>
sender auto operator()(_Sender&& __sndr, _Shape __shape, _Fun __fun) const
noexcept(nothrow_tag_invocable<
bulk_t, __completion_scheduler_for<_Sender, set_value_t>,
_Sender, _Shape, _Fun>)
{
auto __sched = get_completion_scheduler<set_value_t>(get_env(__sndr));
return tag_invoke(bulk_t{}, std::move(__sched), (_Sender&&)__sndr,
(_Shape&&)__shape, (_Fun&&)__fun);
}
template <sender _Sender, integral _Shape, __movable_value _Fun>
requires(!__tag_invocable_with_completion_scheduler<
bulk_t, set_value_t, _Sender, _Shape, _Fun>) &&
tag_invocable<bulk_t, _Sender, _Shape, _Fun>
sender auto operator()(_Sender&& __sndr, _Shape __shape, _Fun __fun) const
noexcept(nothrow_tag_invocable<bulk_t, _Sender, _Shape, _Fun>)
{
return tag_invoke(bulk_t{}, (_Sender&&)__sndr, (_Shape&&)__shape,
(_Fun&&)__fun);
}
template <sender _Sender, integral _Shape, __movable_value _Fun>
requires(!__tag_invocable_with_completion_scheduler<
bulk_t, set_value_t, _Sender, _Shape, _Fun>) &&
(!tag_invocable<bulk_t, _Sender, _Shape, _Fun>)
STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
__sender<_Sender, _Shape, _Fun>
operator()(_Sender&& __sndr, _Shape __shape, _Fun __fun) const
{
return __sender<_Sender, _Shape, _Fun>{(_Sender&&)__sndr, __shape,
(_Fun&&)__fun};
}
template <integral _Shape, class _Fun>
__binder_back<bulk_t, _Shape, _Fun> operator()(_Shape __shape,
_Fun __fun) const
{
return {{}, {}, {(_Shape&&)__shape, (_Fun&&)__fun}};
}
};
} // namespace __bulk
using __bulk::bulk_t;
inline constexpr bulk_t bulk{};
////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.split]
namespace __split
{
template <class _BaseEnv>
using __env_t = //
__make_env_t<_BaseEnv, // BUGBUG NOT TO SPEC
__with<get_stop_token_t, in_place_stop_token>>;
template <class _CvrefSenderId, class _EnvId>
struct __sh_state;
template <class _CvrefSenderId, class _EnvId>
struct __receiver
{
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
using _Env = stdexec::__t<_EnvId>;
class __t
{
stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>& __sh_state_;
public:
using is_receiver = void;
using __id = __receiver;
template <__completion_tag _Tag, class... _As>
friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept
{
stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>& __state =
__self.__sh_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.__sh_state_.__env_;
}
explicit __t(stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>&
__sh_state) noexcept :
__sh_state_(__sh_state)
{}
};
};
struct __operation_base
{
using __notify_fn = void(__operation_base*) noexcept;
__operation_base* __next_{};
__notify_fn* __notify_{};
};
template <class _CvrefSenderId, class _EnvId>
struct __sh_state
{
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
using _Env = stdexec::__t<_EnvId>;
struct __t
{
using __id = __sh_state;
template <class... _Ts>
using __bind_tuples = //
__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>,
_Ts...>;
using __bound_values_t = //
__value_types_of_t<_CvrefSender, __env_t<_Env>,
__mbind_front_q<__decayed_tuple, set_value_t>,
__q<__bind_tuples>>;
using __variant_t = //
__error_types_of_t<
_CvrefSender, __env_t<_Env>,
__transform<__mbind_front_q<__decayed_tuple, set_error_t>,
__bound_values_t>>;
using __receiver_ = stdexec::__t<__receiver<_CvrefSenderId, _EnvId>>;
void* const __token_{(void*)0xDEADBEEF};
in_place_stop_source __stop_source_{};
__variant_t __data_;
std::atomic<void*> __head_{nullptr};
__env_t<_Env> __env_;
connect_result_t<_CvrefSender, __receiver_> __op_state2_;
explicit __t(_CvrefSender&& __sndr, _Env __env) :
__env_(
__make_env((_Env&&)__env, __with_(get_stop_token,
__stop_source_.get_token()))),
__op_state2_(connect((_CvrefSender&&)__sndr, __receiver_{*this}))
{}
void __notify() noexcept
{
void* const __completion_state = static_cast<void*>(this);
void* __old = __head_.exchange(__completion_state,
std::memory_order_acq_rel);
__operation_base* __op_state =
static_cast<__operation_base*>(__old);
while (__op_state != nullptr)
{
__operation_base* __next = __op_state->__next_;
__op_state->__notify_(__op_state);
__op_state = __next;
}
}
};
};
template <class _CvrefSenderId, class _EnvId, class _ReceiverId>
struct __operation
{
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
using _Env = stdexec::__t<_EnvId>;
using _Receiver = stdexec::__t<_ReceiverId>;
class __t : public __operation_base
{
struct __on_stop_requested
{
in_place_stop_source& __stop_source_;
void operator()() noexcept
{
__stop_source_.request_stop();
}
};
using __on_stop = //
std::optional<typename stop_token_of_t<env_of_t<_Receiver>&>::
template callback_type<__on_stop_requested>>;
_Receiver __rcvr_;
__on_stop __on_stop_{};
std::shared_ptr<stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>>
__shared_state_;
public:
using __id = __operation;
__t( //
_Receiver&& __rcvr,
std::shared_ptr<stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>>
__shared_state) //
noexcept(std::is_nothrow_move_constructible_v<_Receiver>) :
__operation_base{nullptr, __notify},
__rcvr_((_Receiver&&)__rcvr),
__shared_state_(std::move(__shared_state))
{}
STDEXEC_IMMOVABLE(__t);
static void __notify(__operation_base* __self) noexcept
{
__t* __op = static_cast<__t*>(__self);
__op->__on_stop_.reset();
std::visit(
[&](const auto& __tupl) noexcept -> void {
std::apply(
[&](auto __tag, const auto&... __args) noexcept -> void {
__tag((_Receiver&&)__op->__rcvr_, __args...);
},
__tupl);
},
__op->__shared_state_->__data_);
}
friend void tag_invoke(start_t, __t& __self) noexcept
{
stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>* __shared_state =
__self.__shared_state_.get();
STDEXEC_ASSERT(__shared_state->__token_ == (void*)0xDEADBEEF);
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)
{
__self.__on_stop_.emplace(
get_stop_token(get_env(__self.__rcvr_)),
__on_stop_requested{__shared_state->__stop_source_});
}
do
{
if (__old == __completion_state)
{
__self.__notify(&__self);
return;
}
__self.__next_ = static_cast<__operation_base*>(__old);
} while (!__head.compare_exchange_weak(
__old, static_cast<void*>(&__self), std::memory_order_release,
std::memory_order_acquire));
if (__old == nullptr)
{
// the inner sender isn't running
if (__shared_state->__stop_source_.stop_requested())
{
// 1. resets __head to completion state
// 2. notifies waiting threads
// 3. propagates "stopped" signal to `out_r'`
__shared_state->__notify();
}
else
{
start(__shared_state->__op_state2_);
}
}
}
};
};
template <class _CvrefSenderId, class _EnvId>
struct __sender
{
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
using _Env = stdexec::__t<_EnvId>;
template <class _Receiver>
using __operation = stdexec::__t<
__operation<_CvrefSenderId, _EnvId, stdexec::__id<_Receiver>>>;
struct __t
{
using __id = __sender;
using is_sender = void;
explicit __t(_CvrefSender&& __sndr, _Env __env) :
__shared_state_{std::make_shared<__sh_state_>(
static_cast<_CvrefSender&&>(__sndr), (_Env&&)__env)}
{}
private:
using __sh_state_ = stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>;
template <class... _Tys>
using __set_value_t =
completion_signatures<set_value_t(const __decay_t<_Tys>&...)>;
template <class _Ty>
using __set_error_t =
completion_signatures<set_error_t(const __decay_t<_Ty>&)>;
template <class _Self>
using __completions_t = //
__try_make_completion_signatures<
// NOT TO SPEC:
// See
// https://github.com/brycelelbach/wg21_p2300_execution/issues/26
_CvrefSender, __env_t<__mfront<_Env, _Self>>,
completion_signatures<set_error_t(const std::exception_ptr&),
set_stopped_t()>, // NOT TO SPEC
__q<__set_value_t>, __q<__set_error_t>>;
std::shared_ptr<__sh_state_> __shared_state_;
template <__decays_to<__t> _Self,
receiver_of<__completions_t<_Self>> _Receiver>
friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __recvr) //
noexcept(std::is_nothrow_move_constructible_v<_Receiver>)
-> __operation<_Receiver>
{
return __operation<_Receiver>{(_Receiver&&)__recvr,
__self.__shared_state_};
}
template <__decays_to<__t> _Self, class _OtherEnv>
friend auto tag_invoke(get_completion_signatures_t, _Self&&,
_OtherEnv&&) -> __completions_t<_Self>;
};
};
struct split_t;
// When looking for user-defined customizations of split, these
// are the signatures to test against, in order:
using _Sender = __0;
using _Env = __1;
using __cust_sigs = //
__types<tag_invoke_t(split_t,
get_completion_scheduler_t<set_value_t>(
get_env_t(const _Sender&)),
_Sender),
tag_invoke_t(split_t,
get_completion_scheduler_t<set_value_t>(
get_env_t(const _Sender&)),
_Sender, _Env),
tag_invoke_t(split_t, get_scheduler_t(_Env&), _Sender),
tag_invoke_t(split_t, get_scheduler_t(_Env&), _Sender, _Env),
tag_invoke_t(split_t, _Sender),
tag_invoke_t(split_t, _Sender, _Env)>;
template <class _Sender, class _Env>
inline constexpr bool __is_split_customized =
__minvocable<__which<__cust_sigs>, _Sender, _Env>;
template <class _Sender, class _Env>
using __sender_t =
__t<__sender<stdexec::__cvref_id<_Sender>, stdexec::__id<__decay_t<_Env>>>>;
template <class _Sender, class _Env>
using __dispatcher_for =
__make_dispatcher<__cust_sigs, __mconstructor_for<__sender_t>, _Sender,
_Env>;
struct split_t
{
template <sender _Sender, class _Env = empty_env>
requires(sender_in<_Sender, _Env> &&
__decay_copyable<env_of_t<_Sender>>) ||
__is_split_customized<_Sender, _Env>
auto operator()(_Sender&& __sndr, _Env&& __env = _Env{}) const noexcept(
__nothrow_callable<__dispatcher_for<_Sender, _Env>, _Sender, _Env>)
-> __call_result_t<__dispatcher_for<_Sender, _Env>, _Sender, _Env>
{
return __dispatcher_for<_Sender, _Env>{}((_Sender&&)__sndr,
(_Env&&)__env);
}
__binder_back<split_t> operator()() const
{
return {{}, {}, {}};
}
};
} // namespace __split
using __split::split_t;
inline constexpr split_t split{};
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.ensure_started]
namespace __ensure_started
{
template <class _BaseEnv>
using __env_t = //
__make_env_t<_BaseEnv, // NOT TO SPEC
__with<get_stop_token_t, in_place_stop_token>>;
template <class _CvrefSenderId, class _EnvId>
struct __sh_state;
template <class _CvrefSenderId, class _EnvId>
struct __receiver
{
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
using _Env = stdexec::__t<_EnvId>;
class __t
{
__intrusive_ptr<stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>>
__shared_state_;
public:
using is_receiver = void;
using __id = __receiver;
explicit __t(stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>&
__shared_state) noexcept :
__shared_state_(__shared_state.__intrusive_from_this())
{}
template <__completion_tag _Tag, class... _As>
friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept
{
stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>& __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();
__self.__shared_state_.reset();
}
friend const __env_t<_Env>& tag_invoke(get_env_t,
const __t& __self) noexcept
{
return __self.__shared_state_->__env_;
}
};
};
struct __operation_base
{
using __notify_fn = void(__operation_base*) noexcept;
__notify_fn* __notify_{};
};
template <class _CvrefSenderId, class _EnvId>
struct __sh_state
{
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
using _Env = stdexec::__t<_EnvId>;
struct __t : __enable_intrusive_from_this<__t>
{
using __id = __sh_state;
template <class... _Ts>
using __bind_tuples = //
__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>,
_Ts...>;
using __bound_values_t = //
__value_types_of_t<_CvrefSender, __env_t<_Env>,
__mbind_front_q<__decayed_tuple, set_value_t>,
__q<__bind_tuples>>;
using __variant_t = //
__error_types_of_t<
_CvrefSender, __env_t<_Env>,
__transform<__mbind_front_q<__decayed_tuple, set_error_t>,
__bound_values_t>>;
using __receiver_t = stdexec::__t<__receiver<_CvrefSenderId, _EnvId>>;
__variant_t __data_;
in_place_stop_source __stop_source_{};
std::atomic<void*> __op_state1_{nullptr};
__env_t<_Env> __env_;
connect_result_t<_CvrefSender, __receiver_t> __op_state2_;
explicit __t(_CvrefSender&& __sndr, _Env __env) :
__env_(
__make_env((_Env&&)__env, __with_(get_stop_token,
__stop_source_.get_token()))),
__op_state2_(connect((_CvrefSender&&)__sndr, __receiver_t{*this}))
{
start(__op_state2_);
}
void __notify() noexcept
{
void* const __completion_state = static_cast<void*>(this);
void* const __old = __op_state1_.exchange(
__completion_state, std::memory_order_acq_rel);
if (__old != nullptr)
{
auto* __op = static_cast<__operation_base*>(__old);
__op->__notify_(__op);
}
}
void __detach() noexcept
{
__stop_source_.request_stop();
}
};
};
template <class _CvrefSenderId, class _EnvId, class _ReceiverId>
struct __operation
{
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
using _Env = stdexec::__t<_EnvId>;
using _Receiver = stdexec::__t<_ReceiverId>;
class __t : public __operation_base
{
struct __on_stop_requested
{
in_place_stop_source& __stop_source_;
void operator()() noexcept
{
__stop_source_.request_stop();
}
};
using __on_stop = //
std::optional<typename stop_token_of_t<env_of_t<_Receiver>&>::
template callback_type<__on_stop_requested>>;
_Receiver __rcvr_;
__on_stop __on_stop_{};
__intrusive_ptr<stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>>
__shared_state_;
public:
using __id = __operation;
__t( //
_Receiver __rcvr, //
__intrusive_ptr<stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>>
__shared_state) //
noexcept(std::is_nothrow_move_constructible_v<_Receiver>) :
__operation_base{__notify},
__rcvr_((_Receiver&&)__rcvr),
__shared_state_(std::move(__shared_state))
{}
~__t()
{
// Check to see if this operation was ever started. If not,
// detach the (potentially still running) operation:
if (nullptr ==
__shared_state_->__op_state1_.load(std::memory_order_acquire))
{
__shared_state_->__detach();
}
}
STDEXEC_IMMOVABLE(__t);
static void __notify(__operation_base* __self) noexcept
{
__t* __op = static_cast<__t*>(__self);
__op->__on_stop_.reset();
std::visit(
[&](auto& __tupl) noexcept -> void {
std::apply(
[&](auto __tag, auto&... __args) noexcept -> void {
__tag((_Receiver&&)__op->__rcvr_, std::move(__args)...);
},
__tupl);
},
__op->__shared_state_->__data_);
}
friend void tag_invoke(start_t, __t& __self) noexcept
{
stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>* __shared_state =
__self.__shared_state_.get();
std::atomic<void*>& __op_state1 = __shared_state->__op_state1_;
void* const __completion_state = static_cast<void*>(__shared_state);
void* const __old = __op_state1.load(std::memory_order_acquire);
if (__old == __completion_state)
{
__self.__notify(&__self);
}
else
{
// register stop callback:
__self.__on_stop_.emplace(
get_stop_token(get_env(__self.__rcvr_)),
__on_stop_requested{__shared_state->__stop_source_});
// 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&&)__self.__rcvr_);
}
else
{
// Otherwise, the inner source hasn't notified completion.
// Set this operation as the __op_state1 so it's notified.
void* __old = nullptr;
if (!__op_state1.compare_exchange_weak(
__old, &__self, std::memory_order_release,
std::memory_order_acquire))
{
// We get here when the task completed during the
// execution of this function. Complete the operation
// synchronously.
STDEXEC_ASSERT(__old == __completion_state);
__self.__notify(&__self);
}
}
}
}
};
};
template <class _CvrefSenderId, class _EnvId>
struct __sender
{
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
using _Env = stdexec::__t<_EnvId>;
struct __t
{
using __id = __sender;
using is_sender = void;
explicit __t(_CvrefSender __sndr, _Env __env) :
__shared_state_{__make_intrusive<__sh_state_>(
(_CvrefSender&&)__sndr, (_Env&&)__env)}
{}
~__t()
{
if (nullptr != __shared_state_)
{
// We're detaching a potentially running operation. Request
// cancellation.
__shared_state_->__detach(); // BUGBUG NOT TO SPEC
}
}
// Move-only:
__t(__t&&) = default;
private:
using __sh_state_ = stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>;
template <class _Receiver>
using __operation = stdexec::__t<
__operation<_CvrefSenderId, _EnvId, stdexec::__id<_Receiver>>>;
template <class... _Tys>
using __set_value_t =
completion_signatures<set_value_t(__decay_t<_Tys>&&...)>;
template <class _Ty>
using __set_error_t =
completion_signatures<set_error_t(__decay_t<_Ty>&&)>;
template <class _Self>
using __completions_t = //
__try_make_completion_signatures<
_CvrefSender, __env_t<__mfront<_Env, _Self>>,
completion_signatures<set_error_t(std::exception_ptr&&),
set_stopped_t()>, // BUGBUG NOT TO SPEC
__q<__set_value_t>, __q<__set_error_t>>;
__intrusive_ptr<__sh_state_> __shared_state_;
template <same_as<__t> _Self,
receiver_of<__completions_t<_Self>> _Receiver>
friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) //
noexcept(std::is_nothrow_move_constructible_v<_Receiver>)
-> __operation<_Receiver>
{
return __operation<_Receiver>{(_Receiver&&)__rcvr,
std::move(__self).__shared_state_};
}
template <same_as<__t> _Self, class _OtherEnv>
friend auto tag_invoke(get_completion_signatures_t, _Self&&,
_OtherEnv&&) -> __completions_t<_Self>;
};
};
struct ensure_started_t;
// When looking for user-defined customizations of ensure_started, these
// are the signatures to test against, in order:
using _CvrefSender = __0;
using _Env = __1;
using __cust_sigs = //
__types<tag_invoke_t(ensure_started_t,
get_completion_scheduler_t<set_value_t>(
get_env_t(const _CvrefSender&)),
_CvrefSender),
tag_invoke_t(ensure_started_t,
get_completion_scheduler_t<set_value_t>(
get_env_t(const _CvrefSender&)),
_CvrefSender, _Env),
tag_invoke_t(ensure_started_t, get_scheduler_t(_Env&),
_CvrefSender),
tag_invoke_t(ensure_started_t, get_scheduler_t(_Env&), _CvrefSender,
_Env),
tag_invoke_t(ensure_started_t, _CvrefSender),
tag_invoke_t(ensure_started_t, _CvrefSender, _Env)>;
template <class _CvrefSender, class _Env>
inline constexpr bool __is_ensure_started_customized =
__minvocable<__which<__cust_sigs>, _CvrefSender, _Env>;
template <class _Sender, class _Env>
using __sender_t = __t<__sender<stdexec::__cvref_id<_Sender, _Sender>,
stdexec::__id<__decay_t<_Env>>>>;
template <class _Sender>
concept __ensure_started_sender = //
__is_instance_of<__id<__decay_t<_Sender>>, __sender>;
template <class _Sender>
using __fallback = __if_c<__ensure_started_sender<_Sender>, __mconst<__first>,
__mconstructor_for<__sender_t>>;
template <class _Sender, class _Env>
using __dispatcher_for =
__make_dispatcher<__cust_sigs, __fallback<_Sender>, _Sender, _Env>;
struct ensure_started_t
{
template <sender _Sender, class _Env = empty_env>
requires(sender_in<_Sender, _Env> &&
__decay_copyable<env_of_t<_Sender>>) ||
__is_ensure_started_customized<_Sender, _Env>
auto operator()(_Sender&& __sndr, _Env&& __env = _Env{}) const noexcept(
__nothrow_callable<__dispatcher_for<_Sender, _Env>, _Sender, _Env>)
-> __call_result_t<__dispatcher_for<_Sender, _Env>, _Sender, _Env>
{
return __dispatcher_for<_Sender, _Env>{}((_Sender&&)__sndr,
(_Env&&)__env);
}
__binder_back<ensure_started_t> operator()() const
{
return {{}, {}, {}};
}
};
} // namespace __ensure_started
using __ensure_started::ensure_started_t;
inline constexpr ensure_started_t ensure_started{};
//////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.let_value]
// [execution.senders.adaptors.let_error]
// [execution.senders.adaptors.let_stopped]
namespace __let
{
template <class _Set>
struct __on_not_callable_
{
using __t =
__callable_error<"In stdexec::let_value(Sender, Function)..."__csz>;
};
template <>
struct __on_not_callable_<set_error_t>
{
using __t =
__callable_error<"In stdexec::let_error(Sender, Function)..."__csz>;
};
template <>
struct __on_not_callable_<set_stopped_t>
{
using __t =
__callable_error<"In stdexec::let_stopped(Sender, Function)..."__csz>;
};
template <class _Set>
using __on_not_callable = __t<__on_not_callable_<_Set>>;
template <class _Tp>
using __decay_ref = __decay_t<_Tp>&;
template <class _Fun, class _Set>
using __result_sender = //
__transform<
__q<__decay_ref>,
__mbind_front<__mtry_catch_q<__call_result_t, __on_not_callable<_Set>>,
_Fun>>;
template <class _Receiver, class _Fun, class _Set>
using __op_state_for = __mcompose<__mbind_back_q<connect_result_t, _Receiver>,
__result_sender<_Fun, _Set>>;
template <class _Set, class _Sig>
struct __tfx_signal_
{
template <class, class>
using __f = completion_signatures<_Sig>;
};
template <class _Set, class... _Args>
struct __tfx_signal_<_Set, _Set(_Args...)>
{
template <class _Env, class _Fun>
using __f = //
__try_make_completion_signatures<
__minvoke<__result_sender<_Fun, _Set>, _Args...>, _Env,
// because we don't know if connect-ing the result sender will
// throw:
completion_signatures<set_error_t(std::exception_ptr)>>;
};
template <class _Env, class _Fun, class _Set, class _Sig>
using __tfx_signal_t = __minvoke<__tfx_signal_<_Set, _Sig>, _Env, _Fun>;
template <class _ReceiverId, class _Fun, class _Set, class... _Tuples>
struct __operation_base_
{
using _Receiver = stdexec::__t<_ReceiverId>;
struct __t : __immovable
{
using __id = __operation_base_;
using __results_variant_t = std::variant<std::monostate, _Tuples...>;
using __op_state_variant_t = //
__minvoke<
__transform<__uncurry<__op_state_for<_Receiver, _Fun, _Set>>,
__nullable_variant_t>,
_Tuples...>;
_Receiver __rcvr_;
_Fun __fun_;
__results_variant_t __args_;
__op_state_variant_t __op_state3_;
};
};
template <class _ReceiverId, class _Fun, class _Set, class... _Tuples>
struct __receiver_
{
using _Receiver = stdexec::__t<_ReceiverId>;
using _Env = env_of_t<_Receiver>;
struct __t
{
using is_receiver = void;
using __id = __receiver_;
template <__one_of<_Set> _Tag, class... _As>
requires(1 == __v<__minvoke<__mcount<__decayed_tuple<_As...>>,
_Tuples...>>) &&
__minvocable<__result_sender<_Fun, _Set>, _As...> &&
sender_to<__minvoke<__result_sender<_Fun, _Set>, _As...>,
_Receiver>
friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept
{
try
{
using __tuple_t = __decayed_tuple<_As...>;
using __op_state_t =
__minvoke<__op_state_for<_Receiver, _Fun, _Set>, _As...>;
auto& __args =
__self.__op_state_->__args_.template emplace<__tuple_t>(
(_As&&)__as...);
auto& __op = __self.__op_state_->__op_state3_
.template emplace<__op_state_t>(__conv{[&] {
return connect(
std::apply(std::move(__self.__op_state_->__fun_),
__args),
std::move(__self.__op_state_->__rcvr_));
}});
start(__op);
}
catch (...)
{
set_error(std::move(__self.__op_state_->__rcvr_),
std::current_exception());
}
}
template <__completion_tag _Tag, class... _As>
requires __none_of<_Tag, _Set> &&
__callable<_Tag, _Receiver, _As...>
friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept
{
__tag(std::move(__self.__op_state_->__rcvr_), (_As&&)__as...);
}
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
-> env_of_t<_Receiver>
{
return get_env(__self.__op_state_->__rcvr_);
}
using __operation_base_t = stdexec::__t<
__operation_base_<_ReceiverId, _Fun, _Set, _Tuples...>>;
__operation_base_t* __op_state_;
};
};
template <class _CvrefSenderId, class _ReceiverId, class _Fun, class _Set>
using __receiver = //
stdexec::__t<__gather_completions_for<
_Set, __cvref_t<_CvrefSenderId>, env_of_t<__t<_ReceiverId>>,
__q<__decayed_tuple>,
__munique<__mbind_front_q<__receiver_, _ReceiverId, _Fun, _Set>>>>;
template <class _CvrefSenderId, class _ReceiverId, class _Fun, class _Set>
using __operation_base = typename __receiver<_CvrefSenderId, _ReceiverId, _Fun,
_Set>::__operation_base_t;
template <class _CvrefSenderId, class _ReceiverId, class _Fun, class _Set>
struct __operation
{
using _Sender = stdexec::__cvref_t<_CvrefSenderId>;
struct __t : __operation_base<_CvrefSenderId, _ReceiverId, _Fun, _Set>
{
using __id = __operation;
using __op_base_t =
__operation_base<_CvrefSenderId, _ReceiverId, _Fun, _Set>;
using __receiver_t =
__receiver<_CvrefSenderId, _ReceiverId, _Fun, _Set>;
friend void tag_invoke(start_t, __t& __self) noexcept
{
start(__self.__op_state2_);
}
template <class _Receiver2>
__t(_Sender&& __sndr, _Receiver2&& __rcvr, _Fun __fun) :
__op_base_t{{}, (_Receiver2&&)__rcvr, (_Fun&&)__fun},
__op_state2_(connect((_Sender&&)__sndr, __receiver_t{this}))
{}
connect_result_t<_Sender, __receiver_t> __op_state2_;
};
};
template <class _SenderId, class _Fun, class _SetId>
struct __sender
{
using _Sender = stdexec::__t<_SenderId>;
using _Set = stdexec::__t<_SetId>;
struct __t
{
using __id = __sender;
using is_sender = void;
template <class _Self, class _Receiver>
using __operation_t = //
stdexec::__t<__operation<stdexec::__cvref_id<_Self, _Sender>,
stdexec::__id<_Receiver>, _Fun, _Set>>;
template <class _Self, class _Receiver>
using __receiver_t = __receiver<stdexec::__cvref_id<_Self, _Sender>,
stdexec::__id<_Receiver>, _Fun, _Set>;
template <class _Sender, class _Env>
using __completions = //
__mapply<
__transform<__mbind_front_q<__tfx_signal_t, _Env, _Fun, _Set>,
__q<__concat_completion_signatures_t>>,
__completion_signatures_of_t<_Sender, _Env>>;
template <__decays_to<__t> _Self, receiver _Receiver>
requires sender_to<__copy_cvref_t<_Self, _Sender>,
__receiver_t<_Self, _Receiver>>
friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr)
-> __operation_t<_Self, _Receiver>
{
return __operation_t<_Self, _Receiver>{((_Self&&)__self).__sndr_,
(_Receiver&&)__rcvr,
((_Self&&)__self).__fun_};
}
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
-> env_of_t<const _Sender&>
{
return get_env(__self.__sndr_);
}
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> dependent_completion_signatures<_Env>;
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> __completions<__copy_cvref_t<_Self, _Sender>, _Env>
requires true;
_Sender __sndr_;
_Fun __fun_;
};
};
template <class _LetTag, class _SetTag>
struct __let_xxx_t
{
using __t = _SetTag;
template <class _Sender, class _Fun>
using __sender = stdexec::__t<
__let::__sender<stdexec::__id<__decay_t<_Sender>>, _Fun, _LetTag>>;
template <sender _Sender, __movable_value _Fun>
requires __tag_invocable_with_completion_scheduler<_LetTag, set_value_t,
_Sender, _Fun>
sender auto operator()(_Sender&& __sndr, _Fun __fun) const
noexcept(nothrow_tag_invocable<
_LetTag, __completion_scheduler_for<_Sender, set_value_t>,
_Sender, _Fun>)
{
auto __sched = get_completion_scheduler<set_value_t>(get_env(__sndr));
return tag_invoke(_LetTag{}, std::move(__sched), (_Sender&&)__sndr,
(_Fun&&)__fun);
}
template <sender _Sender, __movable_value _Fun>
requires(!__tag_invocable_with_completion_scheduler<
_LetTag, set_value_t, _Sender, _Fun>) &&
tag_invocable<_LetTag, _Sender, _Fun>
sender auto operator()(_Sender&& __sndr, _Fun __fun) const
noexcept(nothrow_tag_invocable<_LetTag, _Sender, _Fun>)
{
return tag_invoke(_LetTag{}, (_Sender&&)__sndr, (_Fun&&)__fun);
}
template <sender _Sender, __movable_value _Fun>
requires(!__tag_invocable_with_completion_scheduler<
_LetTag, set_value_t, _Sender, _Fun>) &&
(!tag_invocable<_LetTag, _Sender, _Fun>) &&
sender<__sender<_Sender, _Fun>>
__sender<_Sender, _Fun> operator()(_Sender&& __sndr, _Fun __fun) const
{
return __sender<_Sender, _Fun>{(_Sender&&)__sndr, (_Fun&&)__fun};
}
template <class _Fun>
__binder_back<_LetTag, _Fun> operator()(_Fun __fun) const
{
return {{}, {}, {(_Fun&&)__fun}};
}
};
struct let_value_t : __let::__let_xxx_t<let_value_t, set_value_t>
{};
struct let_error_t : __let::__let_xxx_t<let_error_t, set_error_t>
{};
struct let_stopped_t : __let::__let_xxx_t<let_stopped_t, set_stopped_t>
{};
} // namespace __let
using __let::let_value_t;
inline constexpr let_value_t let_value{};
using __let::let_error_t;
inline constexpr let_error_t let_error{};
using __let::let_stopped_t;
inline constexpr let_stopped_t let_stopped{};
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.stopped_as_optional]
// [execution.senders.adaptors.stopped_as_error]
namespace __stopped_as_xxx
{
template <class _CvrefSenderId, class _ReceiverId>
struct __operation;
template <class _CvrefSenderId, class _ReceiverId>
struct __receiver
{
using _Sender = stdexec::__t<_CvrefSenderId>;
using _Receiver = stdexec::__t<_ReceiverId>;
struct __t : receiver_adaptor<__t>
{
using __id = __receiver;
_Receiver&& base() && noexcept
{
return (_Receiver&&)__op_->__rcvr_;
}
const _Receiver& base() const& noexcept
{
return __op_->__rcvr_;
}
template <class _Ty>
void set_value(_Ty&& __a) && noexcept
{
try
{
using _Value = __decay_t<
__single_sender_value_t<_Sender, env_of_t<_Receiver>>>;
static_assert(constructible_from<_Value, _Ty>);
stdexec::set_value(((__t&&)*this).base(),
std::optional<_Value>{(_Ty&&)__a});
}
catch (...)
{
stdexec::set_error(((__t&&)*this).base(),
std::current_exception());
}
}
void set_stopped() && noexcept
{
using _Value = __decay_t<
__single_sender_value_t<_Sender, env_of_t<_Receiver>>>;
stdexec::set_value(((__t&&)*this).base(),
std::optional<_Value>{std::nullopt});
}
stdexec::__t<__operation<_CvrefSenderId, _ReceiverId>>* __op_;
};
};
template <class _CvrefSenderId, class _ReceiverId>
struct __operation
{
using _Sender = stdexec::__t<_CvrefSenderId>;
using _Receiver = stdexec::__t<_ReceiverId>;
using __receiver_t = stdexec::__t<__receiver<_CvrefSenderId, _ReceiverId>>;
struct __t
{
using __id = __operation;
__t(_Sender&& __sndr, _Receiver&& __rcvr) :
__rcvr_((_Receiver&&)__rcvr),
__op_state_(connect((_Sender&&)__sndr, __receiver_t{{}, this}))
{}
STDEXEC_IMMOVABLE(__t);
friend void tag_invoke(start_t, __t& __self) noexcept
{
start(__self.__op_state_);
}
_Receiver __rcvr_;
connect_result_t<_Sender, __receiver_t> __op_state_;
};
};
template <class _SenderId>
struct __sender
{
using _Sender = stdexec::__t<_SenderId>;
struct __t
{
using __id = __sender;
using is_sender = void;
template <class _Self, class _Receiver>
using __operation_t =
stdexec::__t<__operation<stdexec::__cvref_id<_Self, _Sender>,
stdexec::__id<_Receiver>>>;
template <class _Self, class _Receiver>
using __receiver_t =
stdexec::__t<__receiver<stdexec::__cvref_id<_Self, _Sender>,
stdexec::__id<_Receiver>>>;
template <__decays_to<__t> _Self, receiver _Receiver>
requires __single_typed_sender<__copy_cvref_t<_Self, _Sender>,
env_of_t<_Receiver>> &&
sender_to<__copy_cvref_t<_Self, _Sender>,
__receiver_t<_Self, _Receiver>>
friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr)
-> __operation_t<_Self, _Receiver>
{
return {((_Self&&)__self).__sndr_, (_Receiver&&)__rcvr};
}
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
-> env_of_t<const _Sender&>
{
return get_env(__self.__sndr_);
}
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)>;
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> make_completion_signatures<
__copy_cvref_t<_Self, _Sender>, _Env,
completion_signatures<set_error_t(std::exception_ptr)>,
__set_value_t, __set_error_t, completion_signatures<>>;
_Sender __sndr_;
};
};
struct stopped_as_optional_t
{
template <sender _Sender>
auto operator()(_Sender&& __sndr) const
-> __t<__sender<stdexec::__id<__decay_t<_Sender>>>>
{
return {(_Sender&&)__sndr};
}
__binder_back<stopped_as_optional_t> operator()() const noexcept
{
return {};
}
};
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>
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{};
/////////////////////////////////////////////////////////////////////////////
// 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_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 is_sender = void;
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 _State, class... _Tuples>
using __make_bind_ = __mbind_back<_State, _Tuples...>;
template <class _State>
using __make_bind = __mbind_front_q<__make_bind_, _State>;
template <class _Tag>
using __tuple_t = __mbind_front_q<__decayed_tuple, _Tag>;
template <class _Sender, class _Env, class _State, class _Tag>
using __bind_completions_t =
__gather_completions_for<_Tag, _Sender, _Env, __tuple_t<_Tag>,
__make_bind<_State>>;
template <class _Sender, class _Env>
using __variant_for_t = //
__minvoke<__minvoke<
__mfold_right<__nullable_variant_t,
__mbind_front_q<__bind_completions_t, _Sender, _Env>>,
set_value_t, set_error_t, set_stopped_t>>;
template <class _SchedulerId, class _CvrefSenderId, class _ReceiverId>
struct __operation1;
// 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 _SchedulerId, class _CvrefSenderId, class _ReceiverId>
struct __receiver2
{
using _Receiver = stdexec::__t<_ReceiverId>;
struct __t
{
using is_receiver = void;
using __id = __receiver2;
stdexec::__t<__operation1<_SchedulerId, _CvrefSenderId, _ReceiverId>>*
__op_state_;
// If the work is successfully scheduled on the new execution
// context and is ready to run, forward the completion signal in
// the operation state
template <same_as<set_value_t> _Tag>
friend void tag_invoke(_Tag, __t&& __self) noexcept
{
__self.__op_state_->__complete();
}
template <__one_of<set_error_t, set_stopped_t> _Tag, class... _As>
requires __callable<_Tag, _Receiver, _As...>
friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept
{
_Tag{}((_Receiver&&)__self.__op_state_->__rcvr_, (_As&&)__as...);
}
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
-> env_of_t<_Receiver>
{
return get_env(__self.__op_state_->__rcvr_);
}
};
};
// This receiver is connected to the input sender. When that
// sender completes (on whatever context it completes on), save
// the completion information into the operation state. Then,
// schedule a second operation to __complete on the execution
// context of the scheduler. That second receiver will read the
// completion information out of the operation state and propagate
// it to the output receiver from within the desired context.
template <class _SchedulerId, class _CvrefSenderId, class _ReceiverId>
struct __receiver1
{
using _Scheduler = stdexec::__t<_SchedulerId>;
using _Receiver = stdexec::__t<_ReceiverId>;
using __receiver2_t =
stdexec::__t<__receiver2<_SchedulerId, _CvrefSenderId, _ReceiverId>>;
struct __t
{
using is_receiver = void;
stdexec::__t<__operation1<_SchedulerId, _CvrefSenderId, _ReceiverId>>*
__op_state_;
template <class... _Args>
static constexpr bool __nothrow_complete_ =
(__nothrow_decay_copyable<_Args> && ...);
template <class _Tag, class... _Args>
static void __complete_(_Tag, __t&& __self, _Args&&... __args) //
noexcept(__nothrow_complete_<_Args...>)
{
// Write the tag and the args into the operation state so that
// we can forward the completion from within the scheduler's
// execution context.
__self.__op_state_->__data_
.template emplace<__decayed_tuple<_Tag, _Args...>>(
_Tag{}, (_Args&&)__args...);
// Enqueue the schedule operation so the completion happens
// on the scheduler's execution context.
start(__self.__op_state_->__state2_);
}
template <__completion_tag _Tag, class... _Args>
requires __callable<_Tag, _Receiver, __decay_t<_Args>...>
friend void tag_invoke(_Tag __tag, __t&& __self,
_Args&&... __args) noexcept
{
__try_call((_Receiver&&)__self.__op_state_->__rcvr_,
__fun_c<__complete_<_Tag, _Args...>>, (_Tag&&)__tag,
(__t&&)__self, (_Args&&)__args...);
}
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
-> env_of_t<_Receiver>
{
return get_env(__self.__op_state_->__rcvr_);
}
};
};
template <class _SchedulerId, class _CvrefSenderId, class _ReceiverId>
struct __operation1
{
using _Scheduler = stdexec::__t<_SchedulerId>;
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
using _Receiver = stdexec::__t<_ReceiverId>;
using __receiver1_t =
stdexec::__t<__receiver1<_SchedulerId, _CvrefSenderId, _ReceiverId>>;
using __receiver2_t =
stdexec::__t<__receiver2<_SchedulerId, _CvrefSenderId, _ReceiverId>>;
using __variant_t = __variant_for_t<_CvrefSender, env_of_t<_Receiver>>;
struct __t
{
using __id = __operation1;
_Scheduler __sched_;
_Receiver __rcvr_;
__variant_t __data_;
connect_result_t<_CvrefSender, __receiver1_t> __state1_;
connect_result_t<schedule_result_t<_Scheduler>, __receiver2_t>
__state2_;
__t(_Scheduler __sched, _CvrefSender&& __sndr, _Receiver&& __rcvr) :
__sched_((_Scheduler&&)__sched), __rcvr_((_Receiver&&)__rcvr),
__state1_(connect((_CvrefSender&&)__sndr, __receiver1_t{this})),
__state2_(connect(schedule(__sched_), __receiver2_t{this}))
{}
STDEXEC_IMMOVABLE(__t);
friend void tag_invoke(start_t, __t& __op_state) noexcept
{
start(__op_state.__state1_);
}
void __complete() noexcept
{
STDEXEC_ASSERT(!__data_.valueless_by_exception());
std::visit(
[&]<class _Tup>(_Tup& __tupl) -> void {
if constexpr (same_as<_Tup, std::monostate>)
{
std::terminate(); // reaching this indicates a bug in
// schedule_from
}
else
{
std::apply(
[&]<class... _Args>(auto __tag,
_Args&... __args) -> void {
__tag((_Receiver&&)__rcvr_, (_Args&&)__args...);
},
__tupl);
}
},
__data_);
}
};
};
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 _SchedulerId>
struct __env
{
using _Scheduler = stdexec::__t<_SchedulerId>;
struct __t
{
using __id = __env;
_Scheduler __sched_;
template <__one_of<set_value_t, set_stopped_t> _Tag>
friend _Scheduler tag_invoke(get_completion_scheduler_t<_Tag>,
const __t& __self) noexcept
{
return __self.__sched_;
}
};
};
template <class _SchedulerId, class _SenderId>
struct __sender
{
using _Scheduler = stdexec::__t<_SchedulerId>;
using _Sender = stdexec::__t<_SenderId>;
using _Env = stdexec::__t<__env<_SchedulerId>>;
struct __t
{
using __id = __sender;
using is_sender = void;
_Env __env_;
_Sender __sndr_;
template <class _Self, class _Receiver>
using __receiver_t = //
stdexec::__t<
__receiver1<_SchedulerId, stdexec::__cvref_id<_Self, _Sender>,
stdexec::__id<_Receiver>>>;
template <__decays_to<__t> _Self, receiver _Receiver>
requires sender_to<__copy_cvref_t<_Self, _Sender>,
__receiver_t<_Self, _Receiver>>
friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) //
-> stdexec::__t<
__operation1<_SchedulerId, stdexec::__cvref_id<_Self, _Sender>,
stdexec::__id<_Receiver>>>
{
return {__self.__env_.__sched_, ((_Self&&)__self).__sndr_,
(_Receiver&&)__rcvr};
}
template <class... _Ts>
using __all_nothrow_decay_copyable =
__mbool<(__nothrow_decay_copyable<_Ts> && ...)>;
template <class _Self, class _Env>
using __all_values_and_errors_nothrow_decay_copyable =
__mand<error_types_of_t<__copy_cvref_t<_Self, _Sender>, _Env,
__all_nothrow_decay_copyable>,
value_types_of_t<__copy_cvref_t<_Self, _Sender>, _Env,
__all_nothrow_decay_copyable, __mand>>;
template <class _Self, class _Env>
using __with_error_t =
__if<__all_values_and_errors_nothrow_decay_copyable<_Self, _Env>,
completion_signatures<>, __with_exception_ptr>;
template <class _Self, class _Env>
using __completions_t = //
__try_make_completion_signatures<
__copy_cvref_t<_Self, _Sender>, _Env,
__try_make_completion_signatures<
schedule_result_t<_Scheduler>, _Env,
__with_error_t<_Self, _Env>,
__mconst<completion_signatures<>>>,
__decay_signature<set_value_t>, __decay_signature<set_error_t>>;
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> dependent_completion_signatures<_Env>;
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> __completions_t<_Self, _Env>
requires true;
friend const _Env& tag_invoke(get_env_t, const __t& __self) noexcept
{
return __self.__env_;
}
};
};
struct schedule_from_t
{
template <scheduler _Scheduler, sender _Sender>
requires tag_invocable<schedule_from_t, _Scheduler, _Sender>
auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
noexcept(nothrow_tag_invocable<schedule_from_t, _Scheduler, _Sender>)
-> tag_invoke_result_t<schedule_from_t, _Scheduler, _Sender>
{
return tag_invoke(*this, (_Scheduler&&)__sched, (_Sender&&)__sndr);
}
template <scheduler _Scheduler, sender _Sender>
auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
-> stdexec::__t<__sender<stdexec::__id<__decay_t<_Scheduler>>,
stdexec::__id<__decay_t<_Sender>>>>
{
return {{(_Scheduler&&)__sched}, (_Sender&&)__sndr};
}
};
} // namespace __schedule_from
using __schedule_from::schedule_from_t;
inline constexpr schedule_from_t schedule_from{};
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.transfer]
namespace __transfer
{
struct transfer_t
{
template <sender _Sender, scheduler _Scheduler>
requires __tag_invocable_with_completion_scheduler<
transfer_t, set_value_t, _Sender, _Scheduler>
tag_invoke_result_t<transfer_t,
__completion_scheduler_for<_Sender, set_value_t>,
_Sender, _Scheduler>
operator()(_Sender&& __sndr, _Scheduler&& __sched) const
noexcept(nothrow_tag_invocable<
transfer_t, __completion_scheduler_for<_Sender, set_value_t>,
_Sender, _Scheduler>)
{
auto csch = get_completion_scheduler<set_value_t>(get_env(__sndr));
return tag_invoke(transfer_t{}, std::move(csch), (_Sender&&)__sndr,
(_Scheduler&&)__sched);
}
template <sender _Sender, scheduler _Scheduler>
requires(!__tag_invocable_with_completion_scheduler<
transfer_t, set_value_t, _Sender, _Scheduler>) &&
tag_invocable<transfer_t, _Sender, _Scheduler>
tag_invoke_result_t<transfer_t, _Sender, _Scheduler>
operator()(_Sender&& __sndr, _Scheduler&& __sched) const
noexcept(nothrow_tag_invocable<transfer_t, _Sender, _Scheduler>)
{
return tag_invoke(transfer_t{}, (_Sender&&)__sndr,
(_Scheduler&&)__sched);
}
// NOT TO SPEC: permit non-typed senders:
template <sender _Sender, scheduler _Scheduler>
requires(!__tag_invocable_with_completion_scheduler<
transfer_t, set_value_t, _Sender, _Scheduler>) &&
(!tag_invocable<transfer_t, _Sender, _Scheduler>)
auto operator()(_Sender&& __sndr, _Scheduler&& __sched) const
{
return schedule_from((_Scheduler&&)__sched, (_Sender&&)__sndr);
}
template <scheduler _Scheduler>
__binder_back<transfer_t, __decay_t<_Scheduler>>
operator()(_Scheduler&& __sched) const
{
return {{}, {}, {(_Scheduler&&)__sched}};
}
};
} // namespace __transfer
using __transfer::transfer_t;
inline constexpr transfer_t transfer{};
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.on]
namespace __on
{
template <class _SchedulerId, class _SenderId, class _ReceiverId>
struct __operation;
template <class _SchedulerId, class _SenderId, class _ReceiverId>
struct __receiver_ref
{
using _Scheduler = stdexec::__t<_SchedulerId>;
using _Sender = stdexec::__t<_SenderId>;
using _Receiver = stdexec::__t<_ReceiverId>;
struct __t : receiver_adaptor<__t>
{
using __id = __receiver_ref;
stdexec::__t<__operation<_SchedulerId, _SenderId, _ReceiverId>>*
__op_state_;
_Receiver&& base() && noexcept
{
return (_Receiver&&)__op_state_->__rcvr_;
}
const _Receiver& base() const& noexcept
{
return __op_state_->__rcvr_;
}
auto get_env() const noexcept
-> __make_env_t<env_of_t<_Receiver>,
__with<get_scheduler_t, _Scheduler>>
{
return __make_env(
stdexec::get_env(this->base()),
__with_(get_scheduler, __op_state_->__scheduler_));
}
};
};
template <class _SchedulerId, class _SenderId, class _ReceiverId>
struct __receiver
{
using _Scheduler = stdexec::__t<_SchedulerId>;
using _Sender = stdexec::__t<_SenderId>;
using _Receiver = stdexec::__t<_ReceiverId>;
struct __t : receiver_adaptor<__t>
{
using __id = __receiver;
using __receiver_ref_t =
stdexec::__t<__receiver_ref<_SchedulerId, _SenderId, _ReceiverId>>;
stdexec::__t<__operation<_SchedulerId, _SenderId, _ReceiverId>>*
__op_state_;
_Receiver&& base() && noexcept
{
return (_Receiver&&)__op_state_->__rcvr_;
}
const _Receiver& base() const& noexcept
{
return __op_state_->__rcvr_;
}
void set_value() && noexcept
{
// cache this locally since *this is going bye-bye.
auto* __op_state = __op_state_;
try
{
// This line will invalidate *this:
start(__op_state->__data_.template emplace<1>(
__conv{[__op_state] {
return connect((_Sender&&)__op_state->__sndr_,
__receiver_ref_t{{}, __op_state});
}}));
}
catch (...)
{
set_error((_Receiver&&)__op_state->__rcvr_,
std::current_exception());
}
}
};
};
template <class _SchedulerId, class _SenderId, class _ReceiverId>
struct __operation
{
using _Scheduler = stdexec::__t<_SchedulerId>;
using _Sender = stdexec::__t<_SenderId>;
using _Receiver = stdexec::__t<_ReceiverId>;
struct __t
{
using __id = __operation;
using __receiver_t =
stdexec::__t<__receiver<_SchedulerId, _SenderId, _ReceiverId>>;
using __receiver_ref_t =
stdexec::__t<__receiver_ref<_SchedulerId, _SenderId, _ReceiverId>>;
friend void tag_invoke(start_t, __t& __self) noexcept
{
start(std::get<0>(__self.__data_));
}
template <class _Sender2, class _Receiver2>
__t(_Scheduler __sched, _Sender2&& __sndr, _Receiver2&& __rcvr) :
__scheduler_((_Scheduler&&)__sched), __sndr_((_Sender2&&)__sndr),
__rcvr_((_Receiver2&&)__rcvr),
__data_{std::in_place_index<0>, __conv{[this] {
return connect(schedule(__scheduler_), __receiver_t{{}, this});
}}}
{}
STDEXEC_IMMOVABLE(__t);
_Scheduler __scheduler_;
_Sender __sndr_;
_Receiver __rcvr_;
std::variant<
connect_result_t<schedule_result_t<_Scheduler>, __receiver_t>,
connect_result_t<_Sender, __receiver_ref_t>>
__data_;
};
};
template <class _SchedulerId, class _SenderId>
struct __sender
{
using _Scheduler = stdexec::__t<_SchedulerId>;
using _Sender = stdexec::__t<_SenderId>;
struct __t
{
using __id = __sender;
using is_sender = void;
template <class _ReceiverId>
using __receiver_ref_t =
stdexec::__t<__receiver_ref<_SchedulerId, _SenderId, _ReceiverId>>;
template <class _ReceiverId>
using __receiver_t =
stdexec::__t<__receiver<_SchedulerId, _SenderId, _ReceiverId>>;
template <class _ReceiverId>
using __operation_t =
stdexec::__t<__operation<_SchedulerId, _SenderId, _ReceiverId>>;
_Scheduler __scheduler_;
_Sender __sndr_;
template <__decays_to<__t> _Self, receiver _Receiver>
requires constructible_from<_Sender,
__copy_cvref_t<_Self, _Sender>> &&
sender_to<schedule_result_t<_Scheduler>,
__receiver_t<stdexec::__id<_Receiver>>> &&
sender_to<_Sender,
__receiver_ref_t<stdexec::__id<_Receiver>>>
friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr)
-> __operation_t<stdexec::__id<_Receiver>>
{
return {((_Self&&)__self).__scheduler_, ((_Self&&)__self).__sndr_,
(_Receiver&&)__rcvr};
}
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
-> env_of_t<const _Sender&>
{
return get_env(__self.__sndr_);
}
template <class...>
using __value_t = completion_signatures<>;
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> __try_make_completion_signatures<
schedule_result_t<_Scheduler>, _Env,
__try_make_completion_signatures<
__copy_cvref_t<_Self, _Sender>,
__make_env_t<_Env, __with<get_scheduler_t, _Scheduler>>,
completion_signatures<set_error_t(std::exception_ptr)>>,
__q<__value_t>>;
};
};
struct on_t
{
template <scheduler _Scheduler, sender _Sender>
requires tag_invocable<on_t, _Scheduler, _Sender>
auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
noexcept(nothrow_tag_invocable<on_t, _Scheduler, _Sender>)
-> tag_invoke_result_t<on_t, _Scheduler, _Sender>
{
return tag_invoke(*this, (_Scheduler&&)__sched, (_Sender&&)__sndr);
}
template <scheduler _Scheduler, sender _Sender>
auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
-> __t<__sender<stdexec::__id<__decay_t<_Scheduler>>,
stdexec::__id<__decay_t<_Sender>>>>
{
// connect-based customization will remove the need for this check
using __has_customizations =
__call_result_t<__has_algorithm_customizations_t, _Scheduler>;
static_assert(
!__has_customizations{},
"For now the default stdexec::on implementation doesn't support scheduling "
"onto schedulers that customize algorithms.");
return {(_Scheduler&&)__sched, (_Sender&&)__sndr};
}
};
} // namespace __on
using __on::on_t;
inline constexpr on_t on{};
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.transfer_just]
namespace __transfer_just
{
struct transfer_just_t
{
template <scheduler _Scheduler, __movable_value... _Values>
requires tag_invocable<transfer_just_t, _Scheduler, _Values...> &&
sender<tag_invoke_result_t<transfer_just_t, _Scheduler,
_Values...>>
auto operator()(_Scheduler&& __sched, _Values&&... __vals) const
noexcept(nothrow_tag_invocable<transfer_just_t, _Scheduler, _Values...>)
-> tag_invoke_result_t<transfer_just_t, _Scheduler, _Values...>
{
return tag_invoke(*this, (_Scheduler&&)__sched, (_Values&&)__vals...);
}
template <scheduler _Scheduler, __movable_value... _Values>
requires(!tag_invocable<transfer_just_t, _Scheduler, _Values...> ||
!sender<tag_invoke_result_t<transfer_just_t, _Scheduler,
_Values...>>)
auto operator()(_Scheduler&& __sched, _Values&&... __vals) const
-> decltype(transfer(just((_Values&&)__vals...), (_Scheduler&&)__sched))
{
return transfer(just((_Values&&)__vals...), (_Scheduler&&)__sched);
}
};
} // namespace __transfer_just
using __transfer_just::transfer_just_t;
inline constexpr transfer_just_t transfer_just{};
/////////////////////////////////////////////////////////////////////////////
// [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 _ReceiverId, class _Variant>
struct __receiver
{
using _Receiver = stdexec::__t<_ReceiverId>;
struct __t
{
using is_receiver = void;
using __id = __receiver;
using _Receiver = stdexec::__t<_ReceiverId>;
_Receiver __rcvr_;
// Customize set_value by building a variant and passing the result
// to the base class
template <same_as<set_value_t> _Tag, class... _As>
requires constructible_from<_Variant, std::tuple<_As&&...>>
friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept
{
try
{
set_value((_Receiver&&)__self.__rcvr_,
_Variant{std::tuple<_As&&...>{(_As&&)__as...}});
}
catch (...)
{
set_error((_Receiver&&)__self.__rcvr_,
std::current_exception());
}
}
template <same_as<set_error_t> _Tag, class _Error>
friend void tag_invoke(_Tag, __t&& __self, _Error&& __err) noexcept
{
set_error((_Receiver&&)__self.__rcvr_, (_Error&&)__err);
}
template <same_as<set_stopped_t> _Tag>
friend void tag_invoke(_Tag, __t&& __self) noexcept
{
set_stopped((_Receiver&&)__self.__rcvr_);
}
friend env_of_t<_Receiver> tag_invoke(get_env_t,
const __t& __self) noexcept
{
return get_env(__self.__rcvr_);
}
};
};
template <class _SenderId>
struct __sender
{
using _Sender = stdexec::__t<_SenderId>;
template <class _Env>
using __variant_t = __into_variant_result_t<_Sender, _Env>;
template <class _Receiver>
using __receiver_t = //
stdexec::__t<
__receiver<__id<_Receiver>, __variant_t<env_of_t<_Receiver>>>>;
struct __t
{
using __id = __sender;
using is_sender = void;
template <__decays_to<_Sender> _CvrefSender>
explicit __t(_CvrefSender&& __sndr) : __sndr_((_CvrefSender&&)__sndr)
{}
private:
template <class...>
using __value_t = completion_signatures<>;
template <class _Env>
using __compl_sigs = //
make_completion_signatures<
_Sender, _Env,
completion_signatures<set_value_t(__variant_t<_Env>),
set_error_t(std::exception_ptr)>,
__value_t>;
_Sender __sndr_;
template <receiver _Receiver>
requires sender_to<_Sender, __receiver_t<_Receiver>>
friend auto tag_invoke(connect_t, __t&& __self, _Receiver __rcvr) //
noexcept(__nothrow_connectable<_Sender, __receiver_t<_Receiver>>)
-> connect_result_t<_Sender, __receiver_t<_Receiver>>
{
return stdexec::connect(
(_Sender&&)__self.__sndr_,
__receiver_t<_Receiver>{(_Receiver&&)__rcvr});
}
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
-> env_of_t<const _Sender&>
{
return get_env(__self.__sndr_);
}
template <class _Env>
friend auto tag_invoke(get_completion_signatures_t, __t&&, _Env&&) //
-> __compl_sigs<_Env>;
};
};
struct into_variant_t
{
template <sender _Sender>
auto operator()(_Sender&& __sndr) const
-> __t<__sender<stdexec::__id<__decay_t<_Sender>>>>
{
return __t<__sender<stdexec::__id<__decay_t<_Sender>>>>{
(_Sender&&)__sndr};
}
auto operator()() const noexcept
{
return __binder_back<into_variant_t>{};
}
};
} // namespace __into_variant
using __into_variant::into_variant_t;
inline constexpr into_variant_t into_variant{};
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.when_all]
// [execution.senders.adaptors.when_all_with_variant]
namespace __when_all
{
enum __state_t
{
__started,
__error,
__stopped
};
struct __on_stop_requested
{
in_place_stop_source& __stop_source_;
void operator()() noexcept
{
__stop_source_.request_stop();
}
};
template <class _Env>
auto __make_env(_Env&& __env, in_place_stop_source& __stop_source) noexcept
{
return __env::__join_env(__env::__env_fn{[&](get_stop_token_t) noexcept {
return __stop_source.get_token();
}},
(_Env&&)__env);
}
template <class _Env>
using __env_t = //
decltype(__when_all::__make_env(__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> &&
__valid<__value_types_of_t, _Sender, _Env,
__mconst<int>, __msingle_or<void>>;
template <class _Env, class _Sender>
using __single_values_of_t = //
__try_value_types_of_t<_Sender, _Env,
__transform<__q<__decay_rvalue_ref>, __q<__types>>,
__q<__msingle>>;
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... _SenderIds>
using __all_value_and_error_args_nothrow_decay_copyable = //
__mand< //
__mand<__try_value_types_of_t<__t<_SenderIds>, _Env,
__q<__all_nothrow_decay_copyable>,
__q<__mand>>...>,
__mand<__try_error_types_of_t<__t<_SenderIds>, _Env,
__q<__all_nothrow_decay_copyable>>...>>;
template <class _Env, class... _Senders>
using __completions_t = //
__concat_completion_signatures_t<
__if<__all_value_and_error_args_nothrow_decay_copyable<
_Env, __id<_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>
struct __complete_fn
{
_Receiver& __rcvr_;
__complete_fn(_Tag, _Receiver& __rcvr) noexcept : __rcvr_(__rcvr) {}
template <class _Ty, class... _Ts>
void operator()(_Ty& __t, _Ts&... __ts) const noexcept
{
if constexpr (!same_as<_Ty, __not_an_error>)
{
_Tag{}((_Receiver&&)__rcvr_, (_Ty&&)__t, (_Ts&&)__ts...);
}
}
void operator()() const noexcept
{
_Tag{}((_Receiver&&)__rcvr_);
}
};
template <class _Receiver, class _ValuesTuple>
void __set_values(_Receiver& __rcvr, _ValuesTuple& __values) noexcept
{
std::apply(
[&](auto&... __opt_vals) noexcept -> void {
std::apply(__complete_fn{set_value, __rcvr},
std::tuple_cat(std::apply(__tie_fn{}, *__opt_vals)...));
},
__values);
}
template <class _ReceiverId, class _ValuesTuple, class _ErrorsVariant>
struct __operation_base : __immovable
{
using _Receiver = stdexec::__t<_ReceiverId>;
void __arrive() noexcept
{
if (0 == --__count_)
{
__complete();
}
}
void __complete() 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:;
}
}
_Receiver __rcvr_;
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_NO_UNIQUE_ADDRESS _ValuesTuple __values_{};
std::optional<typename stop_token_of_t<
env_of_t<_Receiver>&>::template callback_type<__on_stop_requested>>
__on_stop_{};
};
template <std::size_t _Index, class _ReceiverId, class _ValuesTuple,
class _ErrorsVariant>
struct __receiver
{
using _Receiver = stdexec::__t<_ReceiverId>;
template <class _Tuple>
using __tuple_type =
typename std::tuple_element_t<_Index, _Tuple>::value_type;
using _TupleType =
__minvoke<__with_default<__q<__tuple_type>, __ignore>, _ValuesTuple>;
struct __t
{
using is_receiver = void;
using __id = __receiver;
template <class _Error>
void __set_error(_Error&& __err) noexcept
{
// TODO: What memory orderings are actually needed here?
if (__error != __op_state_->__state_.exchange(__error))
{
__op_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>)
{
__op_state_->__errors_.template emplace<__decay_t<_Error>>(
(_Error&&)__err);
}
else
{
try
{
__op_state_->__errors_
.template emplace<__decay_t<_Error>>(
(_Error&&)__err);
}
catch (...)
{
__op_state_->__errors_
.template emplace<std::exception_ptr>(
std::current_exception());
}
}
}
}
template <same_as<set_value_t> _Tag, class... _Values>
requires same_as<_ValuesTuple, __ignore> ||
constructible_from<_TupleType, _Values...>
friend void tag_invoke(_Tag, __t&& __self, _Values&&... __vals) noexcept
{
if constexpr (!same_as<_ValuesTuple, __ignore>)
{
static_assert(
same_as<_TupleType, std::tuple<__decay_t<_Values>...>>,
"One of the senders in this when_all() is fibbing about what types it sends");
// We only need to bother recording the completion values
// if we're not already in the "error" or "stopped" state.
if (__self.__op_state_->__state_ == __started)
{
if constexpr ((__nothrow_decay_copyable<_Values> && ...))
{
std::get<_Index>(__self.__op_state_->__values_)
.emplace((_Values&&)__vals...);
}
else
{
try
{
std::get<_Index>(__self.__op_state_->__values_)
.emplace((_Values&&)__vals...);
}
catch (...)
{
__self.__set_error(std::current_exception());
}
}
}
}
__self.__op_state_->__arrive();
}
template <same_as<set_error_t> _Tag, class _Error>
requires requires(_ErrorsVariant& __errors, _Error&& __err) {
__errors.template emplace<__decay_t<_Error>>(
(_Error&&)__err);
}
friend void tag_invoke(_Tag, __t&& __self, _Error&& __err) noexcept
{
__self.__set_error((_Error&&)__err);
__self.__op_state_->__arrive();
}
template <same_as<set_stopped_t> _Tag>
requires receiver_of<_Receiver, completion_signatures<_Tag()>>
friend void tag_invoke(_Tag, __t&& __self) noexcept
{
__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 (__self.__op_state_->__state_.compare_exchange_strong(__expected,
__stopped))
{
__self.__op_state_->__stop_source_.request_stop();
}
__self.__op_state_->__arrive();
}
friend __env_t<env_of_t<_Receiver>>
tag_invoke(get_env_t, const __t& __self) noexcept
{
return __when_all::__make_env(get_env(__self.__op_state_->__rcvr_),
__self.__op_state_->__stop_source_);
}
__operation_base<_ReceiverId, _ValuesTuple, _ErrorsVariant>*
__op_state_;
};
};
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_
{
using __completions = __completions_t<__env_t<_Env>, _Senders...>;
// 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_value_and_error_args_nothrow_decay_copyable<
_Env, __id<_Senders>...>,
__error_types,
__minvoke<__push_back_unique<__q<std::variant>>, __error_types,
std::exception_ptr>>;
};
template <receiver _Receiver,
__max1_sender<__env_t<env_of_t<_Receiver>>>... _Senders>
struct __traits : __traits_<env_of_t<_Receiver>, _Senders...>
{
using _Traits = __traits_<env_of_t<_Receiver>, _Senders...>;
using typename _Traits::__completions;
using typename _Traits::__errors_variant;
using typename _Traits::__values_tuple;
template <std::size_t _Index>
using __receiver =
__t<__when_all::__receiver<_Index, __id<_Receiver>, __values_tuple,
__errors_variant>>;
using __operation_base =
__when_all::__operation_base<__id<_Receiver>, __values_tuple,
__errors_variant>;
template <class _Sender, class _Index>
using __op_state = connect_result_t<_Sender, __receiver<__v<_Index>>>;
template <class _Tuple = __q<std::tuple>>
using __op_states_tuple = //
__minvoke<__mzip_with2<__q<__op_state>, _Tuple>, __types<_Senders...>,
__mindex_sequence_for<_Senders...>>;
};
template <class _Cvref, class _ReceiverId, class... _SenderIds>
using __traits_ex =
__traits<__t<_ReceiverId>, __minvoke<_Cvref, __t<_SenderIds>>...>;
template <class _Cvref, class _ReceiverId, class... _SenderIds>
using __op_states_tuple_ex =
typename __traits_ex<_Cvref, _ReceiverId,
_SenderIds...>::template __op_states_tuple<>;
template <class _Cvref, class _ReceiverId, class... _SenderIds>
requires __valid<__op_states_tuple_ex, _Cvref, _ReceiverId, _SenderIds...>
struct __operation
{
using _Receiver = stdexec::__t<_ReceiverId>;
using _Traits = __traits_ex<_Cvref, _ReceiverId, _SenderIds...>;
using _Indices = std::index_sequence_for<_SenderIds...>;
using __operation_base_t = typename _Traits::__operation_base;
using __op_states_tuple_t =
__op_states_tuple_ex<_Cvref, _ReceiverId, _SenderIds...>;
template <std::size_t _Index>
using __receiver_t = typename _Traits::template __receiver<_Index>;
struct __t : __operation_base_t
{
using __id = __operation;
template <class _SendersTuple, std::size_t... _Is>
__t(_SendersTuple&& __sndrs, _Receiver __rcvr,
std::index_sequence<_Is...>) :
__operation_base_t{{}, (_Receiver&&)__rcvr, {sizeof...(_Is)}},
__op_states_{__conv{[&__sndrs, this]() {
return stdexec::connect(std::get<_Is>((_SendersTuple&&)__sndrs),
__receiver_t<_Is>{this});
}}...}
{}
template <class _SendersTuple>
__t(_SendersTuple&& __sndrs, _Receiver __rcvr) :
__t((_SendersTuple&&)__sndrs, (_Receiver&&)__rcvr, _Indices{})
{}
friend void tag_invoke(start_t, __t& __self) noexcept
{
// register stop callback:
__self.__on_stop_.emplace(
get_stop_token(get_env(__self.__rcvr_)),
__on_stop_requested{__self.__stop_source_});
if (__self.__stop_source_.stop_requested())
{
// Stop has already been requested. Don't bother starting
// the child operations.
stdexec::set_stopped((_Receiver&&)__self.__rcvr_);
}
else
{
std::apply(
[](auto&... __child_ops) noexcept -> void {
(stdexec::start(__child_ops), ...);
},
__self.__op_states_);
if constexpr (sizeof...(_SenderIds) == 0)
{
__self.__complete();
}
}
}
__op_states_tuple_t __op_states_;
};
};
template <class _From, class _ToId>
using __cvref_id = __copy_cvref_t<_From, __t<_ToId>>;
template <class _Indices, class... _SenderIds>
struct __sender;
template <std::size_t... _Indices, class... _SenderIds>
struct __sender<std::index_sequence<_Indices...>, _SenderIds...>
{
template <class _Self, class _Env>
using __completions_t =
typename __traits_<_Env,
__cvref_id<_Self, _SenderIds>...>::__completions;
template <class _Self, class _Receiver, std::size_t _Index>
using __receiver_t =
typename __traits<_Receiver, __cvref_id<_Self, _SenderIds>...>::
template __receiver<_Index>;
template <class _Self, class _Receiver>
using __operation_t =
stdexec::__t<__operation<__copy_cvref_fn<_Self>,
stdexec::__id<_Receiver>, _SenderIds...>>;
struct __t
{
using __id = __sender;
using is_sender = void;
template <class... _Sndrs>
explicit(sizeof...(_Sndrs) == 1) __t(_Sndrs&&... __sndrs) :
__sndrs_((_Sndrs&&)__sndrs...)
{}
private:
template <__decays_to<__t> _Self, receiver _Receiver>
requires(sender_to<__cvref_id<_Self, _SenderIds>,
__receiver_t<_Self, _Receiver, _Indices>> &&
...)
friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr)
-> __operation_t<_Self, _Receiver>
{
return {((_Self&&)__self).__sndrs_, (_Receiver&&)__rcvr};
}
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> dependent_completion_signatures<_Env>;
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> __completions_t<_Self, _Env>
requires true;
friend empty_env tag_invoke(get_env_t, const __t&) noexcept
{
return {};
}
std::tuple<stdexec::__t<_SenderIds>...> __sndrs_;
};
};
template <class _Sender>
using __into_variant_result_t = decltype(into_variant(__declval<_Sender>()));
struct when_all_t
{
template <class... _Senders>
using __sender_t = __t<__sender<std::index_sequence_for<_Senders...>,
__id<__decay_t<_Senders>>...>>;
template <sender... _Senders>
requires tag_invocable<when_all_t, _Senders...> &&
sender<tag_invoke_result_t<when_all_t, _Senders...>>
auto operator()(_Senders&&... __sndrs) const
noexcept(nothrow_tag_invocable<when_all_t, _Senders...>)
-> tag_invoke_result_t<when_all_t, _Senders...>
{
return tag_invoke(*this, (_Senders&&)__sndrs...);
}
template <sender... _Senders>
requires(!tag_invocable<when_all_t, _Senders...>) &&
sender<__sender_t<_Senders...>>
__sender_t<_Senders...> operator()(_Senders&&... __sndrs) const
{
return __sender_t<_Senders...>{(_Senders&&)__sndrs...};
}
};
struct when_all_with_variant_t
{
template <sender... _Senders>
requires tag_invocable<when_all_with_variant_t, _Senders...> &&
sender<
tag_invoke_result_t<when_all_with_variant_t, _Senders...>>
auto operator()(_Senders&&... __sndrs) const
noexcept(nothrow_tag_invocable<when_all_with_variant_t, _Senders...>)
-> tag_invoke_result_t<when_all_with_variant_t, _Senders...>
{
return tag_invoke(*this, (_Senders&&)__sndrs...);
}
template <sender... _Senders>
requires(!tag_invocable<when_all_with_variant_t, _Senders...>) &&
(__callable<into_variant_t, _Senders> && ...)
auto operator()(_Senders&&... __sndrs) const
{
return when_all_t{}(into_variant((_Senders&&)__sndrs)...);
}
};
struct transfer_when_all_t
{
template <scheduler _Sched, sender... _Senders>
requires tag_invocable<transfer_when_all_t, _Sched, _Senders...> &&
sender<tag_invoke_result_t<transfer_when_all_t, _Sched,
_Senders...>>
auto operator()(_Sched&& __sched, _Senders&&... __sndrs) const noexcept(
nothrow_tag_invocable<transfer_when_all_t, _Sched, _Senders...>)
-> tag_invoke_result_t<transfer_when_all_t, _Sched, _Senders...>
{
return tag_invoke(*this, (_Sched&&)__sched, (_Senders&&)__sndrs...);
}
template <scheduler _Sched, sender... _Senders>
requires((!tag_invocable<transfer_when_all_t, _Sched, _Senders...>) ||
(!sender<tag_invoke_result_t<transfer_when_all_t, _Sched,
_Senders...>>))
auto operator()(_Sched&& __sched, _Senders&&... __sndrs) const
{
return transfer(when_all_t{}((_Senders&&)__sndrs...),
(_Sched&&)__sched);
}
};
struct transfer_when_all_with_variant_t
{
template <scheduler _Sched, sender... _Senders>
requires tag_invocable<transfer_when_all_with_variant_t, _Sched,
_Senders...> &&
sender<tag_invoke_result_t<transfer_when_all_with_variant_t,
_Sched, _Senders...>>
auto operator()(_Sched&& __sched, _Senders&&... __sndrs) const
noexcept(nothrow_tag_invocable<transfer_when_all_with_variant_t, _Sched,
_Senders...>)
-> tag_invoke_result_t<transfer_when_all_with_variant_t, _Sched,
_Senders...>
{
return tag_invoke(*this, (_Sched&&)__sched, (_Senders&&)__sndrs...);
}
template <scheduler _Sched, sender... _Senders>
requires(!tag_invocable<transfer_when_all_with_variant_t, _Sched,
_Senders...>) &&
(__callable<into_variant_t, _Senders> && ...)
auto operator()(_Sched&& __sched, _Senders&&... __sndrs) const
{
return transfer_when_all_t{}((_Sched&&)__sched,
into_variant((_Senders&&)__sndrs)...);
}
};
} // 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{};
namespace __read
{
template <class _Tag, class _ReceiverId>
struct __operation
{
using _Receiver = stdexec::__t<_ReceiverId>;
struct __t : __immovable
{
using __id = __operation;
_Receiver __rcvr_;
friend void tag_invoke(start_t, __t& __self) noexcept
{
try
{
auto __env = get_env(__self.__rcvr_);
set_value(std::move(__self.__rcvr_), _Tag{}(__env));
}
catch (...)
{
set_error(std::move(__self.__rcvr_), std::current_exception());
}
}
};
};
template <class _Tag>
struct __sender
{
using __t = __sender;
using __id = __sender;
using is_sender = void;
template <class _Env>
requires __callable<_Tag, _Env>
using __completions_t = //
completion_signatures<set_value_t(__call_result_t<_Tag, _Env>),
set_error_t(std::exception_ptr)>;
template <class _Receiver>
requires receiver_of<_Receiver, __completions_t<env_of_t<_Receiver>>>
friend auto tag_invoke(connect_t, __sender, _Receiver __rcvr) //
noexcept(std::is_nothrow_move_constructible_v<_Receiver>)
-> stdexec::__t<__operation<_Tag, stdexec::__id<_Receiver>>>
{
return {{}, (_Receiver&&)__rcvr};
}
template <class _Env>
friend auto tag_invoke(get_completion_signatures_t, __sender, _Env&&)
-> dependent_completion_signatures<_Env>;
template <__none_of<no_env> _Env>
friend auto tag_invoke(get_completion_signatures_t, __sender, _Env&&)
-> __completions_t<_Env>;
friend empty_env tag_invoke(get_env_t, const __t&) noexcept
{
return {};
}
};
struct __read_t
{
template <class _Tag>
constexpr __sender<_Tag> operator()(_Tag) const noexcept
{
return {};
}
};
} // namespace __read
inline constexpr __read::__read_t read{};
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.consumers.sync_wait]
// [execution.senders.consumers.sync_wait_with_variant]
namespace __sync_wait
{
template <class _Sender>
using __into_variant_result_t = decltype(into_variant(__declval<_Sender>()));
inline auto __make_env(run_loop& __loop) noexcept
{
return __env::__env_fn{
[&](__one_of<get_scheduler_t,
get_delegatee_scheduler_t> auto) noexcept {
return __loop.get_scheduler();
}};
}
using __env = decltype(__sync_wait::__make_env(__declval<run_loop&>()));
// What should sync_wait(just_stopped()) return?
template <sender_in<__env> _Sender, class _Continuation>
using __sync_wait_result_impl = __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 =
__sync_wait_result_t<__into_variant_result_t<_Sender>>;
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 is_receiver = void;
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 __sync_wait::__make_env(*__rcvr.__loop_);
}
};
};
template <class _Sender>
using __into_variant_result_t = decltype(into_variant(__declval<_Sender>()));
struct sync_wait_t;
using _Sender = __0;
using __cust_sigs =
__types<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)>;
template <class _Sender>
inline constexpr bool __is_sync_wait_customized =
__minvocable<__which<__cust_sigs>, _Sender>;
template <class _Sender>
using __receiver_t = __t<__sync_wait_result_impl<_Sender, __q<__receiver>>>;
struct __default_impl
{
template <class _Sender>
auto operator()(_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_));
}
};
template <class _Sender>
using __dispatcher_for =
__make_dispatcher<__cust_sigs, __mconst<__default_impl>, _Sender>;
// 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>>;
////////////////////////////////////////////////////////////////////////////
// [execution.senders.consumers.sync_wait]
struct sync_wait_t
{
template <sender_in<__env> _Sender>
requires __satisfies<__single_value_variant_sender<_Sender, __env>> &&
(sender_to<_Sender, __sync_receiver_for_t<_Sender>> ||
__is_sync_wait_customized<_Sender>)
auto operator()(_Sender&& __sndr) const
-> std::optional<__value_tuple_for_t<_Sender>>
{
// The selected implementation should return void
return __dispatcher_for<_Sender>{}((_Sender&&)__sndr);
}
};
////////////////////////////////////////////////////////////////////////////
// [execution.senders.consumers.sync_wait_with_variant]
struct sync_wait_with_variant_t
{
template <sender_in<__env> _Sender>
requires __tag_invocable_with_completion_scheduler<
sync_wait_with_variant_t, set_value_t, _Sender>
tag_invoke_result_t<sync_wait_with_variant_t,
__completion_scheduler_for<_Sender, set_value_t>,
_Sender>
operator()(_Sender&& __sndr) const
noexcept(nothrow_tag_invocable<
sync_wait_with_variant_t,
__completion_scheduler_for<_Sender, set_value_t>, _Sender>)
{
static_assert(
std::is_same_v<
tag_invoke_result_t<
sync_wait_with_variant_t,
__completion_scheduler_for<_Sender, set_value_t>, _Sender>,
std::optional<__sync_wait_with_variant_result_t<_Sender>>>,
"The type of tag_invoke(sync_wait_with_variant, get_completion_scheduler, S) "
"must be sync-wait-with-variant-type<S, sync-wait-env>");
auto __sched = get_completion_scheduler<set_value_t>(get_env(__sndr));
return tag_invoke(sync_wait_with_variant_t{}, std::move(__sched),
(_Sender&&)__sndr);
}
template <sender_in<__env> _Sender>
requires(!__tag_invocable_with_completion_scheduler<
sync_wait_with_variant_t, set_value_t, _Sender>) &&
tag_invocable<sync_wait_with_variant_t, _Sender>
tag_invoke_result_t<sync_wait_with_variant_t, _Sender>
operator()(_Sender&& __sndr) const
noexcept(nothrow_tag_invocable<sync_wait_with_variant_t, _Sender>)
{
static_assert(
std::is_same_v<
tag_invoke_result_t<sync_wait_with_variant_t, _Sender>,
std::optional<__sync_wait_with_variant_result_t<_Sender>>>,
"The type of tag_invoke(sync_wait_with_variant, S) "
"must be sync-wait-with-variant-type<S, sync-wait-env>");
return tag_invoke(sync_wait_with_variant_t{}, (_Sender&&)__sndr);
}
template <sender_in<__env> _Sender>
requires(!__tag_invocable_with_completion_scheduler<
sync_wait_with_variant_t, set_value_t, _Sender>) &&
(!tag_invocable<sync_wait_with_variant_t, _Sender>) &&
invocable<sync_wait_t, __into_variant_result_t<_Sender>>
std::optional<__sync_wait_with_variant_result_t<_Sender>>
operator()(_Sender&& __sndr) const
{
return sync_wait_t{}(into_variant((_Sender&&)__sndr));
}
};
} // 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 is_sender = void;
template <sender _Sender>
constexpr __ignore_sender(_Sender&&) noexcept
{}
};
template <auto _Reason = "You cannot pipe one sender into another."__csz>
struct _CANNOT_PIPE_INTO_A_SENDER_
{};
template <class _Sender>
struct _WITH_SENDER_
{};
template <class _Sender>
using __bad_pipe_sink_t =
__mexception<_CANNOT_PIPE_INTO_A_SENDER_<>, _WITH_SENDER_<_Sender>>;
} // namespace stdexec
// 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"
#ifdef __EDG__
#pragma diagnostic pop
#endif
STDEXEC_PRAGMA_POP()