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 <memory> 21 22 #include "__meta.hpp" 23 #include "__env.hpp" 24 #include "__receivers.hpp" 25 #include "__env.hpp" 26 #include "__scope.hpp" 27 #include "__submit.hpp" 28 #include "__transform_sender.hpp" 29 30 namespace stdexec { 31 ///////////////////////////////////////////////////////////////////////////// 32 // [execution.senders.consumer.start_detached] 33 namespace __start_detached { 34 struct __submit_receiver { 35 using receiver_concept = receiver_t; 36 using __t = __submit_receiver; 37 using __id = __submit_receiver; 38 39 template <class... _As> set_valuestdexec::__start_detached::__submit_receiver40 void set_value(_As&&...) noexcept { 41 } 42 43 template <class _Error> 44 [[noreturn]] set_errorstdexec::__start_detached::__submit_receiver45 void set_error(_Error&&) noexcept { 46 // A detached operation failed. There is noplace for the error to go. 47 // This is unrecoverable, so we terminate. 48 std::terminate(); 49 } 50 set_stoppedstdexec::__start_detached::__submit_receiver51 void set_stopped() noexcept { 52 } 53 54 [[nodiscard]] get_envstdexec::__start_detached::__submit_receiver55 auto get_env() const noexcept -> __root_env { 56 return {}; 57 } 58 }; 59 60 template <class _SenderId, class _EnvId> 61 struct __operation : __immovable { 62 using _Sender = __cvref_t<_SenderId>; 63 using _Env = __t<_EnvId>; 64 __operationstdexec::__start_detached::__operation65 explicit __operation(connect_t, _Sender&& __sndr, _Env __env) 66 : __env_(static_cast<_Env&&>(__env)) 67 , __op_data_(static_cast<_Sender&&>(__sndr), __receiver{this}) { 68 } 69 __operationstdexec::__start_detached::__operation70 explicit __operation(_Sender&& __sndr, _Env __env) 71 : __operation(connect, static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env)) { 72 // If the operation completes synchronously, then the following line will cause 73 // the destruction of *this, which is not a problem because we used a delegating 74 // constructor, so *this is considered fully constructed. 75 __op_data_.submit(static_cast<_Sender&&>(__sndr), __receiver{this}); 76 } 77 __destroy_deletestdexec::__start_detached::__operation78 static void __destroy_delete(__operation* __self) noexcept { 79 if constexpr (__callable<get_allocator_t, _Env>) { 80 auto __alloc = stdexec::get_allocator(__self->__env_); 81 using _Alloc = decltype(__alloc); 82 using _OpAlloc = std::allocator_traits<_Alloc>::template rebind_alloc<__operation>; 83 _OpAlloc __op_alloc{__alloc}; 84 std::allocator_traits<_OpAlloc>::destroy(__op_alloc, __self); 85 std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __self, 1); 86 } else { 87 delete __self; 88 } 89 } 90 91 // The start_detached receiver deletes the operation state. 92 struct __receiver { 93 using receiver_concept = receiver_t; 94 using __t = __receiver; 95 using __id = __receiver; 96 97 template <class... _As> set_valuestdexec::__start_detached::__operation::__receiver98 void set_value(_As&&...) noexcept { 99 __operation::__destroy_delete(__op_); // NB: invalidates *this 100 } 101 102 template <class _Error> 103 [[noreturn]] set_errorstdexec::__start_detached::__operation::__receiver104 void set_error(_Error&&) noexcept { 105 // A detached operation failed. There is noplace for the error to go. 106 // This is unrecoverable, so we terminate. 107 std::terminate(); 108 } 109 set_stoppedstdexec::__start_detached::__operation::__receiver110 void set_stopped() noexcept { 111 __operation::__destroy_delete(__op_); // NB: invalidates *this 112 } 113 get_envstdexec::__start_detached::__operation::__receiver114 auto get_env() const noexcept -> const _Env& { 115 return __op_->__env_; 116 } 117 118 __operation* __op_; 119 }; 120 121 STDEXEC_ATTRIBUTE(no_unique_address) _Env __env_; 122 STDEXEC_ATTRIBUTE(no_unique_address) submit_result<_Sender, __receiver> __op_data_; 123 }; 124 125 template <class _Sender, class _Env> 126 concept __use_submit = __submittable<_Sender, __submit_receiver> && __same_as<_Env, __root_env> 127 && __same_as<void, __submit_result_t<_Sender, __submit_receiver>>; 128 129 struct start_detached_t { 130 template <sender_in<__root_env> _Sender> 131 requires __callable< 132 apply_sender_t, 133 __late_domain_of_t<_Sender, __root_env, __early_domain_of_t<_Sender>>, 134 start_detached_t, 135 _Sender 136 > operator ()stdexec::__start_detached::start_detached_t137 void operator()(_Sender&& __sndr) const { 138 auto __domain = __get_late_domain(__sndr, __root_env{}, __get_early_domain(__sndr)); 139 stdexec::apply_sender(__domain, *this, static_cast<_Sender&&>(__sndr)); 140 } 141 142 template <class _Env, sender_in<__as_root_env_t<_Env>> _Sender> 143 requires __callable< 144 apply_sender_t, 145 __late_domain_of_t<_Sender, __as_root_env_t<_Env>, __early_domain_of_t<_Sender>>, 146 start_detached_t, 147 _Sender, 148 __as_root_env_t<_Env> 149 > operator ()stdexec::__start_detached::start_detached_t150 void operator()(_Sender&& __sndr, _Env&& __env) const { 151 auto __env2 = __as_root_env(static_cast<_Env&&>(__env)); 152 auto __domain = __get_late_domain(__sndr, __env2, __get_early_domain(__sndr)); 153 stdexec::apply_sender(__domain, *this, static_cast<_Sender&&>(__sndr), __env2); 154 } 155 156 // Below is the default implementation for `start_detached`. 157 template <class _Sender, class _Env = __root_env> 158 requires sender_in<_Sender, __as_root_env_t<_Env>> apply_senderstdexec::__start_detached::start_detached_t159 void apply_sender(_Sender&& __sndr, _Env&& __env = {}) const noexcept(false) { 160 using _Op = __operation<__cvref_id<_Sender>, __id<__decay_t<_Env>>>; 161 162 #if !STDEXEC_APPLE_CLANG() // There seems to be a codegen bug in apple clang that causes 163 // `start_detached` to segfault when the code path below is 164 // taken. 165 // BUGBUG NOT TO SPEC: the use of the non-standard `submit` algorithm here is a 166 // conforming extension. 167 if constexpr (__use_submit<_Sender, _Env>) { 168 // If submit(sndr, rcvr) returns void, then no state needs to be kept alive 169 // for the operation. We can just call submit and return. 170 stdexec::__submit::__submit(static_cast<_Sender&&>(__sndr), __submit_receiver{}); 171 } else 172 #endif 173 if constexpr (__callable<get_allocator_t, _Env>) { 174 // Use the provided allocator if any to allocate the operation state. 175 auto __alloc = get_allocator(__env); 176 using _Alloc = decltype(__alloc); 177 using _OpAlloc = std::allocator_traits<_Alloc>::template rebind_alloc<_Op>; 178 // We use the allocator to allocate the op state and also to construct it. 179 _OpAlloc __op_alloc{__alloc}; 180 _Op* __op = std::allocator_traits<_OpAlloc>::allocate(__op_alloc, 1); __anon311ab2ec0102() 181 __scope_guard __g{[__op, &__op_alloc]() noexcept { 182 std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __op, 1); 183 }}; 184 // This can potentially throw. If it does, the scope guard will deallocate the 185 // storage automatically. 186 std::allocator_traits<_OpAlloc>::construct( 187 __op_alloc, __op, static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env)); 188 // The operation state is now constructed, dismiss the scope guard. 189 __g.__dismiss(); 190 } else { 191 // The caller did not provide an allocator, so we use the default allocator. 192 [[maybe_unused]] 193 _Op* __op = new _Op(static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env)); 194 // The operation has now started and is responsible for deleting itself when it 195 // completes. 196 } 197 } 198 }; 199 } // namespace __start_detached 200 201 using __start_detached::start_detached_t; 202 inline constexpr start_detached_t start_detached{}; 203 } // namespace stdexec 204