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