Skip to content

Commit a4e0899

Browse files
committed
merge the __submit implementation into start_detached for stdexec/
1 parent c25517a commit a4e0899

4 files changed

Lines changed: 72 additions & 161 deletions

File tree

include/stdexec/__detail/__receiver_ref.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include "__execution_fwd.hpp"
1919

2020
#include "__completion_signatures.hpp"
21-
#include "__env.hpp"
2221
#include "__receivers.hpp"
2322

2423
#include <functional>

include/stdexec/__detail/__start_detached.hpp

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,45 +20,74 @@
2020
#include "__meta.hpp"
2121
#include "__env.hpp"
2222
#include "__receivers.hpp"
23-
#include "__submit.hpp"
23+
#include "__env.hpp"
24+
#include "__scope.hpp"
2425
#include "__transform_sender.hpp"
25-
#include "__type_traits.hpp"
2626

2727
namespace stdexec {
2828
/////////////////////////////////////////////////////////////////////////////
2929
// [execution.senders.consumer.start_detached]
3030
namespace __start_detached {
31-
template <class _EnvId>
32-
struct __detached_receiver {
33-
using _Env = stdexec::__t<_EnvId>;
31+
template <class _SenderId, class _EnvId>
32+
struct __operation {
33+
using _Sender = __cvref_t<_SenderId>;
34+
using _Env = __t<_EnvId>;
35+
36+
explicit __operation(_Sender&& __sndr, _Env __env)
37+
: __env_(static_cast<_Env&&>(__env))
38+
, __op_state_(connect(static_cast<_Sender&&>(__sndr), __receiver{this})) {
39+
}
3440

35-
struct __t {
41+
// If the operation state was allocated with a user-provided allocator, then we must
42+
// use the allocator stored within the operation state to destroy the operation
43+
// state. This is a good time to use C++20's destroying delete operation.
44+
static void operator delete(__operation* __self, std::destroying_delete_t) noexcept {
45+
if constexpr (__callable<get_allocator_t, _Env>) {
46+
auto __alloc = stdexec::get_allocator(__self->__env_);
47+
using _Alloc = decltype(__alloc);
48+
using _OpAlloc =
49+
typename std::allocator_traits<_Alloc>::template rebind_alloc<__operation>;
50+
_OpAlloc __op_alloc{__alloc};
51+
std::allocator_traits<_OpAlloc>::destroy(__op_alloc, __self);
52+
std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __self, 1);
53+
} else {
54+
std::destroy_at(__self);
55+
::operator delete(__self);
56+
}
57+
}
58+
59+
// The start_detached receiver deletes the operation state.
60+
struct __receiver {
3661
using receiver_concept = receiver_t;
37-
using __id = __detached_receiver;
38-
STDEXEC_ATTRIBUTE((no_unique_address)) _Env __env_;
62+
using __t = __receiver;
63+
using __id = __receiver;
64+
__operation* __op_;
3965

4066
template <class... _As>
4167
void set_value(_As&&...) noexcept {
68+
delete __op_; // NB: invalidates *this
4269
}
4370

4471
template <class _Error>
4572
[[noreturn]]
4673
void set_error(_Error&&) noexcept {
74+
// A detached operation failed. There is noplace for the error to go.
75+
// This is unrecoverable, so we terminate.
4776
std::terminate();
4877
}
4978

5079
void set_stopped() noexcept {
80+
delete __op_; // NB: invalidates *this
5181
}
5282

5383
auto get_env() const noexcept -> const _Env& {
54-
// BUGBUG NOT TO SPEC
55-
return __env_;
84+
return __op_->__env_;
5685
}
5786
};
58-
};
5987

60-
template <class _Env = env<>>
61-
using __detached_receiver_t = __t<__detached_receiver<__id<__decay_t<_Env>>>>;
88+
STDEXEC_ATTRIBUTE((no_unique_address)) _Env __env_;
89+
connect_result_t<_Sender, __receiver> __op_state_;
90+
};
6291

6392
struct start_detached_t {
6493
template <sender_in<__root_env> _Sender>
@@ -84,11 +113,37 @@ namespace stdexec {
84113
__as_root_env(static_cast<_Env&&>(__env)));
85114
}
86115

116+
// Below is the default implementation for `start_detached`.
87117
template <class _Sender, class _Env = __root_env>
88-
requires sender_to<_Sender, __detached_receiver_t<_Env>>
89-
void apply_sender(_Sender&& __sndr, _Env&& __env = {}) const {
90-
__submit(
91-
static_cast<_Sender&&>(__sndr), __detached_receiver_t<_Env>{static_cast<_Env&&>(__env)});
118+
requires sender_in<_Sender, __as_root_env_t<_Env>>
119+
void apply_sender(_Sender&& __sndr, _Env&& __env = {}) const noexcept(false) {
120+
using _Op = __operation<__cvref_id<_Sender>, __id<__decay_t<_Env>>>;
121+
// Use the provided allocator, if any, to allocate the operation state.
122+
if constexpr (__callable<get_allocator_t, _Env>) {
123+
auto __alloc = get_allocator(__env);
124+
using _Alloc = decltype(__alloc);
125+
using _OpAlloc = typename std::allocator_traits<_Alloc>::template rebind_alloc<_Op>;
126+
// We use the allocator to allocate the operation state and also to construct
127+
// it.
128+
_OpAlloc __op_alloc{__alloc};
129+
_Op* __op = std::allocator_traits<_OpAlloc>::allocate(__op_alloc, 1);
130+
__scope_guard __g{[__op, &__op_alloc]() noexcept {
131+
std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __op, 1);
132+
}};
133+
// This can potentially throw. If it does, the scope guard will deallocate the
134+
// storage automatically.
135+
std::allocator_traits<_OpAlloc>::construct(
136+
__op_alloc, __op, static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env));
137+
// The operation state is now constructed, dismiss the scope guard.
138+
__g.__dismiss();
139+
// This cannot throw:
140+
stdexec::start(__op->__op_state_);
141+
} else {
142+
// The caller did not provide an allocator, so we use the default allocator.
143+
auto* __op = new __operation<__id<_Sender>, __id<__decay_t<_Env>>>{
144+
static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env)};
145+
start(__op->__op_state_);
146+
}
92147
}
93148
};
94149
} // namespace __start_detached

include/stdexec/__detail/__submit.hpp

Lines changed: 0 additions & 142 deletions
This file was deleted.

include/stdexec/execution.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
#include "__detail/__starts_on.hpp" // IWYU pragma: export
5353
#include "__detail/__stopped_as_error.hpp" // IWYU pragma: export
5454
#include "__detail/__stopped_as_optional.hpp" // IWYU pragma: export
55-
#include "__detail/__submit.hpp" // IWYU pragma: export
5655
#include "__detail/__sync_wait.hpp" // IWYU pragma: export
5756
#include "__detail/__then.hpp" // IWYU pragma: export
5857
#include "__detail/__transfer_just.hpp" // IWYU pragma: export

0 commit comments

Comments
 (0)