xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__domain.hpp (revision 10d0b4b7d1498cfd5c3d37edea271a54d1984e41)
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 "__execution_fwd.hpp"
19 
20 #include "__config.hpp"
21 #include "__concepts.hpp"
22 #include "__env.hpp"
23 #include "__sender_introspection.hpp"
24 #include "__meta.hpp"
25 
26 #include "../functional.hpp"
27 #include "__utility.hpp"
28 
29 namespace stdexec {
30 
31   struct default_domain;
32   struct dependent_domain;
33 
34   namespace __detail {
35     template <class _DomainOrTag, class _Sender, class... _Env>
36     concept __has_transform_sender =
37       requires(_DomainOrTag __tag, _Sender&& __sender, const _Env&... __env) {
38         __tag.transform_sender(static_cast<_Sender &&>(__sender), __env...);
39       };
40 
41     template <class _DomainOrTag, class _Sender, class... _Env>
42     concept __has_nothrow_transform_sender =
43       requires(_DomainOrTag __tag, _Sender&& __sender, const _Env&... __env) {
44         { __tag.transform_sender(static_cast<_Sender &&>(__sender), __env...) } noexcept;
45       };
46 
47     template <class _Sender, class... _Env>
48     concept __has_default_transform_sender =
49       sender_expr<_Sender> && __has_transform_sender<tag_of_t<_Sender>, _Sender, _Env...>;
50 
51     template <class _DomainOrTag, class _Sender, class... _Env>
52     using __transform_sender_result_t =
53       decltype(_DomainOrTag{}.transform_sender(__declval<_Sender>(), __declval<const _Env&>()...));
54 
55     template <class _DomainOrTag, class _Sender, class _Env>
56     concept __has_transform_env = requires(_DomainOrTag __tag, _Sender&& __sender, _Env&& __env) {
57       __tag.transform_env(static_cast<_Sender &&>(__sender), static_cast<_Env &&>(__env));
58     };
59 
60     template <class _Sender, class _Env>
61     concept __has_default_transform_env = sender_expr<_Sender>
62                                        && __has_transform_env<tag_of_t<_Sender>, _Sender, _Env>;
63 
64     template <class _DomainOrTag, class _Sender, class _Env>
65     using __transform_env_result_t =
66       decltype(_DomainOrTag{}.transform_env(__declval<_Sender>(), __declval<_Env>()));
67 
68     template <class _DomainOrTag, class... _Args>
69     concept __has_apply_sender = requires(_DomainOrTag __tag, _Args&&... __args) {
70       __tag.apply_sender(static_cast<_Args &&>(__args)...);
71     };
72 
73     template <class _Tag, class... _Args>
74     using __apply_sender_result_t = decltype(_Tag{}.apply_sender(__declval<_Args>()...));
75 
76     ////////////////////////////////////////////////////////////////////////////////////////////////
77     template <class _Env, class _Tag>
78     using __completion_scheduler_for =
79       __meval_or<__call_result_t, __none_such, get_completion_scheduler_t<_Tag>, _Env>;
80 
81     template <class _Env, class _Tag>
82     using __completion_domain_for =
83       __meval_or<__call_result_t, __none_such, get_domain_t, __completion_scheduler_for<_Env, _Tag>>;
84 
85     // Check the value, error, and stopped channels for completion schedulers.
86     // Of the completion schedulers that are known, they must all have compatible
87     // domains. This computes that domain, or else returns __none_such if there
88     // are no completion schedulers or if they don't specify a domain.
89     template <class _Env>
90     struct __completion_domain_or_none_
91       : __mdefer_<
92           __mtransform<
93             __mbind_front_q<__completion_domain_for, _Env>,
94             __mremove<__none_such, __munique<__msingle_or<__none_such>>>
95           >,
96           set_value_t,
97           set_error_t,
98           set_stopped_t
99         > { };
100 
101     template <class _Sender>
102     using __completion_domain_or_none = __t<__completion_domain_or_none_<env_of_t<_Sender>>>;
103 
104     template <class _Sender>
105     concept __consistent_completion_domains = __mvalid<__completion_domain_or_none, _Sender>;
106 
107     template <class _Sender>
108     concept __has_completion_domain = (!same_as<__completion_domain_or_none<_Sender>, __none_such>);
109 
110     template <__has_completion_domain _Sender>
111     using __completion_domain_of = __completion_domain_or_none<_Sender>;
112 
113     ////////////////////////////////////////////////////////////////////////////////////////////////
114     //! Function object implementing `get-domain-early(snd)`
115     //! from [exec.snd.general] item 3.9. It is the first well-formed expression of
116     //! a) `get_domain(get_env(sndr))`
117     //! b) `completion-domain(sndr)`
118     //! c) `default_domain()`
119     struct __get_early_domain_t {
120       template <class _Sender, class _Default = default_domain>
operator ()stdexec::__detail::__get_early_domain_t121       auto operator()(const _Sender&, _Default = {}) const noexcept {
122         if constexpr (__callable<get_domain_t, env_of_t<_Sender>>) {
123           return __domain_of_t<env_of_t<_Sender>>();
124         } else if constexpr (__has_completion_domain<_Sender>) {
125           return __completion_domain_of<_Sender>();
126         } else {
127           return _Default();
128         }
129       }
130     };
131 
132     template <class _Sender, class _Default = default_domain>
133     using __early_domain_of_t = __call_result_t<__get_early_domain_t, _Sender, _Default>;
134 
135     ////////////////////////////////////////////////////////////////////////////////////////////////
136     //! Function object implementing `get-domain-late(snd)`
137     struct __get_late_domain_t {
138       // When connect is looking for a customization, it first checks if the sender has a
139       // late domain override. If so, that is the domain that is used to transform the
140       // sender. Otherwise, look to the receiver for information about where the resulting
141       // operation state will be started.
142       template <class _Sender, class _Env, class _Default = default_domain>
operator ()stdexec::__detail::__get_late_domain_t143       auto operator()(const _Sender& __sndr, const _Env& __env, _Default = {}) const noexcept {
144         // The schedule_from algorithm is the exception to the rule. It ignores the domain
145         // of the predecessor, and dispatches based on the domain of the scheduler to
146         // which execution is being transferred.
147         if constexpr (__callable<get_domain_override_t, env_of_t<_Sender>>) {
148           return get_domain_override(get_env(__sndr));
149         } else if constexpr (__callable<get_domain_t, const _Env&>) {
150           return get_domain(__env);
151         } else if constexpr (__callable<__composed<get_domain_t, get_scheduler_t>, const _Env&>) {
152           return get_domain(get_scheduler(__env));
153         } else {
154           return _Default();
155         }
156       }
157     };
158 
159     template <class _Sender, class _Env, class _Default = default_domain>
160     using __late_domain_of_t = __call_result_t<__get_late_domain_t, _Sender, _Env, _Default>;
161 
162     struct __common_domain_fn {
163       template <
164         class _Default = default_domain,
165         class _Dependent = dependent_domain,
166         class... _Domains
167       >
__common_domainstdexec::__detail::__common_domain_fn168       static auto __common_domain(_Domains...) noexcept {
169         if constexpr (sizeof...(_Domains) == 0) {
170           return _Default();
171         } else if constexpr (__one_of<_Dependent, _Domains...>) {
172           return _Dependent();
173         } else if constexpr (stdexec::__mvalid<std::common_type_t, _Domains...>) {
174           return std::common_type_t<_Domains...>();
175         } else {
176           return __none_such();
177         }
178       }
179 
operator ()stdexec::__detail::__common_domain_fn180       auto operator()(__ignore, __ignore, const auto&... __sndrs) const noexcept {
181         return __common_domain(__get_early_domain_t{}(__sndrs)...);
182       }
183     };
184   } // namespace __detail
185 
186   struct default_domain {
187     template <class _Sender, class... _Env>
188       requires __detail::__has_default_transform_sender<_Sender, _Env...>
STDEXEC_ATTRIBUTEstdexec::default_domain189     STDEXEC_ATTRIBUTE(always_inline)
190     auto transform_sender(_Sender&& __sndr, _Env&&... __env) const
191       noexcept(__detail::__has_nothrow_transform_sender<tag_of_t<_Sender>, _Sender, _Env...>)
192         -> __detail::__transform_sender_result_t<tag_of_t<_Sender>, _Sender, _Env...> {
193       return tag_of_t<_Sender>().transform_sender(static_cast<_Sender&&>(__sndr), __env...);
194     }
195 
196     template <class _Sender, class... _Env>
STDEXEC_ATTRIBUTEstdexec::default_domain197     STDEXEC_ATTRIBUTE(always_inline)
198     auto transform_sender(_Sender&& __sndr, _Env&&...) const
199       noexcept(__nothrow_constructible_from<_Sender, _Sender>) -> _Sender {
200       return static_cast<_Sender>(static_cast<_Sender&&>(__sndr));
201     }
202 
203     template <class _Sender, class _Env>
204       requires __detail::__has_default_transform_env<_Sender, _Env>
transform_envstdexec::default_domain205     auto transform_env(_Sender&& __sndr, _Env&& __env) const noexcept
206       -> __detail::__transform_env_result_t<tag_of_t<_Sender>, _Sender, _Env> {
207       return tag_of_t<_Sender>()
208         .transform_env(static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env));
209     }
210 
211     template <class _Env>
transform_envstdexec::default_domain212     auto transform_env(__ignore, _Env&& __env) const noexcept -> _Env {
213       return static_cast<_Env>(static_cast<_Env&&>(__env));
214     }
215 
216     template <class _Tag, class... _Args>
217       requires __detail::__has_apply_sender<_Tag, _Args...>
STDEXEC_ATTRIBUTEstdexec::default_domain218     STDEXEC_ATTRIBUTE(always_inline)
219     auto apply_sender(_Tag, _Args&&... __args) const
220       -> __detail::__apply_sender_result_t<_Tag, _Args...> {
221       return _Tag().apply_sender(static_cast<_Args&&>(__args)...);
222     }
223   };
224 
225   inline constexpr __detail::__get_early_domain_t __get_early_domain{};
226   inline constexpr __detail::__get_late_domain_t __get_late_domain{};
227   using __detail::__early_domain_of_t;
228   using __detail::__late_domain_of_t;
229 
230   //////////////////////////////////////////////////////////////////////////////////////////////////
231   // dependent_domain
232   struct dependent_domain {
233     // defined in __transform_sender.hpp
234     template <class _Sender, class _Env>
235     static constexpr auto __is_nothrow_transform_sender() noexcept -> bool;
236 
237     // defined in __transform_sender.hpp
238     template <sender_expr _Sender, class _Env>
239       requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
240     STDEXEC_ATTRIBUTE(always_inline)
241     auto transform_sender(_Sender&& __sndr, const _Env& __env) const
242       noexcept(__is_nothrow_transform_sender<_Sender, _Env>()) -> decltype(auto);
243   };
244 
245   template <class... _Senders>
246   using __common_domain_t = __call_result_t<__detail::__common_domain_fn, int, int, _Senders...>;
247 
248   template <class... _Senders>
249   concept __has_common_domain = __none_of<__none_such, __common_domain_t<_Senders...>>;
250 } // namespace stdexec
251