fix(pool): data race on Conn.onClose during init vs Close - #3966
fix(pool): data race on Conn.onClose during init vs Close#3966saddamr3e wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8e39e98346
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| cn.onClose.Store(nil) | ||
| return | ||
| } | ||
| cn.onClose.Store(&fn) |
There was a problem hiding this comment.
Run callbacks installed after close
When Close wins the race and completes Swap(nil) before an in-flight initConn reaches this store, SetOnClose installs the streaming-provider unsubscribe callback on an already-closed connection, so no later Close will invoke it. This leaves the provider subscribed to a dead connection—the exact shutdown/reinitialization interleaving this change targets—while the new regression test checks only for a data-race report and never verifies callback delivery. Preserve a terminal closed sentinel in the atomic slot or otherwise synchronize installation with the CLOSED transition so a late callback runs immediately.
AGENTS.md reference: AGENTS.md:L172-L180
Useful? React with 👍 / 👎.
go test -race, internal/pool:
initConn installs the close callback (the StreamingCredentialsProvider unsubscribe) via SetOnClose while the connection is INITIALIZING inside SetNetConnAndInitConn. Conn.Close transitions to CLOSED from any state, then reads and nils cn.onClose with no synchronization, so a pool shutdown or connection removal that closes a conn whose (re)init is still in flight races the setter.
Store the hook in atomic.Pointer[func() error] so the setter and Close no longer race, with no per-connection mutex so Conn stays slim; Swap(nil) in Close also runs the hook at most once under a concurrent double-close. Regression test drives SetNetConnAndInitConn against Close and is clean under -race.
Note
Low Risk
Localized synchronization fix in pool connection teardown; behavior unchanged aside from eliminating the race and ensuring at-most-once callback invocation under concurrent close.
Overview
Fixes a data race on
Conn.onClosewhen connection init (SetNetConnAndInitConn→SetOnClose, e.g. streaming-credentials unsubscribe) runs concurrently withCloseduring pool shutdown or removal.onCloseis now anatomic.Pointer[func() error]:SetOnClosestores the hook (or clears withnil), andCloseusesSwap(nil)to read, clear, and invoke the callback once without a per-connection mutex. Comments document why atomics were chosen over a mutex onConn.Adds
TestConnOnCloseRaceWithInitConn, which hammers concurrent init and close under the race detector.Reviewed by Cursor Bugbot for commit 8e39e98. Bugbot is set up for automated code reviews on this repo. Configure here.