1 /* 2 * Copyright (c) 2021-2022 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 "../functional.hpp" 19 #include "__basic_sender.hpp" 20 #include "__concepts.hpp" 21 #include "__config.hpp" 22 #include "__env.hpp" 23 #include "__execution_fwd.hpp" 24 #include "__meta.hpp" 25 26 namespace stdexec 27 { 28 29 struct default_domain; 30 struct dependent_domain; 31 32 namespace __domain 33 { 34 template <class _Tag> 35 using __legacy_c11n_for = typename _Tag::__legacy_customizations_t; 36 37 template <class _Tag, class... _Args> 38 using __legacy_c11n_fn = // 39 __make_dispatcher<__legacy_c11n_for<_Tag>, __none_such, _Args...>; 40 41 template <class _Tag, class... _Args> 42 concept __has_legacy_c11n = // 43 __callable<__legacy_c11n_fn<_Tag, _Args...>, _Args...>; 44 45 struct __legacy_customization 46 { 47 template <class _Tag, class _Data, class... _Children> 48 requires __has_legacy_c11n<_Tag, _Data, _Children...> operator ()stdexec::__domain::__legacy_customization49 auto operator()(_Tag, _Data&& __data, _Children&&... __children) const 50 -> decltype(auto) 51 { 52 return __legacy_c11n_fn<_Tag, _Data, _Children...>()( 53 static_cast<_Data&&>(__data), 54 static_cast<_Children&&>(__children)...); 55 } 56 }; 57 58 template <class _DomainOrTag, class _Sender, class... _Env> 59 concept __has_transform_sender = 60 requires(_DomainOrTag __tag, _Sender&& __sender, const _Env&... __env) { 61 __tag.transform_sender(static_cast<_Sender&&>(__sender), __env...); 62 }; 63 64 template <class _Sender, class... _Env> 65 concept __has_default_transform_sender = // 66 sender_expr<_Sender> // 67 && __has_transform_sender<tag_of_t<_Sender>, _Sender, _Env...>; 68 69 template <class _Type, class _Sender, class _Env> 70 concept __has_transform_env = 71 requires(_Type __obj, _Sender&& __sender, _Env&& __env) { 72 __obj.transform_env(static_cast<_Sender&&>(__sender), 73 static_cast<_Env&&>(__env)); 74 }; 75 76 template <class _Sender, class _Env> 77 concept __has_default_transform_env = // 78 sender_expr<_Sender> // 79 && __has_transform_env<tag_of_t<_Sender>, _Sender, _Env>; 80 81 template <class _DomainOrTag, class... _Args> 82 concept __has_apply_sender = 83 requires(_DomainOrTag __tag, _Args&&... __args) { 84 __tag.apply_sender(static_cast<_Args&&>(__args)...); 85 }; 86 } // namespace __domain 87 88 struct default_domain 89 { 90 default_domain() = default; 91 92 // Called without the environment during eager customization 93 template <class _Sender> 94 STDEXEC_ATTRIBUTE((always_inline)) transform_senderstdexec::default_domain95 decltype(auto) transform_sender(_Sender&& __sndr) const 96 { 97 // Look for a legacy customization for the given tag, and if found, 98 // apply it. 99 if constexpr (__callable<__sexpr_apply_t, _Sender, 100 __domain::__legacy_customization>) 101 { 102 return stdexec::__sexpr_apply(static_cast<_Sender&&>(__sndr), 103 __domain::__legacy_customization()); 104 } 105 else if constexpr (__domain::__has_default_transform_sender<_Sender>) 106 { 107 return tag_of_t<_Sender>().transform_sender( 108 static_cast<_Sender&&>(__sndr)); 109 } 110 else 111 { 112 return static_cast<_Sender>(static_cast<_Sender&&>(__sndr)); 113 } 114 } 115 116 // Called with an environment during lazy customization 117 template <class _Sender, class _Env> 118 STDEXEC_ATTRIBUTE((always_inline)) transform_senderstdexec::default_domain119 decltype(auto) transform_sender(_Sender&& __sndr, const _Env& __env) const 120 { 121 if constexpr (__domain::__has_default_transform_sender<_Sender, _Env>) 122 { 123 return tag_of_t<_Sender>().transform_sender( 124 static_cast<_Sender&&>(__sndr), __env); 125 } 126 else 127 { 128 return static_cast<_Sender>(static_cast<_Sender&&>(__sndr)); 129 } 130 } 131 132 template <class _Tag, class _Sender, class... _Args> 133 requires __domain::__has_legacy_c11n<_Tag, _Sender, _Args...> || 134 __domain::__has_apply_sender<_Tag, _Sender, _Args...> 135 STDEXEC_ATTRIBUTE((always_inline)) decltype(auto) 136 apply_sender(_Tag, _Sender&& __sndr, _Args&&... __args) const 137 { 138 // Look for a legacy customization for the given tag, and if found, 139 // apply it. 140 if constexpr (__domain::__has_legacy_c11n<_Tag, _Sender, _Args...>) 141 { 142 return __domain::__legacy_c11n_fn<_Tag, _Sender, _Args...>()( 143 static_cast<_Sender&&>(__sndr), 144 static_cast<_Args&&>(__args)...); 145 } 146 else 147 { 148 return _Tag().apply_sender(static_cast<_Sender&&>(__sndr), 149 static_cast<_Args&&>(__args)...); 150 } 151 } 152 153 template <class _Sender, class _Env> transform_envstdexec::default_domain154 auto transform_env(_Sender&& __sndr, _Env&& __env) const noexcept 155 -> decltype(auto) 156 { 157 if constexpr (__domain::__has_default_transform_env<_Sender, _Env>) 158 { 159 return tag_of_t<_Sender>().transform_env( 160 static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env)); 161 } 162 else 163 { 164 return static_cast<_Env>(static_cast<_Env&&>(__env)); 165 } 166 } 167 }; 168 169 ///////////////////////////////////////////////////////////////////////////// 170 namespace __detail 171 { 172 template <class _Env, class _Tag> 173 using __completion_scheduler_for = 174 __meval_or<__call_result_t, __none_such, get_completion_scheduler_t<_Tag>, 175 _Env>; 176 177 template <class _Env, class _Tag> 178 using __completion_domain_for = 179 __meval_or<__call_result_t, __none_such, get_domain_t, 180 __completion_scheduler_for<_Env, _Tag>>; 181 182 // Check the value, error, and stopped channels for completion schedulers. 183 // Of the completion schedulers that are known, they must all have compatible 184 // domains. This computes that domain, or else returns __none_such if there 185 // are no completion schedulers or if they don't specify a domain. 186 template <class _Env> 187 struct __completion_domain_or_none_ : 188 __mdefer_<__transform< 189 __mbind_front_q<__completion_domain_for, _Env>, 190 __remove<__none_such, __munique<__msingle_or<__none_such>>>>, 191 set_value_t, set_error_t, set_stopped_t> 192 {}; 193 194 template <class _Sender> 195 using __completion_domain_or_none = 196 __t<__completion_domain_or_none_<env_of_t<_Sender>>>; 197 198 template <class _Sender> 199 concept __consistent_completion_domains = 200 __mvalid<__completion_domain_or_none, _Sender>; 201 202 template <class _Sender> 203 concept __has_completion_domain = 204 (!same_as<__completion_domain_or_none<_Sender>, __none_such>); 205 206 template <__has_completion_domain _Sender> 207 using __completion_domain_of = __completion_domain_or_none<_Sender>; 208 } // namespace __detail 209 210 ///////////////////////////////////////////////////////////////////////////// 211 inline constexpr struct __get_early_domain_t 212 { 213 template <class _Sender, class _Default = default_domain> operator ()stdexec::__get_early_domain_t214 auto operator()(const _Sender&, _Default __def = {}) const noexcept 215 { 216 if constexpr (__callable<get_domain_t, env_of_t<_Sender>>) 217 { 218 return __call_result_t<get_domain_t, env_of_t<_Sender>>(); 219 } 220 else if constexpr (__detail::__has_completion_domain<_Sender>) 221 { 222 return __detail::__completion_domain_of<_Sender>(); 223 } 224 else 225 { 226 return __def; 227 } 228 } 229 } __get_early_domain{}; 230 231 template <class _Sender, class _Default = default_domain> 232 using __early_domain_of_t = 233 __call_result_t<__get_early_domain_t, _Sender, _Default>; 234 235 ///////////////////////////////////////////////////////////////////////////// 236 inline constexpr struct __get_late_domain_t 237 { 238 // When connect is looking for a customization, it first checks the sender's 239 // domain. If the sender knows the domain in which it completes, then that 240 // is where the subsequent task will execute. Otherwise, look to the 241 // receiver for late-bound information about the current execution context. 242 template <class _Sender, class _Env> operator ()stdexec::__get_late_domain_t243 auto operator()(const _Sender& __sndr, const _Env& __env) const noexcept 244 { 245 if constexpr (!same_as<dependent_domain, 246 __early_domain_of_t<_Sender, dependent_domain>>) 247 { 248 return __get_early_domain(__sndr); 249 } 250 else if constexpr (__callable<get_domain_t, const _Env&>) 251 { 252 return get_domain(__env); 253 } 254 else if constexpr (__callable<__composed<get_domain_t, get_scheduler_t>, 255 const _Env&>) 256 { 257 return get_domain(get_scheduler(__env)); 258 } 259 else 260 { 261 return default_domain(); 262 } 263 } 264 265 // The transfer algorithm is the exception to the rule. It ignores the 266 // domain of the predecessor, and dispatches based on the domain of the 267 // scheduler to which execution is being transferred. 268 template <sender_expr_for<transfer_t> _Sender, class _Env> operator ()stdexec::__get_late_domain_t269 auto operator()(const _Sender& __sndr, const _Env&) const noexcept 270 { 271 return __sexpr_apply(__sndr, 272 [](__ignore, auto& __data, __ignore) noexcept { 273 auto __sched = get_completion_scheduler<set_value_t>(__data); 274 return query_or(get_domain, __sched, default_domain()); 275 }); 276 } 277 } __get_late_domain{}; 278 279 template <class _Sender, class _Env> 280 using __late_domain_of_t = __call_result_t<__get_late_domain_t, _Sender, _Env>; 281 282 namespace __domain 283 { 284 struct __common_domain_fn 285 { __common_domainstdexec::__domain::__common_domain_fn286 static auto __common_domain() noexcept -> default_domain 287 { 288 return {}; 289 } 290 291 template <class _Domain, class... _OtherDomains> 292 requires __all_of<_Domain, _OtherDomains...> __common_domainstdexec::__domain::__common_domain_fn293 static auto __common_domain(_Domain __domain, _OtherDomains...) noexcept 294 -> _Domain 295 { 296 return static_cast<_Domain&&>(__domain); 297 } 298 299 template <class... _Domains> __common_domainstdexec::__domain::__common_domain_fn300 static auto __common_domain(_Domains...) noexcept // 301 -> __if_c<__one_of<dependent_domain, _Domains...>, dependent_domain, 302 __none_such> 303 { 304 return {}; 305 } 306 operator ()stdexec::__domain::__common_domain_fn307 auto operator()(__ignore, __ignore, const auto&... __sndrs) const noexcept 308 { 309 return __common_domain(__get_early_domain(__sndrs)...); 310 } 311 }; 312 313 template <class... _Senders> 314 using __common_domain_t = // 315 __call_result_t<__common_domain_fn, int, int, _Senders...>; 316 317 template <class... _Senders> 318 concept __has_common_domain = // 319 __none_of<__none_such, __common_domain_t<_Senders...>>; 320 } // namespace __domain 321 } // namespace stdexec 322