When co_waiter is destroyed, the cancellation slot may still hold a
reference to the op_cancellation callback which captures 'this'. If
the cancellation signal is emitted after co_waiter is destroyed (e.g.,
during co_throttle shutdown), it results in a stack-use-after-scope
error.
Fix by adding a destructor that retrieves the cancellation slot from
the handler (if still active) and clears it before destruction. This
ensures the cancellation callback is removed before the co_waiter
object goes out of scope, preventing use-after-scope errors.
The cancellation slot cannot be stored as a member variable because
it becomes invalid after the handler is moved out in complete(). Instead,
we retrieve it on demand from the handler in the destructor, which is
the only place we need it.
Signed-off-by: Kefu Chai <k.chai@proxmox.com>
public:
co_waiter() = default;
+ ~co_waiter() {
+ // Clear the cancellation slot to prevent use-after-scope
+ if (handler) {
+ auto slot = boost::asio::get_associated_cancellation_slot(*handler);
+ if (slot.is_connected()) {
+ slot.clear();
+ }
+ }
+ }
+
// copy and move are disabled because the cancellation handler captures 'this'
co_waiter(const co_waiter&) = delete;
co_waiter& operator=(const co_waiter&) = delete;
public:
co_waiter() = default;
+ ~co_waiter() {
+ // Clear the cancellation slot to prevent use-after-scope
+ if (handler) {
+ auto slot = boost::asio::get_associated_cancellation_slot(*handler);
+ if (slot.is_connected()) {
+ slot.clear();
+ }
+ }
+ }
+
// copy and move are disabled because the cancellation handler captures 'this'
co_waiter(const co_waiter&) = delete;
co_waiter& operator=(const co_waiter&) = delete;