Fix terminal PTY teardown races
This commit is contained in:
@@ -32,13 +32,9 @@ afterEach(async () => {
|
||||
const terminalsByCwd = await Promise.all(
|
||||
manager.listDirectories().map((cwd) => manager.getTerminals(cwd)),
|
||||
);
|
||||
await Promise.all(
|
||||
terminalsByCwd
|
||||
.flat()
|
||||
.map((terminal) =>
|
||||
manager.killTerminalAndWait(terminal.id, { gracefulTimeoutMs: 50, forceTimeoutMs: 50 }),
|
||||
),
|
||||
);
|
||||
for (const terminal of terminalsByCwd.flat()) {
|
||||
await manager.killTerminalAndWait(terminal.id);
|
||||
}
|
||||
manager.killAll();
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
@@ -306,13 +302,12 @@ it("kills all terminals and clears state", async () => {
|
||||
it("emits cwd snapshots when terminals are created", async () => {
|
||||
manager = createTerminalManager();
|
||||
const cwd = realpathSync(tmpdir());
|
||||
const snapshots: Array<{ cwd: string; terminals: Array<{ name: string; title?: string }> }> = [];
|
||||
const snapshots: Array<{ cwd: string; terminals: Array<{ name: string }> }> = [];
|
||||
const unsubscribe = manager.subscribeTerminalsChanged((input) => {
|
||||
snapshots.push({
|
||||
cwd: input.cwd,
|
||||
terminals: input.terminals.map((terminal) => ({
|
||||
name: terminal.name,
|
||||
...(terminal.title ? { title: terminal.title } : {}),
|
||||
})),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -114,7 +114,7 @@ const temporaryDirs: string[] = [];
|
||||
|
||||
afterEach(async () => {
|
||||
for (const session of sessions) {
|
||||
session.kill();
|
||||
await session.killAndWait();
|
||||
}
|
||||
sessions.length = 0;
|
||||
while (temporaryDirs.length > 0) {
|
||||
|
||||
@@ -102,6 +102,10 @@ interface EnsureNodePtySpawnHelperExecutableOptions {
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
interface WindowsPtyProcessReadiness {
|
||||
_agent?: { innerPid?: number };
|
||||
}
|
||||
|
||||
function resolveNodePtyPackageRoot(): string | null {
|
||||
try {
|
||||
const packageJsonPath = require.resolve("node-pty/package.json");
|
||||
@@ -754,6 +758,22 @@ export async function createTerminal(options: CreateTerminalOptions): Promise<Te
|
||||
disposeResources();
|
||||
});
|
||||
|
||||
async function waitForPtyProcessStart(): Promise<void> {
|
||||
if (process.platform !== "win32") {
|
||||
return;
|
||||
}
|
||||
|
||||
const started = (): boolean => {
|
||||
const windowsPtyProcess = ptyProcess as unknown as WindowsPtyProcessReadiness;
|
||||
return ptyProcess.pid > 0 || (windowsPtyProcess._agent?.innerPid ?? 0) > 0 || processExited;
|
||||
};
|
||||
|
||||
const deadline = Date.now() + 5000;
|
||||
while (!started() && Date.now() < deadline) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
}
|
||||
}
|
||||
|
||||
function getState(): TerminalState {
|
||||
return {
|
||||
rows: terminal.rows,
|
||||
@@ -917,10 +937,16 @@ export async function createTerminal(options: CreateTerminalOptions): Promise<Te
|
||||
function kill(): void {
|
||||
if (!killed) {
|
||||
killed = true;
|
||||
killPtyProcess();
|
||||
if (!processExited) {
|
||||
killPtyProcess();
|
||||
}
|
||||
emitExit(buildExitInfo());
|
||||
}
|
||||
disposeResources();
|
||||
if (processExited) {
|
||||
disposeResources();
|
||||
return;
|
||||
}
|
||||
void waitForProcessExit(1000).finally(disposeResources);
|
||||
}
|
||||
|
||||
function killPtyProcess(signal?: NodeJS.Signals): void {
|
||||
@@ -987,6 +1013,8 @@ export async function createTerminal(options: CreateTerminalOptions): Promise<Te
|
||||
kill();
|
||||
}
|
||||
|
||||
await waitForPtyProcessStart();
|
||||
|
||||
// Small delay to let shell initialize
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user