Release OutputSweeper::pending_sweep flag on future drop#4598
Conversation
`regenerate_and_broadcast_spend_if_necessary` used `pending_sweep: AtomicBool` as a single-runner gate but only cleared the flag with an unconditional `store(false)` *after* the inner future resolved. If the caller's future was dropped while the inner await was `Pending` — which `tokio::time::timeout`, `futures::select!`, manual `JoinHandle::abort`, etc. all do — the reset never ran, leaving the flag stuck `true` and every subsequent call to the function short-circuiting with `Ok(())`. Because `OutputSweeper` is what claims `SpendableOutputDescriptor`s back to the user's wallet after channel closure (including HTLC outputs with time-bounded recovery deadlines), a stuck flag turns into fund-loss exposure: time-sensitive HTLC sweeps simply stop happening, while every other code path keeps queueing new outputs to sweep, until the process is restarted. Replace the trailing `store(false)` with an RAII `PendingSweepGuard` whose `Drop` impl always releases the flag — covering normal return, error, and cancellation alike. Co-Authored-By: HAL 9000
|
👋 Thanks for assigning @joostjager as a reviewer! |
|
The implementation is correct and clean. Key verification points:
No issues found. |
TheBlueMatt
left a comment
There was a problem hiding this comment.
Honestly not sure its worth committing the test for this, but whatever.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4598 +/- ##
==========================================
- Coverage 86.84% 86.25% -0.59%
==========================================
Files 161 159 -2
Lines 109260 109351 +91
Branches 109260 109351 +91
==========================================
- Hits 94882 94319 -563
- Misses 11797 12416 +619
- Partials 2581 2616 +35
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Yeah, true, I was also on the fence whether to include it or not. |
regenerate_and_broadcast_spend_if_necessaryusedpending_sweep: AtomicBoolas a single-runner gate but only cleared the flag with an unconditionalstore(false)after the inner future resolved. If the caller's future was dropped while the inner await wasPending— whichtokio::time::timeout,futures::select!, manualJoinHandle::abort, etc. all do — the reset never ran, leaving the flag stucktrueand every subsequent call to the function short-circuiting withOk(()).Because
OutputSweeperis what claimsSpendableOutputDescriptors back to the user's wallet after channel closure (including HTLC outputs with time-bounded recovery deadlines), a stuck flag turns into fund-loss exposure: time-sensitive HTLC sweeps simply stop happening, while every other code path keeps queueing new outputs to sweep, until the process is restarted.Replace the trailing
store(false)with an RAIIPendingSweepGuardwhoseDropimpl always releases the flag — covering normal return, error, and cancellation alike.Co-Authored-By: HAL 9000