fix: fix connection panic caused by WaitGroup misuse on close#1484
Merged
Conversation
f070589 to
154a0ac
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
This PR addresses a connection-close race that can panic when SendRequest/SendRequestNoWait interact with WaitGroup while the connection is closing.
Changes:
- Synchronizes request state checks and
WaitGroup.Add(1)withc.mu.RLock(). - Replaces sentinel-based channel draining with a non-blocking drain after waiting for in-flight requests.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
c992cf9 to
9785098
Compare
The previous test did not exercise the actual Add/Wait race because it didn't call failLeftRequestsWhenClose() which contains the Wait() call. This updated test: - Directly calls registerIncomingRequest() to exercise WaitGroup.Add() - Concurrently calls failLeftRequestsWhenClose() to exercise Wait() - Runs 10 trials with 50 goroutines each to maximize race window - Verifies no panic occurs in Go 1.25+ with improper synchronization - Completes successfully with proper locking under mu.RLock() Also verify all three entry points (SendRequest, SendRequestNoWait, WriteData) properly reject calls after connection close via assertConnectionClosed test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
crossoverJie
approved these changes
May 15, 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.
Fixes #1483
Motivation
When closing a connection,
failLeftRequestsWhenClose()waits onincomingRequestsWGbefore draining queued requests.Previously, only
SendRequest/SendRequestNoWaitwere tracked by the WaitGroup.WriteDatacould still enqueue intowriteRequestsChafter close selection started, creating a race where buffered data requests were left undrained and never released.Additionally, in Go 1.25+, calling
Add(1)concurrently withWait()on a WaitGroup can panic withsync: WaitGroup is reused before previous Wait has returnedif close and in-flight request paths are not synchronized correctly.Modifications
Introduced a shared
registerIncomingRequest()helper used by all three entry points:SendRequestSendRequestNoWaitWriteDataThe
registerIncomingRequest()helper performs:c.mu.RLock()connectionClosed)incomingRequestsWG.Add(1)(only if still open)This ensures the check-and-add is atomic with respect to the close state transition:
Close()sets state underc.mu.Lock(), creating a clear happens-before relationshipconnectionClosed, no new request/write path can increment the WaitGroupThe non-blocking drain after
Wait()is now safe and sufficient:writeRequestsChbuffers and fails queued request callbacksTesting
Added regression test
TestConnectionSendRequestRaceWithClosethat:registerIncomingRequest()concurrentlyfailLeftRequestsWhenClose()to invoke Wait()assertConnectionClosed