fix: make timer_.on_expired survive (not crash) if the timer as been destroyed
This commit is contained in:
parent
bbcc800964
commit
367c497a38
1 changed files with 26 additions and 10 deletions
|
|
@ -761,24 +761,40 @@ struct timeout_impl {
|
|||
owner_.env_.trace(array("timeout", "set", m, us.count()));
|
||||
|
||||
timer_.expires_after(us);
|
||||
timer_.on_expired([this, start, m(move(m)),
|
||||
lifelock{owner_.lifetime_}](const error_code &error) {
|
||||
if (is_trace_enabled())
|
||||
owner_.env_.trace(array("timeout", "expired", m,
|
||||
// Capture lifelock and dtor_cancelled instead of `this`.
|
||||
// timeout_impl is owned by a unique_ptr and its destructor calls cancel();
|
||||
// the ASIO callback is queued *after* ~timeout_impl returns, so `this`
|
||||
// would be dangling. lifelock keeps the instance alive; dtor_cancelled
|
||||
// distinguishes destructor-triggered cancellation (where no notification
|
||||
// is desired) from an explicit user cancel() call.
|
||||
timer_.on_expired([lifelock{owner_.lifetime_},
|
||||
dtor_cancelled{dtor_cancelled_},
|
||||
start, m(move(m))](const error_code &error) {
|
||||
if (!lifelock) return;
|
||||
if (lifelock->env_.enabled(channel::timer))
|
||||
lifelock->env_.trace(array("timeout", "expired", m,
|
||||
clk::now().time_since_epoch().count() - start,
|
||||
error.value(), error.message()));
|
||||
if (!error)
|
||||
auto _ = owner_.send_raw(m);
|
||||
else
|
||||
auto _ = owner_.send_raw(
|
||||
auto _ = lifelock->send_raw(m);
|
||||
else if (!*dtor_cancelled)
|
||||
// User called cancel() explicitly — preserve the existing timeout_error
|
||||
// notification so callers can react (e.g. arm a different timer).
|
||||
auto _ = lifelock->send_raw(
|
||||
exit_message("timeout_error", error.value(), error.message()));
|
||||
});
|
||||
}
|
||||
~timeout_impl() { cancel(); }
|
||||
~timeout_impl() {
|
||||
*dtor_cancelled_ = true;
|
||||
cancel();
|
||||
}
|
||||
void cancel() { timer_.cancel(); }
|
||||
|
||||
instance &owner_;
|
||||
executor::timer timer_;
|
||||
// Shared with the timer callback so it can distinguish dtor cancellation
|
||||
// from an explicit user cancel() without capturing `this`.
|
||||
shared_ptr<bool> dtor_cancelled_{make_shared<bool>(false)};
|
||||
};
|
||||
void timeout::cancel() {
|
||||
if (ref)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue