Repository navigation
Fix deadlock between thread-safe inproc senders - #1169
Merged
Merged
Conversation
MailboxSafe.Send took the owning socket's lock to write a command. A sender flushing a pipe already holds its own socket's lock, so when the flush activates a passive peer reader it takes the peer's lock while holding its own. Two thread-safe sockets sending to each other at the same time (for example a ServerSocket and a ClientSocket over inproc) each hold their own lock and wait for the other's, and deadlock. Senders now serialise on a private lock in the mailbox and never take the socket's lock. A receiver that must wait re-reads the pipe under the private lock, releases the socket lock at every depth, waits on the private lock, then restores the socket lock after leaving it, so the socket lock is never acquired while the private lock is held. The restore completes even if interrupted and re-arms the interruption. Close passes through both locks. Add a regression that sends in both directions between an inproc ServerSocket and ClientSocket at once.
drewnoakes
approved these changes
Oct 7, 2026
Member
|
Thank you! |
Member
This was referenced Oct 9, 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.
Two thread-safe sockets that send to each other at the same time can deadlock.
MailboxSafe.Sendtakes the owning socket's lock to write a command. But a sender flushing a pipe already holds its own socket's lock, and when the flush wakes a passive reader on the peer it sendsActivateReadinto the peer'sMailboxSafe- which takes the peer's socket lock. So with aServerSocketand aClientSocketconnected over inproc and both sending at once, each thread holds its own socket's lock and waits for the other's.The fix gives
MailboxSafea private lock for senders, the same wayIOThreadMailboxalready works: senders never touch the socket's lock, so the cycle can't form.The receive side needs a little care, because a receiver that has to wait used to wait on the socket's lock itself (
Monitor.Wait(m_sync)). Now it:BindRandomPortcallsBind), waits on the private lock, and then takes the socket lock back after leaving the private lock - so the socket lock is never acquired while the private lock is held;Unlockstill matches.Closepasses through both locks so no sender is still mid-send.The new test
InprocConcurrentSendsInBothDirectionsDoNotDeadlockhas a Server and a Client send 20,000 messages to each other at the same time while draining. On current master it hangs every time I run it (3 out of 3); with the fix it passes reliably (200 out of 200). If it ever regresses, the test fails itsJoinand may then hang during disposal, so the CI hang timeout is what will catch it.