xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__submit.hpp (revision 10d0b4b7d1498cfd5c3d37edea271a54d1984e41)
1 /*
2  * Copyright (c) 2021-2025 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 "__manual_lifetime.hpp"
21 #include "__operation_states.hpp"
22 #include "__optional.hpp"
23 #include "__senders.hpp"
24 #include "__type_traits.hpp"
25 
26 namespace stdexec {
27   namespace __submit {
28     template <class _Sender, class _Receiver>
29     concept __has_memfn = requires(_Sender && (*__sndr)(), _Receiver && (*__rcvr)()) {
30       __sndr().submit(__rcvr());
31     };
32 
33     template <class _Sender, class _Receiver>
34     concept __has_static_memfn = requires(_Sender && (*__sndr)(), _Receiver && (*__rcvr)()) {
35       __decay_t<_Sender>::submit(__sndr(), __rcvr());
36     };
37 
38     // submit is a combination of connect and start. it is customizable for times when it
39     // can be done more efficiently than by calling connect and start directly.
40     struct __submit_t {
41       struct __void { };
42 
43       // This implementation is used if the sender has a non-static submit member function.
44       template <class _Sender, class _Receiver, class _Default = __void>
45         requires sender_to<_Sender, _Receiver> && __submit::__has_memfn<_Sender, _Receiver>
STDEXEC_ATTRIBUTEstdexec::__submit::__submit_t46       STDEXEC_ATTRIBUTE(host, device, always_inline)
47       auto
48         operator()(_Sender&& __sndr, _Receiver __rcvr, [[maybe_unused]] _Default __def = {}) const
49         noexcept(noexcept(static_cast<_Sender&&>(__sndr)
50                             .submit(static_cast<_Receiver&&>(__rcvr)))) {
51         using __result_t = decltype(static_cast<_Sender&&>(__sndr)
52                                       .submit(static_cast<_Receiver&&>(__rcvr)));
53         if constexpr (__same_as<__result_t, void> && !__same_as<_Default, __void>) {
54           static_cast<_Sender&&>(__sndr).submit(static_cast<_Receiver&&>(__rcvr));
55           return __def;
56         } else {
57           return static_cast<_Sender&&>(__sndr).submit(static_cast<_Receiver&&>(__rcvr));
58         }
59       }
60 
61       // This implementation is used if the sender has a static submit member function.
62       template <class _Sender, class _Receiver, class _Default = __void>
63         requires sender_to<_Sender, _Receiver> && __submit::__has_static_memfn<_Sender, _Receiver>
STDEXEC_ATTRIBUTEstdexec::__submit::__submit_t64       STDEXEC_ATTRIBUTE(host, device, always_inline)
65       auto
66         operator()(_Sender&& __sndr, _Receiver __rcvr, [[maybe_unused]] _Default __def = {}) const
67         noexcept(noexcept(
68           __sndr.submit(static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr)))) {
69         using __result_t =
70           decltype(__sndr.submit(static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr)));
71         if constexpr (__same_as<__result_t, void> && !__same_as<_Default, __void>) {
72           __sndr.submit(static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr));
73           return __def;
74         } else {
75           return __sndr.submit(static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr));
76         }
77       }
78     };
79 
80     inline constexpr __submit_t __submit{};
81   } // namespace __submit
82 
83   template <class _Sender, class _Receiver, class _Default = __submit::__submit_t::__void>
84   using __submit_result_t = __call_result_t<__submit::__submit_t, _Sender, _Receiver, _Default>;
85 
86   template <class _Sender, class _Receiver>
87   concept __submittable = requires(_Sender&& __sndr, _Receiver&& __rcvr) {
88     __submit::__submit(static_cast<_Sender &&>(__sndr), static_cast<_Receiver &&>(__rcvr));
89   };
90 
91   template <class _Sender, class _Receiver>
92   concept __nothrow_submittable =
93     __submittable<_Sender, _Receiver> && requires(_Sender&& __sndr, _Receiver&& __rcvr) {
94       {
95         __submit::__submit(static_cast<_Sender &&>(__sndr), static_cast<_Receiver &&>(__rcvr))
96       } noexcept;
97     };
98 
99   enum class __submit_result_kind {
100     __connect,
101     __submit,
102     __submit_void,
103     __submit_nothrow,
104   };
105 
106   template <class _Sender, class _Receiver>
__get_submit_result_kind()107   constexpr auto __get_submit_result_kind() noexcept -> __submit_result_kind {
108     if constexpr (__submittable<_Sender, _Receiver>) {
109       using __result_t = __submit_result_t<_Sender, _Receiver>;
110       constexpr std::size_t __opstate_size = sizeof(connect_result_t<_Sender, _Receiver>);
111 
112       if constexpr (std::is_void_v<__result_t>) {
113         return __submit_result_kind::__submit_void;
114       } else if constexpr (__nothrow_submittable<_Sender, _Receiver>) {
115         return __opstate_size > sizeof(__result_t) ? __submit_result_kind::__submit_nothrow
116                                                    : __submit_result_kind::__connect;
117       } else {
118         return __opstate_size > sizeof(__optional<__result_t>) ? __submit_result_kind::__submit
119                                                                : __submit_result_kind::__connect;
120       }
121     }
122     return __submit_result_kind::__connect;
123   }
124 
125   template <
126     class _Sender,
127     class _Receiver,
128     __submit_result_kind _Kind = __get_submit_result_kind<_Sender, _Receiver>()
129   >
130   struct submit_result {
131     using __result_t = connect_result_t<_Sender, _Receiver>;
132 
submit_resultstdexec::submit_result133     explicit submit_result(_Sender&& __sndr, _Receiver&& __rcvr)
134       noexcept(__nothrow_connectable<_Sender, _Receiver>)
135       : __result_(connect(static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr))) {
136     }
137 
submitstdexec::submit_result138     void submit(_Sender&&, _Receiver&&) noexcept {
139       stdexec::start(__result_);
140     }
141 
142     __result_t __result_;
143   };
144 
145   template <class _Sender, class _Receiver>
146   struct submit_result<_Sender, _Receiver, __submit_result_kind::__submit> {
147     using __result_t = __submit_result_t<_Sender, _Receiver>;
148 
submit_resultstdexec::submit_result149     submit_result(_Sender&&, _Receiver&&) noexcept {
150     }
151 
submitstdexec::submit_result152     void submit(_Sender&& __sndr, _Receiver&& __rcvr) {
153       __result_.__emplace_from(
154         __submit::__submit, static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr));
155     }
156 
157     __optional<__result_t> __result_;
158   };
159 
160   template <class _Sender, class _Receiver>
161   struct submit_result<_Sender, _Receiver, __submit_result_kind::__submit_void> {
162     using __result_t = __submit_result_t<_Sender, _Receiver>;
163 
submit_resultstdexec::submit_result164     explicit submit_result(_Sender&&, _Receiver&&) noexcept {
165     }
166 
submitstdexec::submit_result167     void submit(_Sender&& __sndr, _Receiver&& __rcvr)
168       noexcept(__nothrow_submittable<_Sender, _Receiver>) {
169       __submit::__submit(static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr));
170     }
171   };
172 
173   template <class _Sender, class _Receiver>
174   struct submit_result<_Sender, _Receiver, __submit_result_kind::__submit_nothrow> {
175     using __result_t = __submit_result_t<_Sender, _Receiver>;
176 
submit_resultstdexec::submit_result177     submit_result(_Sender&&, _Receiver&&) noexcept {
178     }
179 
submitstdexec::submit_result180     void submit(_Sender&& __sndr, _Receiver&& __rcvr) noexcept {
181       __result_.__construct_from(
182         __submit::__submit, static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr));
183     }
184 
~submit_resultstdexec::submit_result185     ~submit_result() {
186       __result_.__destroy();
187     }
188 
189     __manual_lifetime<__result_t> __result_;
190   };
191 
192   template <class _Sender, class _Receiver>
193   submit_result(_Sender&&, _Receiver) -> submit_result<_Sender, _Receiver>;
194 
195 } // namespace stdexec
196