fix(stdio): restore 2s socket-release wait to prevent Windows port leak (#1173)#1175
fix(stdio): restore 2s socket-release wait to prevent Windows port leak (#1173)#1175Scriptwonder wants to merge 1 commit into
Conversation
…ak (CoplayDev#1173) PR CoplayDev#688 set toWait.Wait(2000) on StdioBridgeHost.Stop() specifically so the TcpListener could fully release the port on Windows before the post-reload Start() rebinds. PR CoplayDev#787 later reduced this to 500ms without flagging the Windows dependency, and 9.7.1 ships the regressed value. On Windows, 500ms is too short for the OS to free the socket. The post-reload Start() then hits AddressAlreadyInUse, silently falls back to a new port via PortManager.DiscoverNewPort(), and leaves the Python server pinned to the original port. Scene-graph tools start returning busy/timeout indefinitely because OS round-robin sometimes routes them to the orphaned listener. - StdioBridgeHost.Stop: 500 -> 2000 (matches PR CoplayDev#688 intent) - StdioBridgeReloadHandler.OnBeforeAssemblyReload: 500 -> 2000 (matches the StopAsync ceiling; was already too short for the same reason) Reporter (CoplayDev#1173) bisected to Reload Domain ON; Reload Domain OFF is clean across 4+ play cycles, consistent with the socket-release race.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughTwo files increase their shutdown wait timeouts from 500ms to 2000ms: ChangesStdio Transport Shutdown Timeout Extension
Possibly related PRs
Poem
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Restores the original 2-second shutdown wait in the Unity Editor stdio transport teardown path to prevent Windows from rebinding the same TCP port before the OS fully releases the socket during domain reloads (fixing the port-leak/orphan-listener regression described in #1173).
Changes:
- Restore
Wait(2000)inStdioBridgeHost.Stop()to allow Windows time to fully release the disposed listener socket. - Restore the matching 2-second wait in
StdioBridgeReloadHandler.OnBeforeAssemblyReload()to keep teardown timing consistent during domain reload.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| MCPForUnity/Editor/Services/Transport/Transports/StdioBridgeHost.cs | Increases the listener-task wait ceiling back to 2s to prevent Windows port reuse races after Stop()/Dispose. |
| MCPForUnity/Editor/Services/StdioBridgeReloadHandler.cs | Aligns the pre-reload stop sequence timeout with the host stop timing during domain reloads. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Windows needs the full 2s for the socket to release after Dispose; with 500ms | ||
| // the next Start() can hit AddressAlreadyInUse and silently fall back to a new | ||
| // port, orphaning the listener. See #1173 / #688. | ||
| try { toWait.Wait(2000); } catch { } | ||
| } |
| var stopTask = MCPServiceLocator.TransportManager.StopAsync(TransportMode.Stdio); | ||
| try { stopTask.Wait(500); } catch { } | ||
| // Match StdioBridgeHost.Stop's 2s socket-release wait; 500ms is too short on | ||
| // Windows and lets the next reload bind the same port before the OS frees it. | ||
| try { stopTask.Wait(2000); } catch { } |
|
Closing — the 2000ms restore re-revives the backgrounded-Unity stall that PR #787 explicitly fixed by reducing this wait, and doesn't address the silent port fallback that's the real masking layer in #1173. A follow-up will rework the listener loop to exit promptly on cancellation (so Wait returns fast regardless of OS) and add a same-port retry before falling back to DiscoverNewPort, closing #1173 without trading off #787. |
Summary
toWait.Wait(2000)inStdioBridgeHost.Stop()(was reduced 2000 → 500 in PR Fix stdio bridge stalls when Unity is backgrounded during domain reload #787, regressed since 9.7.1)stopTask.Wait(2000)inStdioBridgeReloadHandler.OnBeforeAssemblyReloadRoot cause (from #1173)
PR #688 set the 2000ms wait specifically so the
TcpListenersocket could fully release on Windows before the post-reloadStart()rebinds. PR #787 later reduced this to 500ms without flagging the Windows dependency.On Windows, 500ms is too short for the OS to free the socket. After the 2nd play-mode entry within a single Editor session (default Enter Play Mode Options, Reload Domain enabled), the post-reload
Start()hitsAddressAlreadyInUse, the catch block silently falls back to a new port viaPortManager.DiscoverNewPort(), and the Python server keeps talking to the orphan on the original port. Scene-graph tools (find_gameobjects,manage_scene get_hierarchy,execute_code) start returningbusy/timeout indefinitely because OS round-robin sometimes routes calls to the orphaned listener.Reporter (#1173) bisected to Reload Domain as the variable —
Reload Domain OFFis clean across 4+ play cycles, consistent with the socket-release race.Verified via
git log -Lthat:StdioBridgeHost.cs:400Wait(500)came from commit79e6c917(PR Fix stdio bridge stalls when Unity is backgrounded during domain reload #787) which silently reduced PR fix: harden localhost resolution and reload transport resilience on Windows #688'sWait(2000)StdioBridgeReloadHandler.cs:73was500mssince the file's initial 17cd543 introduction — never had the Windows-safe ceilingScope
Two-line timeout restore; no behavioural change on macOS/Linux (where socket release is fast). The 1.5s extra wait only runs on
Stop(), which is rare (domain reload / quit), so editor responsiveness is unaffected.Test plan
6400(no silent shift to6402)find_gameobjects/manage_scene get_hierarchykeep responding after cycle 2StdioBridgeReconnectTestscontinue to passCloses #1173
Summary by CodeRabbit