Skip to content

Commit daf12bc

Browse files
authored
Merge pull request #1620 from RobertLeahy/completion_token_20250825
asioexec::completion_token: Various Fixes/Updates
2 parents 64524f0 + 8bb5b46 commit daf12bc

File tree

6 files changed

+430
-32
lines changed

6 files changed

+430
-32
lines changed

include/asioexec/as_default_on.hpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
* Copyright (c) 2025 Robert Leahy. All rights reserved.
4+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
*
6+
* Licensed under the Apache License, Version 2.0 with LLVM Exceptions (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://llvm.org/LICENSE.txt
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#pragma once
20+
21+
#include <type_traits>
22+
#include <utility>
23+
#include <asioexec/asio_config.hpp>
24+
#include <asioexec/executor_with_default.hpp>
25+
26+
namespace asioexec {
27+
28+
template <typename CompletionToken, typename IoObject>
29+
using as_default_on_t =
30+
typename std::remove_cvref_t<IoObject>::template rebind_executor<executor_with_default<
31+
std::remove_cvref_t<decltype(std::declval<IoObject&>().get_executor())>,
32+
CompletionToken>>::other;
33+
34+
namespace detail::as_default_on {
35+
36+
template <typename CompletionToken>
37+
struct t {
38+
template <typename IoObject>
39+
constexpr asioexec::as_default_on_t<CompletionToken, IoObject>
40+
operator()(IoObject&& io) const {
41+
return asioexec::as_default_on_t<CompletionToken, IoObject>((IoObject&&) io);
42+
}
43+
};
44+
45+
} // namespace detail::as_default_on
46+
47+
template <typename CompletionToken>
48+
inline constexpr detail::as_default_on::t<CompletionToken> as_default_on;
49+
50+
} // namespace asioexec

include/asioexec/completion_token.hpp

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include <tuple>
2727
#include <type_traits>
2828
#include <utility>
29+
#include <version>
30+
#include <asioexec/as_default_on.hpp>
2931
#include <asioexec/asio_config.hpp>
3032
#include <stdexec/execution.hpp>
3133

@@ -68,7 +70,32 @@ namespace asioexec {
6870
};
6971

7072
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
7299
constexpr T&& convert(U&& u) noexcept {
73100
return static_cast<U&&>(u);
74101
}
@@ -109,18 +136,6 @@ namespace asioexec {
109136
::stdexec::set_stopped_t()
110137
>;
111138

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-
124139
template <typename, typename>
125140
class completion_handler;
126141

@@ -141,7 +156,7 @@ namespace asioexec {
141156
std::recursive_mutex m_;
142157
frame_* frames_{nullptr};
143158
std::exception_ptr ex_;
144-
completion_handler<Signatures, Receiver>* h_{nullptr};
159+
bool abandoned_{false};
145160

146161
class frame_ {
147162
operation_state_base& self_;
@@ -161,10 +176,11 @@ namespace asioexec {
161176
if (l_) {
162177
STDEXEC_ASSERT(self_.frames_ == this);
163178
self_.frames_ = prev_;
164-
if (!self_.frames_ && !self_.h_) {
179+
if (!self_.frames_ && self_.abandoned_) {
165180
// We are the last frame and the handler is gone so it's up to us to
166181
// finalize the operation
167182
l_.unlock();
183+
self_.callback_.reset();
168184
if (self_.ex_) {
169185
::stdexec::set_error(static_cast<Receiver&&>(self_.r_), std::move(self_.ex_));
170186
} else {
@@ -206,9 +222,18 @@ namespace asioexec {
206222
}
207223
}
208224
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:
209234
std::optional<::stdexec::stop_callback_for_t<
210235
::stdexec::stop_token_of_t<::stdexec::env_of_t<Receiver>>,
211-
stop_callback
236+
on_stop_request_
212237
>>
213238
callback_;
214239
};
@@ -219,16 +244,10 @@ namespace asioexec {
219244
public:
220245
explicit completion_handler(operation_state_base<Signatures, Receiver>& self) noexcept
221246
: self_(&self) {
222-
STDEXEC_ASSERT(!self_->h_);
223-
self_->h_ = this;
224247
}
225248

226249
completion_handler(completion_handler&& other) noexcept
227250
: self_(std::exchange(other.self_, nullptr)) {
228-
if (self_) {
229-
const std::lock_guard l(self_->m_);
230-
self_->h_ = this;
231-
}
232251
}
233252

234253
completion_handler& operator=(const completion_handler&) = delete;
@@ -239,7 +258,7 @@ namespace asioexec {
239258
// it might defer that to the executor frames above us on the call stack
240259
// (if any)
241260
const typename operation_state_base<Signatures, Receiver>::frame_ frame(*self_);
242-
self_->h_ = nullptr;
261+
self_->abandoned_ = true;
243262
}
244263
}
245264

@@ -253,6 +272,7 @@ namespace asioexec {
253272
}
254273
STDEXEC_ASSERT(!self_->frames_);
255274
}
275+
self_->callback_.reset();
256276
if (self_->ex_) {
257277
::stdexec::set_error(static_cast<Receiver&&>(self_->r_), std::move(self_->ex_));
258278
} else {
@@ -315,14 +335,16 @@ namespace asioexec {
315335
if (!base_::ex_) {
316336
base_::ex_ = std::current_exception();
317337
}
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
319341
}
320342
// In the case of an immediate completion *this may already be outside its
321343
// lifetime so we can't proceed into the branch
322344
if (frame) {
323345
base_::callback_.emplace(
324346
::stdexec::get_stop_token(::stdexec::get_env(base_::r_)),
325-
stop_callback(base_::signal_));
347+
typename base_::on_stop_request_{*this});
326348
}
327349
}
328350
};
@@ -490,7 +512,11 @@ namespace asioexec {
490512

491513
} // namespace detail::completion_token
492514

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+
};
494520

495521
inline const completion_token_t completion_token{};
496522

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
* Copyright (c) 2025 Robert Leahy. All rights reserved.
4+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
*
6+
* Licensed under the Apache License, Version 2.0 with LLVM Exceptions (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://llvm.org/LICENSE.txt
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#pragma once
20+
21+
#include <asioexec/asio_config.hpp>
22+
23+
namespace asioexec {
24+
25+
template <typename Executor, typename CompletionToken>
26+
struct executor_with_default : Executor {
27+
using default_completion_token_type = CompletionToken;
28+
executor_with_default(const Executor& ex) noexcept
29+
: Executor(ex) {
30+
}
31+
};
32+
33+
} // namespace asioexec

include/asioexec/use_sender.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <system_error>
2424
#include <type_traits>
2525
#include <utility>
26+
#include <asioexec/as_default_on.hpp>
2627
#include <asioexec/asio_config.hpp>
2728
#include <asioexec/completion_token.hpp>
2829
#include <stdexec/execution.hpp>
@@ -196,7 +197,11 @@ namespace asioexec {
196197

197198
} // namespace detail::use_sender
198199

199-
struct use_sender_t { };
200+
struct use_sender_t {
201+
static constexpr auto as_default_on = asioexec::as_default_on<use_sender_t>;
202+
template <typename IoObject>
203+
using as_default_on_t = asioexec::as_default_on_t<use_sender_t, IoObject>;
204+
};
200205

201206
inline const use_sender_t use_sender{};
202207

0 commit comments

Comments
 (0)