From fa8b83816e886d6d1d5f9bd29704e7a8ec03a6c5 Mon Sep 17 00:00:00 2001 From: pku-xht Date: Fri, 21 Aug 2026 23:03:59 +0800 Subject: [PATCH] fix(subprocess): defer native handshake allocation --- .../subprocess-local/src/runner-launch.ts | 3 ++- .../subprocess-local/tests/spawn-runner.spec.ts | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/subprocess/subprocess-local/src/runner-launch.ts b/packages/subprocess/subprocess-local/src/runner-launch.ts index c29621ea07..60472c357e 100644 --- a/packages/subprocess/subprocess-local/src/runner-launch.ts +++ b/packages/subprocess/subprocess-local/src/runner-launch.ts @@ -16,7 +16,7 @@ import { import type { RunnerEvent, RunnerFiles, RunnerRequest } from './runner-protocol.ts' import { childEnv } from './spawn.ts' -const handshakeWait = new Int32Array(new SharedArrayBuffer(4)) +let handshakeWait: Int32Array | undefined const RUNNER_HANDSHAKE_TIMEOUT_MS = 10_000 const RUNNER_EVENT_POLL_MS = 100 const PACKAGED_RUNNER_ARG = '--dsh-internal-subprocess-runner' @@ -102,6 +102,7 @@ function waitForRunnerHandshake(child: ChildProcess, files: RunnerFiles): Runner if (terminal?.type === 'spawn-error' || terminal?.type === 'runner-error') return { pid: -1, events } if (child.pid === undefined) throw new Error('native subprocess runner failed to start') if (runnerExited(child, child.pid)) throw new Error('native subprocess runner exited before reporting target start') + handshakeWait ??= new Int32Array(new SharedArrayBuffer(4)) Atomics.wait(handshakeWait, 0, 0, 5) } throw new Error(`native subprocess runner did not report target start within ${String(RUNNER_HANDSHAKE_TIMEOUT_MS)}ms`) diff --git a/packages/subprocess/subprocess-local/tests/spawn-runner.spec.ts b/packages/subprocess/subprocess-local/tests/spawn-runner.spec.ts index 07bc9ca2b4..5841a44a54 100644 --- a/packages/subprocess/subprocess-local/tests/spawn-runner.spec.ts +++ b/packages/subprocess/subprocess-local/tests/spawn-runner.spec.ts @@ -63,6 +63,20 @@ describe('spawn runner transport', () => { expect(spawnRunnerInvocation()).toEqual(sourceInvocation) }) + it('does not require SharedArrayBuffer until a native handshake runs', async () => { + const descriptor = Object.getOwnPropertyDescriptor(globalThis, 'SharedArrayBuffer') + Object.defineProperty(globalThis, 'SharedArrayBuffer', { configurable: true, value: undefined }) + vi.resetModules() + try { + const isolated = await import('../src/runner-launch.ts') + expect(isolated.runnerStdio(spec())).toEqual(['ignore', 'pipe', 'pipe']) + } finally { + if (descriptor === undefined) Reflect.deleteProperty(globalThis, 'SharedArrayBuffer') + else Object.defineProperty(globalThis, 'SharedArrayBuffer', descriptor) + vi.resetModules() + } + }) + it('re-enters a packaged executable through its private runner dispatch', () => { const packagedProcess = process as NodeJS.Process & { pkg?: unknown } const original = Object.getOwnPropertyDescriptor(packagedProcess, 'pkg')