fix(runtime): close lifecycle teardown races
This commit is contained in:
@@ -101,15 +101,30 @@ export class LocalPtyBackend implements PtyBackend {
|
||||
ensureSandboxModeFence(this.ctx, spec.owner)
|
||||
const argv = spawnArgv(this.ctx, this.config, spec)
|
||||
if (argv[0] === undefined) throw new Error('pty-local: sandbox returned empty argv')
|
||||
const terminal = await this.spawnTerminal({
|
||||
argv,
|
||||
cwd: spec.cwd ?? this.ctx.sandboxPolicy.workspaceRoot,
|
||||
env: childEnvironment(spec),
|
||||
rows: this.config.rows,
|
||||
cols: this.config.cols,
|
||||
graceMs: this.config.disposeGraceMs,
|
||||
signal: spec.signal,
|
||||
})
|
||||
let terminalSignal: AbortSignal | undefined
|
||||
let detachSetupSignal: (() => void) | undefined
|
||||
if (spec.signal !== undefined) {
|
||||
const source = spec.signal
|
||||
const controller = new AbortController()
|
||||
const onAbort = (): void => { controller.abort(source.reason) }
|
||||
source.addEventListener('abort', onAbort, { once: true })
|
||||
detachSetupSignal = () => { source.removeEventListener('abort', onAbort) }
|
||||
terminalSignal = controller.signal
|
||||
}
|
||||
let terminal: SubprocessTerminalHandle
|
||||
try {
|
||||
terminal = await this.spawnTerminal({
|
||||
argv,
|
||||
cwd: spec.cwd ?? this.ctx.sandboxPolicy.workspaceRoot,
|
||||
env: childEnvironment(spec),
|
||||
rows: this.config.rows,
|
||||
cols: this.config.cols,
|
||||
graceMs: this.config.disposeGraceMs,
|
||||
signal: terminalSignal,
|
||||
})
|
||||
} finally {
|
||||
detachSetupSignal?.()
|
||||
}
|
||||
const session = this.createSession(terminal, this.config)
|
||||
try {
|
||||
await session.initialize(spec.signal)
|
||||
|
||||
@@ -473,17 +473,23 @@ export class LocalPtySession implements PtyBackendSession {
|
||||
this.active = undefined
|
||||
}
|
||||
|
||||
private failActive(error: unknown): void {
|
||||
private failActive(error: unknown, retainOwnership = false): void {
|
||||
const operation = this.active
|
||||
if (operation === undefined) return
|
||||
this.clearActive()
|
||||
if (retainOwnership) {
|
||||
this.stopPolling()
|
||||
this.activeAbort?.()
|
||||
this.activeAbort = undefined
|
||||
} else {
|
||||
this.clearActive()
|
||||
}
|
||||
operation.fail(error)
|
||||
}
|
||||
|
||||
private interrupt(operation: LocalSendOperation): void {
|
||||
if (this.active !== operation) return
|
||||
void this.terminal.signalForeground('SIGINT').catch((error: unknown) => {
|
||||
if (this.active === operation) this.failActive(error)
|
||||
if (this.active === operation) this.failActive(error, this.writing === operation)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user