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 these after __execution_fwd.hpp 21 #include "__concepts.hpp" 22 #include "__cpo.hpp" 23 #include "__env.hpp" 24 #include "__senders.hpp" 25 #include "__tag_invoke.hpp" 26 27 namespace stdexec 28 { 29 ///////////////////////////////////////////////////////////////////////////// 30 // [execution.senders.schedule] 31 namespace __sched 32 { 33 struct schedule_t 34 { 35 template <__same_as<schedule_t> _Self, class _Scheduler> 36 STDEXEC_ATTRIBUTE((host, device, always_inline)) 37 friend auto tag_invoke(_Self, _Scheduler&& __sched) // 38 noexcept(noexcept(static_cast<_Scheduler&&>(__sched).schedule())) 39 -> decltype(static_cast<_Scheduler&&>(__sched).schedule()) 40 { 41 static_assert( 42 sender<decltype(static_cast<_Scheduler&&>(__sched).schedule())>, 43 "schedule() member functions must return a sender"); 44 return static_cast<_Scheduler&&>(__sched).schedule(); 45 } 46 47 template <class _Scheduler> 48 requires tag_invocable<schedule_t, _Scheduler> 49 STDEXEC_ATTRIBUTE((host, device)) 50 auto operator()(_Scheduler&& __sched) const 51 noexcept(nothrow_tag_invocable<schedule_t, _Scheduler>) 52 { 53 static_assert(sender<tag_invoke_result_t<schedule_t, _Scheduler>>); 54 return tag_invoke(schedule_t{}, static_cast<_Scheduler&&>(__sched)); 55 } 56 57 static constexpr auto query(forwarding_query_t) noexcept -> bool 58 { 59 return false; 60 } 61 }; 62 } // namespace __sched 63 64 using __sched::schedule_t; 65 inline constexpr schedule_t schedule{}; 66 67 template <class _Scheduler> 68 concept __has_schedule = // 69 requires(_Scheduler&& __sched) { 70 { schedule(static_cast<_Scheduler&&>(__sched)) } -> sender; 71 }; 72 73 template <class _Scheduler> 74 concept __sender_has_completion_scheduler = 75 requires(_Scheduler&& __sched) { 76 { 77 stdexec::__decay_copy(get_completion_scheduler<set_value_t>( 78 get_env(schedule(static_cast<_Scheduler&&>(__sched))))) 79 } -> same_as<__decay_t<_Scheduler>>; 80 }; 81 82 template <class _Scheduler> 83 concept scheduler = // 84 __has_schedule<_Scheduler> // 85 && __sender_has_completion_scheduler<_Scheduler> // 86 && equality_comparable<__decay_t<_Scheduler>> // 87 && copy_constructible<__decay_t<_Scheduler>>; 88 89 template <scheduler _Scheduler> 90 using schedule_result_t = __call_result_t<schedule_t, _Scheduler>; 91 92 template <class _SchedulerProvider> 93 concept __scheduler_provider = // 94 requires(const _SchedulerProvider& __sp) { 95 { get_scheduler(__sp) } -> scheduler; 96 }; 97 98 namespace __queries 99 { 100 template <class _Env> 101 requires tag_invocable<get_scheduler_t, const _Env&> 102 inline auto get_scheduler_t::operator()(const _Env& __env) const noexcept 103 -> tag_invoke_result_t<get_scheduler_t, const _Env&> 104 { 105 static_assert(nothrow_tag_invocable<get_scheduler_t, const _Env&>); 106 static_assert(scheduler<tag_invoke_result_t<get_scheduler_t, const _Env&>>); 107 return tag_invoke(get_scheduler_t{}, __env); 108 } 109 110 template <class _Env> 111 requires tag_invocable<get_delegation_scheduler_t, const _Env&> 112 inline auto get_delegation_scheduler_t::operator()( 113 const _Env& __env) const noexcept 114 -> tag_invoke_result_t<get_delegation_scheduler_t, const _Env&> 115 { 116 static_assert( 117 nothrow_tag_invocable<get_delegation_scheduler_t, const _Env&>); 118 static_assert( 119 scheduler< 120 tag_invoke_result_t<get_delegation_scheduler_t, const _Env&>>); 121 return tag_invoke(get_delegation_scheduler_t{}, __env); 122 } 123 124 template <__completion_tag _Tag> 125 template <__has_completion_scheduler_for<_Tag> _Env> 126 auto get_completion_scheduler_t<_Tag>::operator()( 127 const _Env& __env) const noexcept 128 -> tag_invoke_result_t<get_completion_scheduler_t<_Tag>, const _Env&> 129 { 130 static_assert( 131 nothrow_tag_invocable<get_completion_scheduler_t<_Tag>, const _Env&>, 132 "get_completion_scheduler<_Tag> should be noexcept"); 133 static_assert( 134 scheduler<tag_invoke_result_t<get_completion_scheduler_t<_Tag>, 135 const _Env&>>); 136 return tag_invoke(*this, __env); 137 } 138 } // namespace __queries 139 140 namespace __detail 141 { 142 // A handy utility for augmenting an environment with a scheduler. 143 template <class _Env, class _Scheduler> 144 STDEXEC_ATTRIBUTE((always_inline)) 145 auto __mkenv_sched(_Env&& __env, _Scheduler __sched) 146 { 147 auto __env2 = 148 __env::__join(prop{get_scheduler, __sched}, 149 __env::__without(static_cast<_Env&&>(__env), get_domain)); 150 using _Env2 = decltype(__env2); 151 152 struct __env_t : _Env2 153 {}; 154 155 return __env_t{static_cast<_Env2&&>(__env2)}; 156 } 157 } // namespace __detail 158 } // namespace stdexec 159