26
26
#include < tuple>
27
27
#include < type_traits>
28
28
#include < utility>
29
+ #include < version>
30
+ #include < asioexec/as_default_on.hpp>
29
31
#include < asioexec/asio_config.hpp>
30
32
#include < stdexec/execution.hpp>
31
33
@@ -68,7 +70,32 @@ namespace asioexec {
68
70
};
69
71
70
72
template <typename T, typename U>
71
- requires std::is_same_v<T&&, U&&> || std::is_convertible_v<U&&, T&&>
73
+ inline constexpr bool at_least_as_const_qualified_v = std::is_const_v<T> || !std::is_const_v<U>;
74
+ template <typename T, typename U>
75
+ inline constexpr bool at_least_as_volatile_qualified_v = std::is_volatile_v<T>
76
+ || !std::is_volatile_v<U>;
77
+ template <typename T, typename U>
78
+ inline constexpr bool at_least_as_qualified_v = at_least_as_const_qualified_v<T, U>
79
+ && at_least_as_volatile_qualified_v<T, U>;
80
+
81
+ template <typename T, typename U>
82
+ requires
83
+ #ifdef __cpp_lib_reference_from_temporary
84
+ (std::is_convertible_v<U &&, T &&> && !std::reference_converts_from_temporary_v<T &&, U &&>)
85
+ #else
86
+ (
87
+ // Just using is_base_of_v is insufficient because it always reports false for built-in types
88
+ (std::is_base_of_v<std::remove_cvref_t <T>, std::remove_cvref_t <U>>
89
+ || std::is_same_v<std::remove_cvref_t <T>, std::remove_cvref_t <U>>)
90
+ &&
91
+ // The returned type must be at least as cv-qualified as the input type (it can be more cv-qualified)
92
+ at_least_as_qualified_v<std::remove_reference_t <T>, std::remove_reference_t <U>>
93
+ && (
94
+ // Reference type must agree except...
95
+ (std::is_lvalue_reference_v<T> == std::is_lvalue_reference_v<T>) ||
96
+ // ...special rules for const& which allows rvalues to bind thereto
97
+ (std::is_lvalue_reference_v<T> && std::is_const_v<std::remove_reference_t <T>>) ))
98
+ #endif
72
99
constexpr T&& convert(U&& u) noexcept {
73
100
return static_cast <U&&>(u);
74
101
}
@@ -109,18 +136,6 @@ namespace asioexec {
109
136
::stdexec::set_stopped_t()
110
137
>;
111
138
112
- struct stop_callback {
113
- constexpr explicit stop_callback (asio_impl::cancellation_signal& signal) noexcept
114
- : signal_(signal) {
115
- }
116
-
117
- void operator ()() && noexcept {
118
- signal_.emit (asio_impl::cancellation_type::partial);
119
- }
120
- private:
121
- asio_impl::cancellation_signal& signal_;
122
- };
123
-
124
139
template <typename , typename >
125
140
class completion_handler ;
126
141
@@ -141,7 +156,7 @@ namespace asioexec {
141
156
std::recursive_mutex m_;
142
157
frame_* frames_{nullptr };
143
158
std::exception_ptr ex_;
144
- completion_handler<Signatures, Receiver>* h_{ nullptr };
159
+ bool abandoned_{ false };
145
160
146
161
class frame_ {
147
162
operation_state_base& self_;
@@ -161,10 +176,11 @@ namespace asioexec {
161
176
if (l_) {
162
177
STDEXEC_ASSERT (self_.frames_ == this );
163
178
self_.frames_ = prev_;
164
- if (!self_.frames_ && ! self_.h_ ) {
179
+ if (!self_.frames_ && self_.abandoned_ ) {
165
180
// We are the last frame and the handler is gone so it's up to us to
166
181
// finalize the operation
167
182
l_.unlock ();
183
+ self_.callback_ .reset ();
168
184
if (self_.ex_ ) {
169
185
::stdexec::set_error (static_cast <Receiver&&>(self_.r_), std::move(self_.ex_));
170
186
} else {
@@ -206,9 +222,18 @@ namespace asioexec {
206
222
}
207
223
}
208
224
protected:
225
+ struct on_stop_request_ {
226
+ void operator ()() && noexcept {
227
+ const std::lock_guard l (self_.m_ );
228
+ self_.signal_ .emit (asio_impl::cancellation_type::all);
229
+ }
230
+
231
+ operation_state_base& self_;
232
+ };
233
+ public:
209
234
std::optional<::stdexec::stop_callback_for_t <
210
235
::stdexec::stop_token_of_t <::stdexec::env_of_t <Receiver>>,
211
- stop_callback
236
+ on_stop_request_
212
237
>>
213
238
callback_;
214
239
};
@@ -219,16 +244,10 @@ namespace asioexec {
219
244
public:
220
245
explicit completion_handler (operation_state_base<Signatures, Receiver>& self) noexcept
221
246
: self_(&self) {
222
- STDEXEC_ASSERT (!self_->h_ );
223
- self_->h_ = this ;
224
247
}
225
248
226
249
completion_handler (completion_handler&& other) noexcept
227
250
: self_(std::exchange(other.self_, nullptr )) {
228
- if (self_) {
229
- const std::lock_guard l (self_->m_ );
230
- self_->h_ = this ;
231
- }
232
251
}
233
252
234
253
completion_handler& operator =(const completion_handler&) = delete ;
@@ -239,7 +258,7 @@ namespace asioexec {
239
258
// it might defer that to the executor frames above us on the call stack
240
259
// (if any)
241
260
const typename operation_state_base<Signatures, Receiver>::frame_ frame (*self_);
242
- self_->h_ = nullptr ;
261
+ self_->abandoned_ = true ;
243
262
}
244
263
}
245
264
@@ -253,6 +272,7 @@ namespace asioexec {
253
272
}
254
273
STDEXEC_ASSERT (!self_->frames_ );
255
274
}
275
+ self_->callback_ .reset ();
256
276
if (self_->ex_ ) {
257
277
::stdexec::set_error (static_cast <Receiver&&>(self_->r_), std::move(self_->ex_));
258
278
} else {
@@ -315,14 +335,16 @@ namespace asioexec {
315
335
if (!base_::ex_) {
316
336
base_::ex_ = std::current_exception ();
317
337
}
318
- return ;
338
+ // It's important that we fallthrough here, just because the
339
+ // initiating function threw doesn't mean that there's no outstanding
340
+ // operations
319
341
}
320
342
// In the case of an immediate completion *this may already be outside its
321
343
// lifetime so we can't proceed into the branch
322
344
if (frame) {
323
345
base_::callback_.emplace (
324
346
::stdexec::get_stop_token (::stdexec::get_env(base_::r_)),
325
- stop_callback( base_::signal_) );
347
+ typename base_::on_stop_request_{* this } );
326
348
}
327
349
}
328
350
};
@@ -490,7 +512,11 @@ namespace asioexec {
490
512
491
513
} // namespace detail::completion_token
492
514
493
- struct completion_token_t { };
515
+ struct completion_token_t {
516
+ static constexpr auto as_default_on = asioexec::as_default_on<completion_token_t >;
517
+ template <typename IoObject>
518
+ using as_default_on_t = asioexec::as_default_on_t <completion_token_t , IoObject>;
519
+ };
494
520
495
521
inline const completion_token_t completion_token{};
496
522
0 commit comments