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