refactor(runtime): remove duplicate terminal ownership state
This commit is contained in:
@@ -167,7 +167,7 @@ export class LocalPtySession implements PtyBackendSession {
|
|||||||
private activeDeadlineTimer: NodeJS.Timeout | undefined
|
private activeDeadlineTimer: NodeJS.Timeout | undefined
|
||||||
private activeAbort: (() => void) | undefined
|
private activeAbort: (() => void) | undefined
|
||||||
private interrupting: LocalSendOperation | undefined
|
private interrupting: LocalSendOperation | undefined
|
||||||
private activeWrite: { operation: LocalSendOperation; settled: Promise<boolean> } | undefined
|
private activeWrite: Promise<boolean> | undefined
|
||||||
private pollingReady: LocalSendOperation | undefined
|
private pollingReady: LocalSendOperation | undefined
|
||||||
private polling = false
|
private polling = false
|
||||||
private promptSeen = false
|
private promptSeen = false
|
||||||
@@ -238,7 +238,7 @@ export class LocalPtySession implements PtyBackendSession {
|
|||||||
}
|
}
|
||||||
this.activeDeadlineTimer = setTimeout(() => {
|
this.activeDeadlineTimer = setTimeout(() => {
|
||||||
if (this.active === operation) {
|
if (this.active === operation) {
|
||||||
this.settleActive('timeout', this.activeWrite?.operation === operation || this.interrupting === operation)
|
this.settleActive('timeout', this.activeWrite !== undefined || this.interrupting === operation)
|
||||||
}
|
}
|
||||||
}, this.config.timeoutMs)
|
}, this.config.timeoutMs)
|
||||||
void this.beginSend(operation, request)
|
void this.beginSend(operation, request)
|
||||||
@@ -254,11 +254,7 @@ export class LocalPtySession implements PtyBackendSession {
|
|||||||
if (input.length > 0 && !operation.cancelRequested) {
|
if (input.length > 0 && !operation.cancelRequested) {
|
||||||
this.resetReadinessEvidence()
|
this.resetReadinessEvidence()
|
||||||
const write = this.terminal.write(input)
|
const write = this.terminal.write(input)
|
||||||
const activeWrite = {
|
this.activeWrite = write.then(() => true, () => false)
|
||||||
operation,
|
|
||||||
settled: write.then(() => true, () => false),
|
|
||||||
}
|
|
||||||
this.activeWrite = activeWrite
|
|
||||||
try {
|
try {
|
||||||
await write
|
await write
|
||||||
} finally {
|
} finally {
|
||||||
@@ -272,7 +268,7 @@ export class LocalPtySession implements PtyBackendSession {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Closing can race the awaited provider write even though static analysis sees only local assignments.
|
// Closing can race the awaited provider write even though static analysis sees only local assignments.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
// oxlint-disable-next-line typescript/no-unnecessary-condition -- awaited provider writes can close the session.
|
||||||
if (this.active === operation && !this.closing) {
|
if (this.active === operation && !this.closing) {
|
||||||
this.pollingReady = operation
|
this.pollingReady = operation
|
||||||
this.schedulePoll(operation)
|
this.schedulePoll(operation)
|
||||||
@@ -448,7 +444,7 @@ export class LocalPtySession implements PtyBackendSession {
|
|||||||
this.polling = false
|
this.polling = false
|
||||||
const active = this.active
|
const active = this.active
|
||||||
// Awaited provider inspection can clear or replace the active send despite static analysis.
|
// Awaited provider inspection can clear or replace the active send despite static analysis.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
// oxlint-disable-next-line typescript/no-unnecessary-condition -- awaited inspection can replace the active send.
|
||||||
if (active !== undefined && this.pollingReady === active) this.schedulePoll(active)
|
if (active !== undefined && this.pollingReady === active) this.schedulePoll(active)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -506,7 +502,7 @@ export class LocalPtySession implements PtyBackendSession {
|
|||||||
private async interruptOnce(operation: LocalSendOperation): Promise<void> {
|
private async interruptOnce(operation: LocalSendOperation): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const activeWrite = this.activeWrite
|
const activeWrite = this.activeWrite
|
||||||
if (activeWrite?.operation === operation && !await activeWrite.settled) return
|
if (activeWrite !== undefined && !await activeWrite) return
|
||||||
await this.terminal.signalForeground('SIGINT')
|
await this.terminal.signalForeground('SIGINT')
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (this.active === operation && !this.closing) this.onTransportFailure(error)
|
if (this.active === operation && !this.closing) this.onTransportFailure(error)
|
||||||
|
|||||||
@@ -58,19 +58,18 @@ export class LocalTerminalHandle implements SubprocessTerminalHandle {
|
|||||||
exitCode: exitSignal === undefined || exitSignal === 0 ? exitCode : null,
|
exitCode: exitSignal === undefined || exitSignal === 0 ? exitCode : null,
|
||||||
signal: signalName(exitSignal),
|
signal: signalName(exitSignal),
|
||||||
})
|
})
|
||||||
void this.terminate().catch(() => {})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// node-pty writes synchronously; the seam returns a promise for remote transports.
|
// node-pty writes synchronously; the seam returns a promise for remote transports.
|
||||||
// eslint-disable-next-line @typescript-eslint/require-await
|
// oxlint-disable-next-line typescript/require-await -- Preserve promise rejection semantics at the async provider seam.
|
||||||
async write(data: string): Promise<void> {
|
async write(data: string): Promise<void> {
|
||||||
if (this.exited) throw new Error('terminal process has exited')
|
if (this.exited) throw new Error('terminal process has exited')
|
||||||
this.terminal.write(data)
|
this.terminal.write(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local inspection is synchronous; the seam returns a promise for remote transports.
|
// Local inspection is synchronous; the seam returns a promise for remote transports.
|
||||||
// eslint-disable-next-line @typescript-eslint/require-await
|
// oxlint-disable-next-line typescript/require-await -- Preserve promise rejection semantics at the async provider seam.
|
||||||
async inspectForeground(): Promise<SubprocessTerminalForeground | undefined> {
|
async inspectForeground(): Promise<SubprocessTerminalForeground | undefined> {
|
||||||
this.descendants()
|
this.descendants()
|
||||||
const processGroupId = this.inspector.foregroundPgid(this.pid)
|
const processGroupId = this.inspector.foregroundPgid(this.pid)
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ describe('LocalTerminalHandle', () => {
|
|||||||
const handle = new LocalTerminalHandle(pty.asPty(), inspector, 20)
|
const handle = new LocalTerminalHandle(pty.asPty(), inspector, 20)
|
||||||
|
|
||||||
const quiescent = handle.terminate()
|
const quiescent = handle.terminate()
|
||||||
|
expect(handle.terminate()).toBe(quiescent)
|
||||||
await vi.advanceTimersByTimeAsync(20)
|
await vi.advanceTimersByTimeAsync(20)
|
||||||
expect(inspector.processes).toContainEqual([124, 'SIGKILL'])
|
expect(inspector.processes).toContainEqual([124, 'SIGKILL'])
|
||||||
expect(pty.kills).toEqual([])
|
expect(pty.kills).toEqual([])
|
||||||
|
|||||||
Reference in New Issue
Block a user