Skip to content

Fix deadlock when handling open channel on contended handler - #734

Merged
Eugeny merged 7 commits into
Eugeny:mainfrom
EpicEric:fix-channel-open-starvation
Aug 11, 2026
Merged

Fix deadlock when handling open channel on contended handler#734
Eugeny merged 7 commits into
Eugeny:mainfrom
EpicEric:fix-channel-open-starvation

Conversation

@EpicEric

@EpicEric EpicEric commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Before #723, synchronous channel acceptance/rejection would happen regardless of message dispatch. Now, the channel reply uses the same bounded mpsc channel that the handler consumes from.

However, under contention (for example, with multiple simultaneous channels opening and writing over the same handler), the awaits in ChannelOpenHandleInner::accept and ChannelOpenHandleInner::reject may hang since the consumer is the handler itself, thus leading to a self-deadlock.

This PR changes channel confirmation to use its own unbounded channel on both server and client implementations, avoiding any awaits (since send is sync), and thus, any deadlocks under load.

Because of the change to ChannelOpenHandleInner::accept and ChannelOpenHandleInner::reject into sync methods, this is backwards-incompatible.

@EpicEric EpicEric changed the title Fix deadlock when handling open channel on contented handler Fix deadlock when handling open channel on contended handler Jul 6, 2026
EpicEric added 2 commits July 6, 2026 09:16
Before Eugeny#723, synchronous channel acceptance/rejection would happen regardless of
message dispatch. Now, the channel reply uses the same bounded mpsc channel
that the handler consumes from.

However, under contention (for example, with multiple simultaneous channels
opening and writing over the same handler), the awaits in
`ChannelOpenHandleInner::accept` and `ChannelOpenHandleInner::reject` may
hang while the consumer is the handler itself, thus leading to a self-deadlock.

This PR changes channel confirmation to use its own unbounded channel on
both server and client implementations, avoiding any awaits (since send
is sync), and thus, any deadlocks under load.

Because of the change to `ChannelOpenHandleInner::accept` and
`ChannelOpenHandleInner::reject` into sync methods, this is
backwards-incompatible.
@EpicEric
EpicEric force-pushed the fix-channel-open-starvation branch from 53070fd to 7298d48 Compare July 6, 2026 12:16
@EpicEric

EpicEric commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

I've added a test_contention regression test that sometimes locks up on russh 0.62.1 due to the race condition, but runs correctly with these changes.

@EpicEric

EpicEric commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Despite the accept/reject methods being fully sync, I could change them back to async as to not break backwards compatibility.

@Eugeny

Eugeny commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Thanks for the PR! Please change the accept/reject fns back to async - it's barely an issue for client code, and I'd like to avoid a breaking change for a bugfix

@Eugeny
Eugeny merged commit c66837e into Eugeny:main Aug 11, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants