Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 74 additions & 8 deletions include/stdexec/__detail/__completion_behavior.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ namespace STDEXEC
// __get_completion_behavior
struct __completion_behavior
{
//private:
// private:
template <__completion_tag _Tag>
friend struct __get_completion_behavior_t;
friend struct __completion_info;

enum __flag : std::uint8_t
{
Expand All @@ -63,6 +64,50 @@ namespace STDEXEC
__unknown = __not_affine_ | __async_ | __inline_
};

// For use when computing the completion behavior of two senders when run in parallel.
//
// __asynchronous | __asynchronous_affine => __async_ (aka __asynchronous)
// __asynchronous | __inline_completion => __async_ | __inline_ | __not_affine (aka __unknown)
// __asynchronous_affine | __inline_completion => __async_ | __inline_
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
friend constexpr auto operator|(__behavior __left, __behavior __right) noexcept //
-> __behavior
{
return __behavior(std::uint8_t(__left) | std::uint8_t(__right));
}

STDEXEC_ATTRIBUTE(always_inline, host, device)
friend constexpr auto operator|=(__behavior &__left, __behavior __right) noexcept //
-> __behavior &
{
return (__left = (__left | __right));
}

// For use when computing the completion behavior of two senders when run in sequence.
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
friend constexpr auto operator&(__behavior __left, __behavior __right) noexcept //
-> __behavior
{
// Two senders in sequence can only complete inline if both senders can complete
// inline.
auto const __possibly_inline = std::uint8_t(__left) & std::uint8_t(__right) & __inline_;
// Two senders in sequence can complete in a different context if either sender can
// complete in a different context.
auto const __not_affine = (std::uint8_t(__left) | std::uint8_t(__right)) & __not_affine_;
// Two senders in sequence can complete asynchronously if either sender can complete
// asynchronously.
auto const __possibly_async = (std::uint8_t(__left) | std::uint8_t(__right)) & __async_;

return __behavior(__possibly_inline | __not_affine | __possibly_async);
}

STDEXEC_ATTRIBUTE(always_inline, host, device)
friend constexpr auto operator&=(__behavior &__left, __behavior __right) noexcept //
-> __behavior &
{
return (__left = (__left & __right));
}

template <__behavior _CB>
using __constant_t = std::integral_constant<__behavior, _CB>;

Expand All @@ -81,15 +126,37 @@ namespace STDEXEC
static constexpr __asynchronous_affine_t __asynchronous_affine{};
static constexpr __inline_completion_t __inline_completion{};

// __asynchronous | __asynchronous_affine => __async_ (aka __asynchronous)
// __asynchronous | __inline_completion => __async_ | __inline_ | __not_affine (aka __unknown)
// __asynchronous_affine | __inline_completion => __async_ | __inline_
//private:
template <__behavior _CB>
static constexpr auto __reify() noexcept
{
if constexpr (_CB == __behavior::__asynchronous)
return __asynchronous;
else if constexpr (_CB == __behavior::__asynchronous_affine)
return __asynchronous_affine;
else if constexpr (_CB == __behavior::__inline_completion)
return __inline_completion;
else if constexpr (_CB == __behavior::__unknown)
return __unknown;
else
return __constant_t<_CB>{};
}

public:
// For use when computing the completion behavior of two senders when run in parallel.
template <__behavior _Left, __behavior _Right>
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
friend constexpr auto operator|(__constant_t<_Left>, __constant_t<_Right>) noexcept
{
return __constant_t<static_cast<__behavior>(static_cast<std::uint8_t>(_Left)
| static_cast<std::uint8_t>(_Right))>();
return __reify<_Left | _Right>();
}

// For use when computing the completion behavior of two senders when run in sequence.
template <__behavior _Left, __behavior _Right>
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
friend constexpr auto operator&(__constant_t<_Left>, __constant_t<_Right>) noexcept
{
return __reify<_Left & _Right>();
}

template <__behavior _Left, __behavior _Right>
Expand Down Expand Up @@ -123,11 +190,10 @@ namespace STDEXEC
struct __common_t
{
template <__behavior... _CSs>
requires(sizeof...(_CSs) > 0)
STDEXEC_ATTRIBUTE(nodiscard, host, device)
constexpr auto operator()(__constant_t<_CSs>... __cbs) const noexcept
{
return (__cbs | ...);
return (__cbs | ... | __constant_t<__behavior(0)>());
}
};

Expand Down
36 changes: 33 additions & 3 deletions include/stdexec/__detail/__completion_info.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "__completion_behavior.hpp"
#include "__completion_signatures.hpp"
#include "__meta.hpp"
#include "__ranges.hpp"
#include "__static_vector.hpp"
#include "__typeinfo.hpp"

Expand All @@ -45,14 +46,14 @@ namespace STDEXEC
STDEXEC::__disposition __disposition = __invalid_disposition;
__type_index __signature = __mtypeid<void>;
__type_index __domain = __mtypeid<void>;
__behavior_t __behavior = __completion_behavior::__unknown;
__behavior_t __behavior = __behavior_t(0);

__completion_info() = default;

template <__completion_tag _Tag, class... _Args>
constexpr __completion_info(_Tag (*)(_Args...),
__type_index __domain = __mtypeid<void>,
__behavior_t __behavior = __completion_behavior::__unknown) noexcept
__behavior_t __behavior = __behavior_t(0)) noexcept
: __disposition(_Tag::__disposition)
, __signature(__mtypeid<_Tag(_Args...)>)
, __domain(__domain)
Expand Down Expand Up @@ -126,6 +127,17 @@ namespace STDEXEC
}
}();

template <std::ranges::forward_range auto _Range, class _Fn, class... _Args>
constexpr auto __range_apply(_Fn &&__fn, _Args &&...args) noexcept
{
auto __impl = [&]<std::size_t... _Is>(__indices<_Is...>)
{
return static_cast<_Fn &&>(__fn).template
operator()<(*std::ranges::next(_Range.begin(), _Is))...>(std::forward<_Args>(args)...);
};
return __impl(__make_indices<std::ranges::size(_Range)>());
}

template <class _GetComplInfo>
consteval auto __completion_sigs_from(_GetComplInfo) noexcept
{
Expand All @@ -142,13 +154,31 @@ namespace STDEXEC

template <class... _Sigs>
[[nodiscard]]
consteval auto __to_array(completion_signatures<_Sigs...>) noexcept
consteval auto __reflect(completion_signatures<_Sigs...>) noexcept
{
using __array_t = __static_vector<__completion_info, sizeof...(_Sigs)>;
auto __compls = __array_t{__completion_info(__signature<_Sigs>)...};
std::ranges::sort(__compls);
return __compls;
}

template <auto>
extern int __splice_v;

template <auto _Info>
using __splice_t = decltype(__splice_v<_Info>);

template <__same_as<__completion_info> auto _Info>
extern __fn_ptr_t<__tuple<__msplice<_Info.__signature>,
__msplice<_Info.__domain>,
__mconstant<_Info.__behavior>>>
__splice_v<_Info>;

template <std::ranges::forward_range auto _Info>
requires __same_as<std::ranges::range_value_t<decltype(_Info)>, __completion_info>
constexpr auto __splice_v<_Info> = __with_indices<_Info.size()>()(
[]<std::size_t... _Is>() { return __fn_ptr_t<__tuple<__splice_t<_Info[_Is]>...>>(); });

} // namespace __cmplsigs
} // namespace STDEXEC

Expand Down
9 changes: 8 additions & 1 deletion include/stdexec/__detail/__completion_signatures.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ namespace STDEXEC
//! tag.
template <__completion_tag _Tag>
[[nodiscard]]
static consteval auto __select(_Tag) noexcept
static consteval auto __select(_Tag = _Tag()) noexcept
{
if constexpr (_Tag{} == set_value)
{
Expand Down Expand Up @@ -560,6 +560,12 @@ namespace STDEXEC
return _ID; \
} else

# define STDEXEC_IF_OK_OR(_ID, ...) \
if constexpr (STDEXEC::__merror<decltype(_ID)>) \
{ \
return __VA_ARGS__; \
} else

template <class, class _Sndr>
[[nodiscard]]
consteval auto __throw_dependent_sender_error_r() noexcept -> __dependent_sender_error_t<_Sndr>
Expand All @@ -577,6 +583,7 @@ namespace STDEXEC
#else // ^^^ no constexpr exceptions ^^^ / vvv constexpr exceptions vvv

# define STDEXEC_IF_OK(_ID)
# define STDEXEC_IF_OK_OR(_ID, ...)

template <class _Result, class _Sndr>
[[noreturn, nodiscard]]
Expand Down
65 changes: 33 additions & 32 deletions include/stdexec/__detail/__config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -658,38 +658,6 @@ namespace STDEXEC
# define STDEXEC_IF_NOT_CONSTEVAL STDEXEC_IF_CONSTEVAL {} else
#endif

#if defined(STDEXEC_ASSERT)
// nothing to do, user has provided their own assertion macro
#elif defined(STDEXEC_ASSERT_FN)
// legacy way to customize assertions, still supported for backward compatibility
# define STDEXEC_ASSERT(_XP) STDEXEC_ASSERT_FN(_XP)
#else
# define STDEXEC_ASSERT(_XP) \
do \
{ \
STDEXEC_IF_CONSTEVAL \
{ \
if (!(_XP)) \
STDEXEC::__throw_assertion_failure(); \
} \
else \
{ \
assert(_XP); \
} \
} while (false)
#endif

namespace STDEXEC
{
struct __assertion_failure
{};

inline void __throw_assertion_failure()
{
throw __assertion_failure{};
}
} // namespace STDEXEC

#define STDEXEC_AUTO_RETURN(...) \
noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) { \
return __VA_ARGS__; \
Expand Down Expand Up @@ -807,6 +775,39 @@ namespace STDEXEC
}
} // namespace STDEXEC

#if defined(STDEXEC_ASSERT)
// nothing to do, user has provided their own assertion macro
#elif defined(STDEXEC_ASSERT_FN)
// legacy way to customize assertions, still supported for backward compatibility
# define STDEXEC_ASSERT(_XP) STDEXEC_ASSERT_FN(_XP)
#else
# define STDEXEC_ASSERT(_XP) \
do \
{ \
STDEXEC_IF_CONSTEVAL \
{ \
if (!(_XP)) \
STDEXEC::__throw_assertion_failure(); \
} \
else \
{ \
assert(_XP); \
} \
} while (false)
#endif

namespace STDEXEC
{
struct __assertion_failure
{};

STDEXEC_ATTRIBUTE(host, device)
inline void __throw_assertion_failure()
{
STDEXEC_THROW(__assertion_failure{});
}
} // namespace STDEXEC

///////////////////////////////////////////////////////////////////////////////
/// To hook a customization point like STDEXEC::connect, define a member
/// function like this:
Expand Down
4 changes: 2 additions & 2 deletions include/stdexec/__detail/__domain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,8 @@ namespace STDEXEC
{
using __domain_t = __call_result_t<__read_query_t, _Attrs const &, _Env const &...>;
return __check_domain<_Attrs, _Env...>(__domain_t{});
// Otherwise, if _Tag is void, fall back to querying for the set_value_t completion domain:
}
// Otherwise, if _Tag is void, fall back to querying for the set_value_t completion domain:
else if constexpr (__same_as<_Tag, void>)
{
if constexpr (__callable<get_completion_domain_t<set_value_t>,
Expand All @@ -344,7 +344,7 @@ namespace STDEXEC
{
using __sch_t =
__call_result_t<get_completion_scheduler_t<_Tag>, _Attrs const &, _Env const &...>;
using __read_query_t = typename get_completion_domain_t<set_value_t>::__read_query_t;
using __read_query_t = get_completion_domain_t<set_value_t>::__read_query_t;

if constexpr (__callable<__read_query_t, __sch_t, _Env const &...>)
{
Expand Down
33 changes: 30 additions & 3 deletions include/stdexec/__detail/__env.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")

namespace STDEXEC
{
//////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
// [exec.envs]
namespace __env
{
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
// cprop
template <class _Query, auto _Value>
struct cprop
Expand Down Expand Up @@ -155,6 +155,33 @@ namespace STDEXEC
return __join(__root_env{}, static_cast<std::unwrap_reference_t<_Env> &&>(__env));
}
};

// A query result is valid if it is not void and does not indicate a failure.
template <class _Ty>
concept __valid_query_result = __not_same_as<_Ty, void> && __ok<_Ty>;

template <class _Env, class _Query, class... _Args>
concept __valid_queryable_with = requires(_Env const &__env, _Args &&...__args) {
{ __env.__query(_Query(), static_cast<_Args &&>(__args)...) } -> __valid_query_result;
};

//////////////////////////////////////////////////////////////////////////////////////
// A CRTP base for an environment whose derived class provides (possibly
// void-returning) __query member functions, and that implements query() by forwarding
// to those member functions that do not return void.
template <__class _Derived>
struct __facade
{
// clang-format off
template <class _Env = _Derived, class _Query, class... _Args>
requires __valid_queryable_with<_Env, _Query, _Args...>
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
constexpr auto query(_Query, _Args &&...__args) const STDEXEC_AUTO_RETURN
(
static_cast<_Env const &>(*this).__query(_Query(), static_cast<_Args &&>(__args)...)
)
// clang-format on
};
} // namespace __env

using __env::__join_env_t;
Expand All @@ -170,7 +197,7 @@ namespace STDEXEC

template <class _Env>
concept __is_root_env = requires(_Env &&__env) {
{ __root_t{}(__env) } -> __std::same_as<bool>;
{ __root_t()(__env) } -> __same_as<bool>;
};

//////////////////////////////////////////////////////////////////////
Expand Down
2 changes: 2 additions & 0 deletions include/stdexec/__detail/__execution_fwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ namespace STDEXEC
using scheduler_t = scheduler_tag;
using receiver_t = receiver_tag;

struct __completion_info;

template <class _Tag, class _Sndr, class... _Env>
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
constexpr auto __get_completion_behavior() noexcept;
Expand Down
2 changes: 1 addition & 1 deletion include/stdexec/__detail/__get_completion_signatures.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ namespace STDEXEC
auto __cmplsigs = STDEXEC::get_completion_signatures<_Sender, _Env...>();
STDEXEC_IF_OK(__cmplsigs)
{
auto __cmplinfo = STDEXEC::__cmplsigs::__to_array(__cmplsigs);
auto __cmplinfo = STDEXEC::__cmplsigs::__reflect(__cmplsigs);
std::ranges::for_each(__cmplinfo, &__completion_info::__populate<_Sender, _Env...>);
return __cmplinfo;
}
Expand Down
Loading
Loading