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 "../functional.hpp" 19 #include "__concepts.hpp" 20 #include "__config.hpp" 21 #include "__env.hpp" 22 #include "__execution_fwd.hpp" 23 #include "__meta.hpp" 24 #include "__sender_introspection.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...> 49 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 87 template <class _Sender> 88 constexpr bool __is_nothrow_transform_sender() 89 { 90 if constexpr (__callable<__sexpr_apply_t, _Sender, 91 __domain::__legacy_customization>) 92 { 93 return __nothrow_callable<__sexpr_apply_t, _Sender, 94 __domain::__legacy_customization>; 95 } 96 else if constexpr (__domain::__has_default_transform_sender<_Sender>) 97 { 98 return noexcept( 99 tag_of_t<_Sender>().transform_sender(__declval<_Sender>())); 100 } 101 else 102 { 103 return __nothrow_constructible_from<_Sender, _Sender>; 104 } 105 } 106 107 template <class _Sender, class _Env> 108 constexpr bool __is_nothrow_transform_sender() noexcept 109 { 110 if constexpr (__domain::__has_default_transform_sender<_Sender, _Env>) 111 { 112 return // 113 noexcept(tag_of_t<_Sender>().transform_sender( 114 __declval<_Sender>(), __declval<const _Env&>())); 115 } 116 else 117 { 118 return __nothrow_constructible_from<_Sender, _Sender>; 119 } 120 } 121 } // namespace __domain 122 123 struct default_domain 124 { 125 default_domain() = default; 126 127 // Called without the environment during eager customization 128 template <class _Sender> 129 STDEXEC_ATTRIBUTE((always_inline)) 130 decltype(auto) transform_sender(_Sender&& __sndr) const 131 noexcept(__domain::__is_nothrow_transform_sender<_Sender>()) 132 { 133 // Look for a legacy customization for the given tag, and if found, 134 // apply it. 135 if constexpr (__callable<__sexpr_apply_t, _Sender, 136 __domain::__legacy_customization>) 137 { 138 return stdexec::__sexpr_apply(static_cast<_Sender&&>(__sndr), 139 __domain::__legacy_customization()); 140 } 141 else if constexpr (__domain::__has_default_transform_sender<_Sender>) 142 { 143 return tag_of_t<_Sender>().transform_sender( 144 static_cast<_Sender&&>(__sndr)); 145 } 146 else 147 { 148 return static_cast<_Sender>(static_cast<_Sender&&>(__sndr)); 149 } 150 } 151 152 // Called with an environment during lazy customization 153 template <class _Sender, class _Env> 154 STDEXEC_ATTRIBUTE((always_inline)) 155 decltype(auto) transform_sender(_Sender&& __sndr, const _Env& __env) const 156 noexcept(__domain::__is_nothrow_transform_sender<_Sender, _Env>()) 157 { 158 if constexpr (__domain::__has_default_transform_sender<_Sender, _Env>) 159 { 160 return tag_of_t<_Sender>().transform_sender( 161 static_cast<_Sender&&>(__sndr), __env); 162 } 163 else 164 { 165 return static_cast<_Sender>(static_cast<_Sender&&>(__sndr)); 166 } 167 } 168 169 template <class _Tag, class _Sender, class... _Args> 170 requires __domain::__has_legacy_c11n<_Tag, _Sender, _Args...> || 171 __domain::__has_apply_sender<_Tag, _Sender, _Args...> 172 STDEXEC_ATTRIBUTE((always_inline)) 173 decltype(auto) apply_sender(_Tag, _Sender&& __sndr, _Args&&... __args) const 174 { 175 // Look for a legacy customization for the given tag, and if found, 176 // apply it. 177 if constexpr (__domain::__has_legacy_c11n<_Tag, _Sender, _Args...>) 178 { 179 return __domain::__legacy_c11n_fn<_Tag, _Sender, _Args...>()( 180 static_cast<_Sender&&>(__sndr), 181 static_cast<_Args&&>(__args)...); 182 } 183 else 184 { 185 return _Tag().apply_sender(static_cast<_Sender&&>(__sndr), 186 static_cast<_Args&&>(__args)...); 187 } 188 } 189 190 template <class _Sender, class _Env> 191 auto transform_env(_Sender&& __sndr, _Env&& __env) const noexcept 192 -> decltype(auto) 193 { 194 if constexpr (__domain::__has_default_transform_env<_Sender, _Env>) 195 { 196 return tag_of_t<_Sender>().transform_env( 197 static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env)); 198 } 199 else 200 { 201 return static_cast<_Env>(static_cast<_Env&&>(__env)); 202 } 203 } 204 }; 205 206 ///////////////////////////////////////////////////////////////////////////// 207 namespace __detail 208 { 209 template <class _Env, class _Tag> 210 using __completion_scheduler_for = 211 __meval_or<__call_result_t, __none_such, get_completion_scheduler_t<_Tag>, 212 _Env>; 213 214 template <class _Env, class _Tag> 215 using __completion_domain_for = 216 __meval_or<__call_result_t, __none_such, get_domain_t, 217 __completion_scheduler_for<_Env, _Tag>>; 218 219 // Check the value, error, and stopped channels for completion schedulers. 220 // Of the completion schedulers that are known, they must all have compatible 221 // domains. This computes that domain, or else returns __none_such if there 222 // are no completion schedulers or if they don't specify a domain. 223 template <class _Env> 224 struct __completion_domain_or_none_ : 225 __mdefer_<__mtransform< 226 __mbind_front_q<__completion_domain_for, _Env>, 227 __mremove<__none_such, __munique<__msingle_or<__none_such>>>>, 228 set_value_t, set_error_t, set_stopped_t> 229 {}; 230 231 template <class _Sender> 232 using __completion_domain_or_none = 233 __t<__completion_domain_or_none_<env_of_t<_Sender>>>; 234 235 template <class _Sender> 236 concept __consistent_completion_domains = 237 __mvalid<__completion_domain_or_none, _Sender>; 238 239 template <class _Sender> 240 concept __has_completion_domain = 241 (!same_as<__completion_domain_or_none<_Sender>, __none_such>); 242 243 template <__has_completion_domain _Sender> 244 using __completion_domain_of = __completion_domain_or_none<_Sender>; 245 } // namespace __detail 246 247 ///////////////////////////////////////////////////////////////////////////// 248 //! Function object implementing `get-domain-early(snd)` 249 //! from [exec.snd.general] item 3.9. It is the first well-formed expression of 250 //! a) `get_domain(get_env(sndr))` 251 //! b) `completion-domain(sndr)` 252 //! c) `default_domain()` 253 inline constexpr struct __get_early_domain_t 254 { 255 template <class _Sender, class _Default = default_domain> 256 auto operator()(const _Sender&, _Default __def = {}) const noexcept 257 { 258 if constexpr (__callable<get_domain_t, env_of_t<_Sender>>) 259 { 260 return __domain_of_t<env_of_t<_Sender>>(); 261 } 262 else if constexpr (__detail::__has_completion_domain<_Sender>) 263 { 264 return __detail::__completion_domain_of<_Sender>(); 265 } 266 else 267 { 268 return __def; 269 } 270 } 271 } __get_early_domain{}; 272 273 template <class _Sender, class _Default = default_domain> 274 using __early_domain_of_t = 275 __call_result_t<__get_early_domain_t, _Sender, _Default>; 276 277 ///////////////////////////////////////////////////////////////////////////// 278 inline constexpr struct __get_late_domain_t 279 { 280 // When connect is looking for a customization, it first checks the sender's 281 // domain. If the sender knows the domain in which it completes, then that 282 // is where the subsequent task will execute. Otherwise, look to the 283 // receiver for late-bound information about the current execution context. 284 template <class _Sender, class _Env> 285 auto operator()(const _Sender& __sndr, const _Env& __env) const noexcept 286 { 287 if constexpr (!same_as<dependent_domain, 288 __early_domain_of_t<_Sender, dependent_domain>>) 289 { 290 return __get_early_domain(__sndr); 291 } 292 else if constexpr (__callable<get_domain_t, const _Env&>) 293 { 294 return get_domain(__env); 295 } 296 else if constexpr (__callable<__composed<get_domain_t, get_scheduler_t>, 297 const _Env&>) 298 { 299 return get_domain(get_scheduler(__env)); 300 } 301 else 302 { 303 return default_domain(); 304 } 305 } 306 307 // The continues_on algorithm is the exception to the rule. It ignores the 308 // domain of the predecessor, and dispatches based on the domain of the 309 // scheduler to which execution is being transferred. 310 template <sender_expr_for<continues_on_t> _Sender, class _Env> 311 auto operator()(const _Sender& __sndr, const _Env&) const noexcept 312 { 313 return __sexpr_apply(__sndr, [](__ignore, auto& __data, 314 __ignore) noexcept { 315 auto __sched = get_completion_scheduler<set_value_t>(__data); 316 return query_or(get_domain, __sched, default_domain()); 317 }); 318 } 319 } __get_late_domain{}; 320 321 template <class _Sender, class _Env> 322 using __late_domain_of_t = __call_result_t<__get_late_domain_t, _Sender, _Env>; 323 324 namespace __domain 325 { 326 struct __common_domain_fn 327 { 328 static auto __common_domain() noexcept -> default_domain 329 { 330 return {}; 331 } 332 333 template <class _Domain, class... _OtherDomains> 334 requires __all_of<_Domain, _OtherDomains...> 335 static auto __common_domain(_Domain __domain, _OtherDomains...) noexcept 336 -> _Domain 337 { 338 return static_cast<_Domain&&>(__domain); 339 } 340 341 template <class... _Domains> 342 static auto __common_domain(_Domains...) noexcept // 343 -> __if_c<__one_of<dependent_domain, _Domains...>, dependent_domain, 344 __none_such> 345 { 346 return {}; 347 } 348 349 auto operator()(__ignore, __ignore, const auto&... __sndrs) const noexcept 350 { 351 return __common_domain(__get_early_domain(__sndrs)...); 352 } 353 }; 354 355 template <class... _Senders> 356 using __common_domain_t = // 357 __call_result_t<__common_domain_fn, int, int, _Senders...>; 358 359 template <class... _Senders> 360 concept __has_common_domain = // 361 __none_of<__none_such, __common_domain_t<_Senders...>>; 362 } // namespace __domain 363 } // namespace stdexec 364