fix: Shared compiler shutdown no longer leaves worker processes running

Merged
HA
3mo ago
+285-11

Summary

  • Shared compiler shutdown now reaches forced termination even when the graceful quit channel is broken.
  • Operating-system termination failures are logged while final process disposal still completes.

User Impact

  • Previously, an input-stream failure during Editor reload or quit could skip the kill fallback and leave the shared compiler worker running after the package forgot it.
  • Shutdown now treats graceful quit and forced termination as separate best-effort phases, reducing orphaned worker processes without changing normal compilation behavior.

Changes

  • Split shared worker shutdown into independently guarded graceful and forced termination phases.
  • Keep HasExited checks inside each phase's exception boundary and catch only documented, reachable process-operation failures.
  • Treat exit-code lookup failures as an unknown state that still requires forced termination, while skipping kill when no process is associated.
  • Preserve exactly-once process disposal with a shared finally block.
  • Add process-free state-transition tests for broken and disposed quit channels, kill failures, successful graceful exit, and already-exited processes.
  • Keep the shared compiler wire format and protocol version unchanged.

Verification

  • Before the fix, the five focused tests produced the expected TDD result: three failures for skipped kill fallback and escaping Win32Exception, with the two normal-path characterization tests passing.
  • dist/darwin-arm64/uloop compile (0 errors, 0 warnings)
  • Focused shutdown workflow tests (7 passed)
  • SharedRoslynCompilerWorkerHostTests and StaticFacadeStateGuardTests (43 passed)
  • git diff --check