fix: process leaks in extensionHostConnection#319223
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Updates extension host socket disposal to perform a graceful shutdown rather than an immediate destroy, aiming to reduce abrupt termination during cleanup.
Changes:
- Replace
extHostSocket.destroy()with a conditionalextHostSocket.end()on dispose. - Add guards to avoid ending an already-destroyed or already-ended socket.
| if (!extHostSocket.destroyed && !extHostSocket.writableEnded) { | ||
| extHostSocket.end(); | ||
| } |
There was a problem hiding this comment.
This can be a helper, something like "gracefulSocketEnd" which can potentially be reused
There was a problem hiding this comment.
Perhaps? I'm not actually sure what's the right way to gracefully close a NodeJS socket. The code with socket.end() just seemed to work.
Can socket.end leave the socket half-open forever if the other side never closes the socket? I don't know...
There seem to be other places where, on socket.end event, after 30 seconds, it destroys the socket like
vscode/src/vs/base/parts/ipc/node/ipc.net.ts
Line 143 in 1f03fed
I just looked at the NodeJS docs, and there is also a method socket.destroySoon, which looks like even better:
Destroys the socket after all data is written. If the 'finish' event was already emitted the socket is destroyed immediately. If the socket is still writable it implicitly calls socket.end().
If I would make this PR again I'd probably choose socket.destroySoon
There was a problem hiding this comment.
Well, perhaps a follow up PR? I think a timeout race would still be useful in this case.
Head branch was pushed to by a user without write access
|
(made another small change to try to fix windows ci) |
|
thank you! |
|
|
||
| const SLOWED_DOWN_CONNECTION_DELAY = 800; | ||
|
|
||
| function isExpectedSocketCloseError(error: NodeJS.ErrnoException): boolean { |
There was a problem hiding this comment.
nit: isBenignSocketTeardownError name would probably be better here
|
@SimonSiefke Thanks a lot! Please take a look at some feedback |
Fixes #211462.
Details
When the connection closes, the extension host sends a disconnect message, and then closes the socket. The remote agent host, receives the disconnect message, starts its graceful cleanup but then the socket close event comes which immediately triggers
extHostSocket.destroy()before the graceful cleanup is finishedChange
The change tries to ensure the
extHostSocketgets shutdown gracefully, so that the cleanup code and still run and finish.Test out locally (Linux)
product.overrides.json{ "nameShort": "Code", "nameLong": "Visual Studio Code", "applicationName": "code", "extensionsGallery": { "nlsBaseUrl": "https://www.vscode-unpkg.net/_lp/", "serviceUrl": "https://marketplace.visualstudio.com/_apis/public/gallery", "itemUrl": "https://marketplace.visualstudio.com/items", "publisherUrl": "https://marketplace.visualstudio.com/publishers", "resourceUrlTemplate": "https://{publisher}.vscode-unpkg.net/{publisher}/{name}/{version}/{path}", "extensionUrlTemplate": "https://www.vscode-unpkg.net/_gallery/{publisher}/{name}/latest", "controlUrl": "https://main.vscode-cdn.net/extensions/marketplace.json", "mcpUrl": "https://main.vscode-cdn.net/mcp/servers.json", "accessSKUs": [ "copilot_enterprise_seat", "copilot_enterprise_seat_quota", "copilot_enterprise_seat_multi_quota", "copilot_enterprise_seat_assignment", "copilot_enterprise_seat_assignment_quota", "copilot_enterprise_seat_assignment_multi_quota", "copilot_enterprise_trial_seat", "copilot_enterprise_trial_seat_quota", "copilot_for_business_seat", "copilot_for_business_seat_quota", "copilot_for_business_seat_multi_quota", "copilot_for_business_seat_assignment", "copilot_for_business_seat_assignment_quota", "copilot_for_business_seat_assignment_multi_quota", "copilot_for_business_trial_seat", "copilot_for_business_trial_seat_quota" ] } }./scripts/code.shIf there are leaks, the number of processes increases each time as described in the issue
Kooha-2026-06-01-12-05-37.mp4
Before
When opening a new window and connecting it to SSH and then closing it 3 times, the number of processes seems to increase each time:
{ "processCount": { "after": 22, "before": 10 }, "isLeak": true }After
When opening a new window and connecting it to SSH and then closing it 3 times, the number of processes seems to stay constant:
{ "processCount": { "after": 13, "before": 13 }, "isLeak": false }