1 /* 2 * Copyright (c) 2021-2024 NVIDIA Corporation 3 * 4 * Licensed under the Apache License Version 2.0 with LLVM Exceptions 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * https://llvm.org/LICENSE.txt 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #pragma once 17 18 #include "__execution_fwd.hpp" 19 20 #include "__concepts.hpp" 21 #include "__completion_behavior.hpp" 22 #include "__meta.hpp" 23 #include "__query.hpp" 24 #include "__stop_token.hpp" 25 #include "__tag_invoke.hpp" 26 // #include "__tuple.hpp" 27 28 #include <exception> // IWYU pragma: keep for std::terminate 29 #include <functional> // IWYU pragma: keep for unwrap_reference_t 30 #include <type_traits> 31 #include <utility> 32 33 STDEXEC_PRAGMA_PUSH() 34 STDEXEC_PRAGMA_IGNORE_EDG(probable_guiding_friend) 35 STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference) 36 37 namespace stdexec { 38 ////////////////////////////////////////////////////////////////////////////////////////////////// 39 // [exec.queries] 40 namespace __queries { 41 struct execute_may_block_caller_t : __query<execute_may_block_caller_t, true> { 42 template <class _Attrs> STDEXEC_ATTRIBUTEstdexec::__queries::execute_may_block_caller_t43 STDEXEC_ATTRIBUTE(always_inline, host, device) 44 static constexpr void __validate() noexcept { 45 static_assert(same_as<bool, __call_result_t<execute_may_block_caller_t, const _Attrs&>>); 46 static_assert(__nothrow_callable<execute_may_block_caller_t, const _Attrs&>); 47 } 48 }; 49 50 struct get_forward_progress_guarantee_t 51 : __query< 52 get_forward_progress_guarantee_t, 53 forward_progress_guarantee::weakly_parallel, 54 __q1<__decay_t> 55 > { 56 template <class _Attrs> STDEXEC_ATTRIBUTEstdexec::__queries::get_forward_progress_guarantee_t57 STDEXEC_ATTRIBUTE(always_inline, host, device) 58 static constexpr void __validate() noexcept { 59 using __result_t = __call_result_t<get_forward_progress_guarantee_t, const _Attrs&>; 60 static_assert(same_as<forward_progress_guarantee, __result_t>); 61 static_assert(__nothrow_callable<get_forward_progress_guarantee_t, const _Attrs&>); 62 } 63 }; 64 65 // TODO: implement allocator concept 66 template <class _T0> 67 concept __allocator_c = true; 68 69 struct get_scheduler_t : __query<get_scheduler_t> { 70 using __query<get_scheduler_t>::operator(); 71 72 template <class _Query = get_scheduler_t> 73 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 74 constexpr auto 75 operator()() const noexcept; // defined in __read_env.hpp // defined in __read_env.hpp 76 77 template <class _Env> 78 STDEXEC_ATTRIBUTE(always_inline, host, device) 79 static constexpr void __validate() noexcept; // defined in __schedulers.hpp 80 STDEXEC_ATTRIBUTEstdexec::__queries::get_scheduler_t81 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 82 static consteval auto query(forwarding_query_t) noexcept -> bool { 83 return true; 84 } 85 }; 86 87 //! The type for `get_delegation_scheduler` [exec.get.delegation.scheduler] 88 //! A query object that asks for a scheduler that can be used to delegate 89 //! work to for the purpose of forward progress delegation ([intro.progress]). 90 struct get_delegation_scheduler_t : __query<get_delegation_scheduler_t> { 91 using __query<get_delegation_scheduler_t>::operator(); 92 93 template <class _Query = get_delegation_scheduler_t> 94 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 95 constexpr auto operator()() const noexcept; // defined in __read_env.hpp 96 97 template <class _Env> 98 STDEXEC_ATTRIBUTE(always_inline, host, device) 99 static constexpr void __validate() noexcept; // defined in __schedulers.hpp 100 STDEXEC_ATTRIBUTEstdexec::__queries::get_delegation_scheduler_t101 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 102 static consteval auto query(forwarding_query_t) noexcept -> bool { 103 return true; 104 } 105 }; 106 107 struct get_allocator_t : __query<get_allocator_t> { 108 using __query<get_allocator_t>::operator(); 109 110 template <class _Query = get_allocator_t> 111 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 112 constexpr auto operator()() const noexcept; // defined in __read_env.hpp 113 114 template <class _Env> STDEXEC_ATTRIBUTEstdexec::__queries::get_allocator_t115 STDEXEC_ATTRIBUTE(always_inline, host, device) 116 static constexpr void __validate() noexcept { 117 static_assert(__nothrow_callable<get_allocator_t, const _Env&>); 118 static_assert(__allocator_c<__call_result_t<get_allocator_t, const _Env&>>); 119 } 120 STDEXEC_ATTRIBUTEstdexec::__queries::get_allocator_t121 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 122 static consteval auto query(forwarding_query_t) noexcept -> bool { 123 return true; 124 } 125 }; 126 127 using __get_stop_token_t = __query<get_stop_token_t, never_stop_token{}, __q1<__decay_t>>; 128 129 struct get_stop_token_t : __get_stop_token_t { 130 using __get_stop_token_t::operator(); 131 132 template <class _Query = get_stop_token_t> 133 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 134 constexpr auto operator()() const noexcept; // defined in __read_env.hpp 135 136 template <class _Env> STDEXEC_ATTRIBUTEstdexec::__queries::get_stop_token_t137 STDEXEC_ATTRIBUTE(always_inline, host, device) 138 static constexpr void __validate() noexcept { 139 static_assert(__nothrow_callable<get_stop_token_t, const _Env&>); 140 static_assert(stoppable_token<__call_result_t<get_stop_token_t, const _Env&>>); 141 } 142 STDEXEC_ATTRIBUTEstdexec::__queries::get_stop_token_t143 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 144 static consteval auto query(forwarding_query_t) noexcept -> bool { 145 return true; 146 } 147 }; 148 149 template <__completion_tag _Query> 150 struct get_completion_scheduler_t : __query<get_completion_scheduler_t<_Query>> { 151 template <class _Env> 152 STDEXEC_ATTRIBUTE(always_inline, host, device) 153 static constexpr void __validate() noexcept; // defined in __schedulers.hpp 154 STDEXEC_ATTRIBUTEstdexec::__queries::get_completion_scheduler_t155 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 156 static consteval auto query(forwarding_query_t) noexcept -> bool { 157 return true; 158 } 159 }; 160 161 struct get_domain_t : __query<get_domain_t, __no_default, __q1<__decay_t>> { 162 template <class _Env> STDEXEC_ATTRIBUTEstdexec::__queries::get_domain_t163 STDEXEC_ATTRIBUTE(always_inline, host, device) 164 static constexpr void __validate() noexcept { 165 static_assert( 166 __nothrow_callable<get_domain_t, const _Env&>, 167 "Customizations of get_domain must be noexcept."); 168 static_assert( 169 __class<__call_result_t<get_domain_t, const _Env&>>, 170 "Customizations of get_domain must return a class type."); 171 } 172 STDEXEC_ATTRIBUTEstdexec::__queries::get_domain_t173 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 174 static consteval auto query(forwarding_query_t) noexcept -> bool { 175 return true; 176 } 177 }; 178 179 struct get_domain_override_t : __query<get_domain_override_t, __no_default, __q1<__decay_t>> { 180 template <class _Env> STDEXEC_ATTRIBUTEstdexec::__queries::get_domain_override_t181 STDEXEC_ATTRIBUTE(always_inline, host, device) 182 static constexpr void __validate() noexcept { 183 static_assert( 184 __nothrow_callable<get_domain_override_t, const _Env&>, 185 "Customizations of get_domain_override must be noexcept."); 186 static_assert( 187 __class<__call_result_t<get_domain_override_t, const _Env&>>, 188 "Customizations of get_domain_override must return a class type."); 189 } 190 STDEXEC_ATTRIBUTEstdexec::__queries::get_domain_override_t191 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 192 static consteval auto query(forwarding_query_t) noexcept -> bool { 193 return false; 194 } 195 }; 196 197 struct __is_scheduler_affine_t { 198 template <class _Result> STDEXEC_ATTRIBUTEstdexec::__queries::__is_scheduler_affine_t199 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 200 static consteval auto __ensure_bool_constant() noexcept { 201 if constexpr (__is_bool_constant<_Result>) { 202 return static_cast<bool>(_Result::value); 203 } else { 204 static_assert( 205 __is_bool_constant<_Result>, 206 "The __is_scheduler_affine query must be one of the following forms:\n" 207 " static constexpr bool query(__is_scheduler_affine_t) noexcept;\n" 208 " bool_constant<Bool> query(__is_scheduler_affine_t) const noexcept;\n" 209 " bool_constant<Bool> query(__is_scheduler_affine_t, const Env&) const noexcept;\n"); 210 } 211 } 212 213 template <class _Attrs, class... _Env> STDEXEC_ATTRIBUTEstdexec::__queries::__is_scheduler_affine_t214 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 215 consteval auto operator()() const noexcept -> bool { 216 return __completes_inline<_Attrs, const _Env&...>; 217 } 218 219 template <__queryable_with<__is_scheduler_affine_t> _Attrs, class... _Env> STDEXEC_ATTRIBUTEstdexec::__queries::__is_scheduler_affine_t220 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 221 consteval auto operator()() const noexcept -> bool { 222 if constexpr (__statically_queryable_with<_Attrs, __is_scheduler_affine_t>) { 223 return _Attrs::query(__is_scheduler_affine_t()); 224 } else { 225 return __ensure_bool_constant<__query_result_t<_Attrs, __is_scheduler_affine_t>>(); 226 } 227 } 228 229 template <class _Attrs, class _Env> 230 requires __queryable_with<_Attrs, __is_scheduler_affine_t, const _Env&> STDEXEC_ATTRIBUTEstdexec::__queries::__is_scheduler_affine_t231 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 232 consteval auto operator()() const noexcept -> bool { 233 using __result_t = __query_result_t<_Attrs, __is_scheduler_affine_t, const _Env&>; 234 return __ensure_bool_constant<__result_t>(); 235 } 236 237 template <class _Attrs, class... _Env> STDEXEC_ATTRIBUTEstdexec::__queries::__is_scheduler_affine_t238 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 239 consteval auto operator()(const _Attrs&, const _Env&...) const noexcept -> bool { 240 return operator()<_Attrs, _Env...>(); 241 } 242 STDEXEC_ATTRIBUTEstdexec::__queries::__is_scheduler_affine_t243 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 244 static consteval auto query(forwarding_query_t) noexcept -> bool { 245 return false; 246 } 247 }; 248 249 struct __root_t : __query<__root_t> { STDEXEC_ATTRIBUTEstdexec::__queries::__root_t250 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 251 static consteval auto query(forwarding_query_t) noexcept -> bool { 252 return false; 253 } 254 }; 255 256 struct __root_env { 257 using __t = __root_env; 258 using __id = __root_env; 259 STDEXEC_ATTRIBUTEstdexec::__queries::__root_env260 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 261 static constexpr auto query(__root_t) noexcept -> bool { 262 return true; 263 } 264 }; 265 } // namespace __queries 266 267 using __queries::execute_may_block_caller_t; 268 using __queries::get_forward_progress_guarantee_t; 269 using __queries::get_allocator_t; 270 using __queries::get_scheduler_t; 271 using __queries::get_delegation_scheduler_t; 272 using get_delegatee_scheduler_t 273 [[deprecated("get_delegatee_scheduler_t has been renamed get_delegation_scheduler_t")]] = 274 get_delegation_scheduler_t; 275 using __queries::get_stop_token_t; 276 using __queries::get_completion_scheduler_t; 277 using __queries::get_domain_t; 278 using __queries::get_domain_override_t; 279 using __queries::__is_scheduler_affine_t; 280 using __queries::__root_t; 281 using __queries::__root_env; 282 283 inline constexpr execute_may_block_caller_t execute_may_block_caller{}; 284 inline constexpr get_forward_progress_guarantee_t get_forward_progress_guarantee{}; 285 inline constexpr get_scheduler_t get_scheduler{}; 286 inline constexpr get_delegation_scheduler_t get_delegation_scheduler{}; 287 inline constexpr auto& get_delegatee_scheduler 288 [[deprecated("get_delegatee_scheduler has been renamed get_delegation_scheduler")]] 289 = get_delegation_scheduler; 290 inline constexpr get_allocator_t get_allocator{}; 291 inline constexpr get_stop_token_t get_stop_token{}; 292 #if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__) 293 template <__completion_tag _Query> 294 inline constexpr get_completion_scheduler_t<_Query> get_completion_scheduler{}; 295 #else 296 template <> 297 inline constexpr get_completion_scheduler_t<set_value_t> get_completion_scheduler<set_value_t>{}; 298 template <> 299 inline constexpr get_completion_scheduler_t<set_error_t> get_completion_scheduler<set_error_t>{}; 300 template <> 301 inline constexpr get_completion_scheduler_t<set_stopped_t> 302 get_completion_scheduler<set_stopped_t>{}; 303 #endif 304 305 inline constexpr get_domain_t get_domain{}; 306 inline constexpr get_domain_override_t get_domain_override{}; 307 308 template <class _Query, class _Queryable, class _Default> 309 using __query_result_or_t = __call_result_t<query_or_t, _Query, _Queryable, _Default>; 310 311 namespace __env { 312 template <class _Tp, class _Promise> 313 concept __has_as_awaitable_member = requires(_Tp&& __t, _Promise& __promise) { 314 static_cast<_Tp &&>(__t).as_awaitable(__promise); 315 }; 316 317 template <class _Promise> 318 struct __with_await_transform { 319 template <class _Ty> STDEXEC_ATTRIBUTEstdexec::__env::__with_await_transform320 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 321 auto await_transform(_Ty&& __value) noexcept -> _Ty&& { 322 return static_cast<_Ty&&>(__value); 323 } 324 325 template <class _Ty> 326 requires __has_as_awaitable_member<_Ty, _Promise&> STDEXEC_ATTRIBUTEstdexec::__env::__with_await_transform327 STDEXEC_ATTRIBUTE(nodiscard, host, device) 328 auto await_transform(_Ty&& __value) 329 noexcept(noexcept(__declval<_Ty>().as_awaitable(__declval<_Promise&>()))) 330 -> decltype(__declval<_Ty>().as_awaitable(__declval<_Promise&>())) { 331 return static_cast<_Ty&&>(__value).as_awaitable(static_cast<_Promise&>(*this)); 332 } 333 334 template <class _Ty> 335 requires(!__has_as_awaitable_member<_Ty, _Promise&>) 336 && tag_invocable<as_awaitable_t, _Ty, _Promise&> STDEXEC_ATTRIBUTEstdexec::__env::__with_await_transform337 STDEXEC_ATTRIBUTE(nodiscard, host, device) 338 auto await_transform(_Ty&& __value) 339 noexcept(nothrow_tag_invocable<as_awaitable_t, _Ty, _Promise&>) 340 -> tag_invoke_result_t<as_awaitable_t, _Ty, _Promise&> { 341 return tag_invoke(as_awaitable, static_cast<_Ty&&>(__value), static_cast<_Promise&>(*this)); 342 } 343 }; 344 345 template <class _Env> 346 struct __promise : __with_await_transform<__promise<_Env>> { 347 STDEXEC_ATTRIBUTE(nodiscard, host, device) 348 auto get_env() const noexcept -> const _Env&; 349 }; 350 351 template <class ValueType> 352 struct __prop_like { 353 template <class _Query> STDEXEC_ATTRIBUTEstdexec::__env::__prop_like354 STDEXEC_ATTRIBUTE(noreturn, nodiscard, host, device) 355 constexpr auto query(_Query) const noexcept -> const ValueType& { 356 STDEXEC_TERMINATE(); 357 } 358 }; 359 360 // A singleton environment from a query/value pair 361 template <class _Query, class _Value> 362 struct prop { 363 using __t = prop; 364 using __id = prop; 365 366 static_assert(__callable<_Query, __prop_like<_Value>>); 367 STDEXEC_ATTRIBUTEstdexec::__env::prop368 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 369 constexpr auto query(_Query) const noexcept -> const _Value& { 370 return __value; 371 } 372 373 STDEXEC_ATTRIBUTE(no_unique_address) _Query __query; 374 STDEXEC_ATTRIBUTE(no_unique_address) _Value __value; 375 }; 376 377 template <class _Query, class _Value> 378 STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE 379 prop(_Query, _Value) -> prop<_Query, std::unwrap_reference_t<_Value>>; 380 381 template <class _Query, auto _Value> 382 struct cprop { 383 using __t = cprop; 384 using __id = cprop; 385 STDEXEC_ATTRIBUTEstdexec::__env::cprop386 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 387 static constexpr auto query(_Query) noexcept { 388 return _Value; 389 } 390 }; 391 392 ////////////////////////////////////////////////////////////////////// 393 // env 394 template <class... Envs> 395 struct env; 396 397 template <> 398 struct env<> { 399 using __t = env; 400 using __id = env; 401 402 STDEXEC_ATTRIBUTE(nodiscard, host, device) 403 auto query() const = delete; 404 }; 405 406 template <class Env> 407 struct env<Env> : Env { 408 using __t = env; 409 using __id = env; 410 }; 411 412 template <class Env> 413 struct env<Env&> { 414 using __t = env; 415 using __id = env; 416 417 template <class Query, class... _Args> 418 requires __queryable_with<Env, Query, _Args...> STDEXEC_ATTRIBUTEstdexec::__env::env419 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 420 constexpr auto query(Query, _Args&&... __args) const 421 noexcept(__nothrow_queryable_with<Env, Query, _Args...>) 422 -> __query_result_t<Env, Query, _Args...> { 423 return __query<Query>()(env_, static_cast<_Args&&>(__args)...); 424 } 425 426 Env& env_; 427 }; 428 429 template <class Env> 430 using __env_base = __if_c<std::is_reference_v<Env>, env<Env>, Env>; 431 432 template <class Env1, class Env2> 433 struct env<Env1, Env2> : __env_base<Env1> { 434 using __t = env; 435 using __id = env; 436 437 using __env_base<Env1>::query; 438 439 template <class Query, class... _Args> 440 requires(!__queryable_with<Env1, Query, _Args...>) 441 && __queryable_with<Env2, Query, _Args...> STDEXEC_ATTRIBUTEstdexec::__env::env442 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 443 constexpr auto query(Query, _Args&&... __args) const 444 noexcept(__nothrow_queryable_with<Env2, Query, _Args...>) 445 -> __query_result_t<Env2, Query, _Args...> { 446 return __query<Query>()(env2_, static_cast<_Args&&>(__args)...); 447 } 448 449 STDEXEC_ATTRIBUTE(no_unique_address) Env2 env2_; 450 }; 451 452 template <class Env1, class Env2, class... Envs> 453 struct env<Env1, Env2, Envs...> : env<env<Env1, Env2>, Envs...> { 454 using __t = env; 455 using __id = env; 456 }; 457 458 template <class... _Envs> 459 STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE env(_Envs...) -> env<std::unwrap_reference_t<_Envs>...>; 460 461 template <class _EnvId> 462 struct __fwd { 463 using _Env = __cvref_t<_EnvId>; 464 static_assert(__nothrow_move_constructible<_Env>); 465 466 struct __t { 467 using __id = __fwd; 468 using __fwd_env_t = __t; 469 470 template <__forwarding_query _Query, class... _Args> 471 requires __queryable_with<_Env, _Query, _Args...> STDEXEC_ATTRIBUTEstdexec::__env::__fwd::__t472 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 473 constexpr auto query(_Query, _Args&&... __args) const 474 noexcept(__nothrow_queryable_with<_Env, _Query, _Args...>) 475 -> __query_result_t<_Env, _Query, _Args...> { 476 return __query<_Query>()(__env_, static_cast<_Args&&>(__args)...); 477 } 478 479 STDEXEC_ATTRIBUTE(no_unique_address) 480 _Env __env_; 481 }; 482 }; 483 484 template <class _Env> 485 concept __is_fwd_env = __same_as<_Env, typename _Env::__fwd_env_t>; 486 487 struct __fwd_fn { 488 template <class _Env> STDEXEC_ATTRIBUTEstdexec::__env::__fwd_fn489 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 490 constexpr auto operator()(_Env&& __env) const -> decltype(auto) { 491 if constexpr (__decays_to<_Env, env<>> || __is_fwd_env<__decay_t<_Env>>) { 492 return static_cast<_Env>(static_cast<_Env&&>(__env)); 493 } else { 494 return __t<__fwd<__cvref_id<_Env>>>{static_cast<_Env&&>(__env)}; 495 } 496 } 497 }; 498 499 template <class _Env> 500 using __fwd_env_t = __call_result_t<__fwd_fn, _Env>; 501 502 template <class _EnvId, class _Query> 503 struct __without_ { 504 using _Env = __cvref_t<_EnvId>; 505 static_assert(__nothrow_move_constructible<_Env>); 506 507 struct __t : __env_base<_Env> { 508 using __id = __without_; 509 using __env_base<_Env>::query; 510 511 STDEXEC_ATTRIBUTE(nodiscard, host, device) 512 auto query(_Query) const noexcept = delete; 513 }; 514 }; 515 516 struct __without_fn { 517 template <class _Env, class _Query> operator ()stdexec::__env::__without_fn518 constexpr auto operator()(_Env&& __env, _Query) const noexcept -> auto { 519 if constexpr (__queryable_with<_Env, _Query>) { 520 using _Without = __t<__without_<__cvref_id<_Env>, _Query>>; 521 return _Without{static_cast<_Env&&>(__env)}; 522 } else { 523 return static_cast<_Env&&>(__env); 524 } 525 } 526 }; 527 528 inline constexpr __without_fn __without{}; 529 530 template <class _Env, class _Query, class... _Tags> 531 using __without_t = __result_of<__without, _Env, _Query, _Tags...>; 532 533 template <__nothrow_move_constructible _Fun> 534 struct __from { 535 using __t = __from; 536 using __id = __from; 537 STDEXEC_ATTRIBUTE(no_unique_address) _Fun __fun_; 538 539 template <class _Query, class... _Args> 540 requires __callable<const _Fun&, _Query, _Args...> STDEXEC_ATTRIBUTEstdexec::__env::__from541 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 542 auto query(_Query, _Args&&... __args) const 543 noexcept(__nothrow_callable<const _Fun&, _Query, _Args...>) 544 -> __call_result_t<const _Fun&, _Query, _Args...> { 545 return __fun_(_Query(), static_cast<_Args&&>(__args)...); 546 } 547 }; 548 549 template <class _Fun> 550 STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __from(_Fun) -> __from<_Fun>; 551 552 struct __join_fn { 553 template <class _Env1, class _Env2> STDEXEC_ATTRIBUTEstdexec::__env::__join_fn554 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 555 constexpr auto operator()(_Env1&& __env1, _Env2&& __env2) const noexcept -> decltype(auto) { 556 if constexpr (__decays_to<_Env1, env<>>) { 557 return __fwd_fn()(static_cast<_Env2&&>(__env2)); 558 } else if constexpr (__decays_to<_Env2, env<>>) { 559 return static_cast<_Env1>(static_cast<_Env1&&>(__env1)); 560 } else { 561 return env<_Env1, __fwd_env_t<_Env2>>{ 562 {static_cast<_Env1&&>(__env1)}, __fwd_fn()(static_cast<_Env2&&>(__env2))}; 563 } 564 } 565 }; 566 567 inline constexpr __join_fn __join{}; 568 569 template <class _First, class... _Second> 570 using __join_env_t = __result_of<__join, _First, _Second...>; 571 572 struct __as_root_env_fn { 573 template <class _Env> STDEXEC_ATTRIBUTEstdexec::__env::__as_root_env_fn574 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 575 constexpr auto operator()(_Env __env) const noexcept 576 -> __join_env_t<__root_env, std::unwrap_reference_t<_Env>> { 577 return __join(__root_env{}, static_cast<std::unwrap_reference_t<_Env>&&>(__env)); 578 } 579 }; 580 581 inline constexpr __as_root_env_fn __as_root_env{}; 582 583 template <class _Env> 584 using __as_root_env_t = __result_of<__as_root_env, _Env>; 585 } // namespace __env 586 587 using __env::__join_env_t; 588 using __env::__fwd_env_t; 589 590 ///////////////////////////////////////////////////////////////////////////// 591 namespace __get_env { 592 template <class _EnvProvider> 593 concept __has_get_env = requires(const _EnvProvider& __env_provider) { 594 __env_provider.get_env(); 595 }; 596 597 // For getting an execution environment from a receiver or the attributes from a sender. 598 struct get_env_t { 599 template <class _EnvProvider> 600 requires __has_get_env<_EnvProvider> STDEXEC_ATTRIBUTEstdexec::__get_env::get_env_t601 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 602 constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept 603 -> decltype(__env_provider.get_env()) { 604 static_assert(queryable<decltype(__env_provider.get_env())>); 605 static_assert(noexcept(__env_provider.get_env()), "get_env() members must be noexcept"); 606 return __env_provider.get_env(); 607 } 608 609 template <class _EnvProvider> 610 requires(!__has_get_env<_EnvProvider>) && tag_invocable<get_env_t, const _EnvProvider&> STDEXEC_ATTRIBUTEstdexec::__get_env::get_env_t611 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 612 constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept 613 -> tag_invoke_result_t<get_env_t, const _EnvProvider&> { 614 static_assert(queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>); 615 static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>); 616 return tag_invoke(*this, __env_provider); 617 } 618 619 template <class _EnvProvider> STDEXEC_ATTRIBUTEstdexec::__get_env::get_env_t620 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 621 constexpr auto operator()(const _EnvProvider&) const noexcept -> env<> { 622 return {}; 623 } 624 }; 625 } // namespace __get_env 626 627 using __get_env::get_env_t; 628 inline constexpr get_env_t get_env{}; 629 630 template <class _EnvProvider> 631 concept environment_provider = requires(_EnvProvider& __ep) { 632 { get_env(std::as_const(__ep)) } -> queryable; 633 }; 634 635 template <class _Scheduler, class _LateDomain = __none_such> 636 struct __sched_attrs { 637 using __t = __sched_attrs; 638 using __id = __sched_attrs; 639 640 using __scheduler_t = __decay_t<_Scheduler>; 641 using __sched_domain_t = __query_result_or_t<get_domain_t, __scheduler_t, default_domain>; 642 STDEXEC_ATTRIBUTEstdexec::__sched_attrs643 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 644 constexpr auto query(get_completion_scheduler_t<set_value_t>) const noexcept -> __scheduler_t { 645 return __sched_; 646 } 647 STDEXEC_ATTRIBUTEstdexec::__sched_attrs648 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 649 constexpr auto query(get_domain_t) const noexcept -> __sched_domain_t { 650 return {}; 651 } 652 STDEXEC_ATTRIBUTEstdexec::__sched_attrs653 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 654 constexpr auto query(get_domain_override_t) const noexcept -> _LateDomain 655 requires(!same_as<_LateDomain, __none_such>) 656 { 657 return {}; 658 } 659 660 _Scheduler __sched_; STDEXEC_ATTRIBUTEstdexec::__sched_attrs661 STDEXEC_ATTRIBUTE(no_unique_address) _LateDomain __late_domain_ { }; 662 }; 663 664 template <class _Scheduler, class _LateDomain = __none_such> 665 STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __sched_attrs(_Scheduler, _LateDomain = {}) 666 -> __sched_attrs<std::unwrap_reference_t<_Scheduler>, _LateDomain>; 667 668 template <class _Scheduler> 669 struct __sched_env { 670 using __t = __sched_env; 671 using __id = __sched_env; 672 673 using __scheduler_t = __decay_t<_Scheduler>; 674 using __sched_domain_t = __query_result_or_t<get_domain_t, __scheduler_t, default_domain>; 675 STDEXEC_ATTRIBUTEstdexec::__sched_env676 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 677 constexpr auto query(get_scheduler_t) const noexcept -> __scheduler_t { 678 return __sched_; 679 } 680 STDEXEC_ATTRIBUTEstdexec::__sched_env681 STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) 682 constexpr auto query(get_domain_t) const noexcept -> __sched_domain_t { 683 return {}; 684 } 685 686 _Scheduler __sched_; 687 }; 688 689 template <class _Scheduler> 690 STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE 691 __sched_env(_Scheduler) -> __sched_env<std::unwrap_reference_t<_Scheduler>>; 692 693 using __env::__as_root_env_t; 694 using __env::__as_root_env; 695 696 template <class _Env> 697 concept __is_root_env = requires(_Env&& __env) { 698 { __root_t{}(__env) } -> same_as<bool>; 699 }; 700 701 template <class _Sender, class... _Env> 702 concept __is_scheduler_affine = requires { 703 requires __is_scheduler_affine_t().operator()<env_of_t<_Sender>, _Env...>(); 704 }; 705 706 // The attributes of a sender adaptor that does not introduce asynchrony. 707 template <class _Sender> 708 struct __sync_attrs { 709 using __t = __sync_attrs; 710 using __id = __sync_attrs; 711 712 [[nodiscard]] querystdexec::__sync_attrs713 constexpr auto query(__is_scheduler_affine_t) const noexcept { 714 return __mbool<__is_scheduler_affine<_Sender>>(); 715 } 716 717 template <class... _Env> 718 [[nodiscard]] querystdexec::__sync_attrs719 constexpr auto query(get_completion_behavior_t, const _Env&...) const noexcept { 720 return get_completion_behavior<_Sender, _Env...>(); 721 } 722 723 template <__forwarding_query _Query, class... _Args> 724 requires __queryable_with<env_of_t<_Sender>, _Query, _Args...> 725 [[nodiscard]] querystdexec::__sync_attrs726 constexpr auto query(_Query, _Args&&... __args) const 727 noexcept(__nothrow_queryable_with<env_of_t<_Sender>, _Query, _Args...>) 728 -> __query_result_t<env_of_t<_Sender>, _Query, _Args...> { 729 return __query<_Query>()(get_env(__sndr_), static_cast<_Args&&>(__args)...); 730 } 731 732 const _Sender& __sndr_; 733 }; 734 735 template <class _Sender> 736 STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __sync_attrs(const _Sender&) -> __sync_attrs<_Sender>; 737 738 } // namespace stdexec 739 740 STDEXEC_PRAGMA_POP() 741