fix(e2b): close adapter lifecycle review gaps
This commit is contained in:
@@ -28,7 +28,8 @@ export class E2BSubprocessService extends SubprocessService {
|
||||
|
||||
private readonly live = new Set<E2BSubprocessHandle>()
|
||||
private readonly terminals = new Set<SubprocessTerminalHandle>()
|
||||
private readonly terminalSetups = new Set<Promise<void>>()
|
||||
private readonly terminalSetups = new Map<Promise<void>, AbortController>()
|
||||
private readonly failedTerminalSetupCleanups = new Set<() => Promise<void>>()
|
||||
private disposing = false
|
||||
|
||||
/** @inheritdoc */
|
||||
@@ -44,9 +45,13 @@ export class E2BSubprocessService extends SubprocessService {
|
||||
this.runtimeRoot = ctx.e2b.runtimeRoot
|
||||
ctx.effect(() => async () => {
|
||||
this.disposing = true
|
||||
await Promise.all([...this.terminalSetups])
|
||||
for (const controller of this.terminalSetups.values()) {
|
||||
controller.abort(new Error('subprocess-e2b: service disposed during terminal setup'))
|
||||
}
|
||||
await Promise.all([...this.terminalSetups.keys()])
|
||||
const handles = [...this.live]
|
||||
const terminals = [...this.terminals]
|
||||
const failedTerminalSetupCleanups = [...this.failedTerminalSetupCleanups]
|
||||
const pending: Promise<unknown>[] = []
|
||||
for (const handle of handles) {
|
||||
handle.terminate()
|
||||
@@ -59,6 +64,9 @@ export class E2BSubprocessService extends SubprocessService {
|
||||
terminal.terminate()
|
||||
pending.push(terminal.waitForExit().then(() => { this.terminals.delete(terminal) }))
|
||||
}
|
||||
for (const cleanup of failedTerminalSetupCleanups) {
|
||||
pending.push(cleanup().then(() => { this.failedTerminalSetupCleanups.delete(cleanup) }))
|
||||
}
|
||||
await Promise.all(pending)
|
||||
}, 'e2b subprocess teardown')
|
||||
}
|
||||
@@ -133,9 +141,18 @@ export class E2BSubprocessService extends SubprocessService {
|
||||
spec.signal?.throwIfAborted()
|
||||
const stateDir = posix.join(this.runtimeRoot, 'terminals', randomUUID())
|
||||
const setup = Promise.withResolvers<void>()
|
||||
this.terminalSetups.add(setup.promise)
|
||||
const setupController = new AbortController()
|
||||
const setupSignal = spec.signal === undefined
|
||||
? setupController.signal
|
||||
: AbortSignal.any([spec.signal, setupController.signal])
|
||||
this.terminalSetups.set(setup.promise, setupController)
|
||||
try {
|
||||
const terminal = await spawnE2BTerminal(this.ctx.e2b, spec, stateDir)
|
||||
const terminal = await spawnE2BTerminal(
|
||||
this.ctx.e2b,
|
||||
{ ...spec, signal: setupSignal },
|
||||
stateDir,
|
||||
(cleanup) => { this.failedTerminalSetupCleanups.add(cleanup) },
|
||||
)
|
||||
this.terminals.add(terminal)
|
||||
if (this.isDisposing()) {
|
||||
terminal.terminate()
|
||||
|
||||
Reference in New Issue
Block a user