Fix liquidity event cancellation in select loop#21
Merged
amackillop merged 1 commit intolsp-0.7.0from Apr 21, 2026
Merged
Conversation
handle_next_event() both consumed events from the queue via next_event_async() and processed them inline, including spawn_blocking(...).await calls for wallet checks. Because this ran as a polled future inside tokio::select!, any tick timer firing while the handler was suspended at an .await point would cancel the future. The event had already been dequeued, so it was silently lost. Split event receipt from processing: next_event_async() is polled as the select! future (cancellation-safe since it only dequeues on Poll::Ready), and handle_event() runs in the handler block which select! guarantees runs to completion before the next iteration. This was the root cause of JIT channel opens and splices timing out in production. The HTLC would be intercepted, the OpenChannel event consumed from the queue, but a timer tick would cancel processing before create_channel was called. The peer would disconnect after 40s and the HTLC would expire.
martinsaposnic
approved these changes
Apr 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
handle_next_event() both consumed events from the queue via next_event_async() and processed them inline, including spawn_blocking(...).await calls for wallet checks. Because this ran as a polled future inside tokio::select!, any tick timer firing while the handler was suspended at an .await point would cancel the future. The event had already been dequeued, so it was silently lost.
Split event receipt from processing: next_event_async() is polled as the select! future (cancellation-safe since it only dequeues on Poll::Ready), and handle_event() runs in the handler block which select! guarantees runs to completion before the next iteration.
This was the root cause of JIT channel opens and splices timing out in production. The HTLC would be intercepted, the OpenChannel event consumed from the queue, but a timer tick would cancel processing before create_channel was called. The peer would disconnect after 40s and the HTLC would expire.