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...>
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
87 template <class _Sender>
__is_nothrow_transform_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>
__is_nothrow_transform_sender()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))
transform_senderstdexec::default_domain130 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))
transform_senderstdexec::default_domain155 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>
transform_envstdexec::default_domain191 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>
operator ()stdexec::__get_early_domain_t256 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>
operator ()stdexec::__get_late_domain_t285 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>
operator ()stdexec::__get_late_domain_t311 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 {
__common_domainstdexec::__domain::__common_domain_fn328 static auto __common_domain() noexcept -> default_domain
329 {
330 return {};
331 }
332
333 template <class _Domain, class... _OtherDomains>
334 requires __all_of<_Domain, _OtherDomains...>
__common_domainstdexec::__domain::__common_domain_fn335 static auto __common_domain(_Domain __domain, _OtherDomains...) noexcept
336 -> _Domain
337 {
338 return static_cast<_Domain&&>(__domain);
339 }
340
341 template <class... _Domains>
__common_domainstdexec::__domain::__common_domain_fn342 static auto __common_domain(_Domains...) noexcept //
343 -> __if_c<__one_of<dependent_domain, _Domains...>, dependent_domain,
344 __none_such>
345 {
346 return {};
347 }
348
operator ()stdexec::__domain::__common_domain_fn349 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