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