fix(runtime): close lifecycle teardown races
This commit is contained in:
@@ -16,6 +16,8 @@ export interface ProcessInspector {
|
||||
isStdinWaiting(pgid: number): boolean
|
||||
/** Return the root and its current transitive descendants, children first. */
|
||||
processTree(rootPid: number): ProcessIdentity[]
|
||||
/** Return current members of one POSIX process session when the platform exposes them. */
|
||||
processSession(sessionId: number): ProcessIdentity[]
|
||||
/** Return whether the exact identity remains a non-quiescent process. */
|
||||
isAlive(identity: ProcessIdentity): boolean
|
||||
signalGroup(pgid: number, signal: SubprocessTerminalSignal): void
|
||||
@@ -201,6 +203,7 @@ abstract class PosixProcessInspector implements ProcessInspector {
|
||||
abstract foregroundPgid(shellPid: number): number | undefined
|
||||
abstract isStdinWaiting(pgid: number): boolean
|
||||
abstract processTree(rootPid: number): ProcessIdentity[]
|
||||
abstract processSession(sessionId: number): ProcessIdentity[]
|
||||
abstract isAlive(identity: ProcessIdentity): boolean
|
||||
|
||||
signalGroup(pgid: number, signal: SubprocessTerminalSignal): void {
|
||||
@@ -272,6 +275,13 @@ class LinuxProcessInspector extends PosixProcessInspector {
|
||||
return processTree(entries, rootPid)
|
||||
}
|
||||
|
||||
processSession(sessionId: number): ProcessIdentity[] {
|
||||
return numericEntries(this.internals, '/proc').flatMap((pid) => {
|
||||
const stat = readLinuxStat(this.internals, pid)
|
||||
return stat?.session === sessionId ? [{ pid, started: stat.started }] : []
|
||||
})
|
||||
}
|
||||
|
||||
isAlive(identity: ProcessIdentity): boolean {
|
||||
const stat = readLinuxStat(this.internals, identity.pid)
|
||||
return stat?.started === identity.started && !/^[ZXx]$/.test(stat.state)
|
||||
@@ -307,6 +317,10 @@ class MacProcessInspector extends PosixProcessInspector {
|
||||
return processTree(macProcessTable(this.internals), rootPid)
|
||||
}
|
||||
|
||||
processSession(_sessionId: number): ProcessIdentity[] {
|
||||
return []
|
||||
}
|
||||
|
||||
isAlive(identity: ProcessIdentity): boolean {
|
||||
return macProcessTable(this.internals).some(entry => entry.pid === identity.pid && entry.started === identity.started)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ export class LocalTerminalHandle implements SubprocessTerminalHandle {
|
||||
private exited = false
|
||||
private termination: Promise<void> | undefined
|
||||
private removeAbort: (() => void) | undefined
|
||||
private trackedDescendants: ProcessIdentity[] = []
|
||||
|
||||
/**
|
||||
* @param terminal - allocated node-pty process.
|
||||
@@ -86,6 +87,7 @@ export class LocalTerminalHandle implements SubprocessTerminalHandle {
|
||||
// Local inspection is synchronous; the seam returns a promise for remote transports.
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
async inspectForeground(): Promise<SubprocessTerminalForeground | undefined> {
|
||||
this.descendants()
|
||||
const processGroupId = this.inspector.foregroundPgid(this.pid)
|
||||
if (processGroupId === undefined) return undefined
|
||||
return {
|
||||
@@ -145,7 +147,12 @@ export class LocalTerminalHandle implements SubprocessTerminalHandle {
|
||||
}
|
||||
|
||||
private descendants(): ProcessIdentity[] {
|
||||
return this.inspector.processTree(this.pid).filter(member => member.pid !== this.pid)
|
||||
this.trackedDescendants = this.survivors(this.unionMembers(
|
||||
this.trackedDescendants,
|
||||
this.inspector.processTree(this.pid),
|
||||
this.inspector.processSession(this.pid),
|
||||
).filter(member => member.pid !== this.pid))
|
||||
return this.trackedDescendants
|
||||
}
|
||||
|
||||
private async waitForMembers(members: ProcessIdentity[]): Promise<ProcessIdentity[]> {
|
||||
|
||||
Reference in New Issue
Block a user