Skip to content

Commit 9a571e9

Browse files
Fix Go onClose deadlock by running state update in goroutine
The onClose callback acquires startStopMux, but Stop/ForceStop already hold that lock while waiting for readLoop to finish via wg.Wait(). Running the state update in a goroutine allows readLoop to complete, breaking the circular wait. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent c08e283 commit 9a571e9

1 file changed

Lines changed: 12 additions & 6 deletions

File tree

go/client.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,9 +1228,13 @@ func (c *Client) startCLIServer(ctx context.Context) error {
12281228
c.client = jsonrpc2.NewClient(stdin, stdout)
12291229
c.client.SetProcessDone(c.processDone, c.processErrorPtr)
12301230
c.client.SetOnClose(func() {
1231-
c.startStopMux.Lock()
1232-
defer c.startStopMux.Unlock()
1233-
c.state = StateDisconnected
1231+
// Run in a goroutine to avoid deadlocking with Stop/ForceStop,
1232+
// which hold startStopMux while waiting for readLoop to finish.
1233+
go func() {
1234+
c.startStopMux.Lock()
1235+
defer c.startStopMux.Unlock()
1236+
c.state = StateDisconnected
1237+
}()
12341238
})
12351239
c.RPC = rpc.NewServerRpc(c.client)
12361240
c.setupNotificationHandler()
@@ -1348,9 +1352,11 @@ func (c *Client) connectViaTcp(ctx context.Context) error {
13481352
c.client.SetProcessDone(c.processDone, c.processErrorPtr)
13491353
}
13501354
c.client.SetOnClose(func() {
1351-
c.startStopMux.Lock()
1352-
defer c.startStopMux.Unlock()
1353-
c.state = StateDisconnected
1355+
go func() {
1356+
c.startStopMux.Lock()
1357+
defer c.startStopMux.Unlock()
1358+
c.state = StateDisconnected
1359+
}()
13541360
})
13551361
c.RPC = rpc.NewServerRpc(c.client)
13561362
c.setupNotificationHandler()

0 commit comments

Comments
 (0)