Skip to content

Commit b43d8f1

Browse files
committed
Self-review
1 parent 8679e36 commit b43d8f1

File tree

2 files changed

+51
-42
lines changed

2 files changed

+51
-42
lines changed

src/remote/sshProcess.ts

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export interface SshProcessMonitorOptions {
3939
remoteSshExtensionId: string;
4040
}
4141

42-
// Cleanup threshold for old network info files (1 hour)
42+
// Cleanup threshold for old network info files
4343
const CLEANUP_MAX_AGE_MS = 60 * 60 * 1000;
4444

4545
/**
@@ -81,11 +81,11 @@ export class SshProcessMonitor implements vscode.Disposable {
8181
maxAgeMs: number,
8282
logger: Logger,
8383
): Promise<void> {
84-
const deletedFiles: string[] = [];
8584
try {
8685
const files = await fs.readdir(networkInfoPath);
8786
const now = Date.now();
8887

88+
const deletedFiles: string[] = [];
8989
for (const file of files) {
9090
if (!file.endsWith(".json")) {
9191
continue;
@@ -104,14 +104,14 @@ export class SshProcessMonitor implements vscode.Disposable {
104104
// File may have been deleted by another process or doesn't exist, ignore
105105
}
106106
}
107-
} catch {
108-
// Directory may not exist yet, that's fine
109-
}
110107

111-
if (deletedFiles.length > 0) {
112-
logger.debug(
113-
`Cleaned up ${deletedFiles.length} old network info file(s): ${deletedFiles.join(", ")}`,
114-
);
108+
if (deletedFiles.length > 0) {
109+
logger.debug(
110+
`Cleaned up ${deletedFiles.length} old network info file(s): ${deletedFiles.join(", ")}`,
111+
);
112+
}
113+
} catch {
114+
// Directory may not exist yet, ignore
115115
}
116116
}
117117

@@ -339,57 +339,58 @@ export class SshProcessMonitor implements vscode.Disposable {
339339

340340
/**
341341
* Monitors network info and updates the status bar.
342-
* Checks file mtime to detect stale connections and trigger reconnection search.
343-
* After consecutive failures to read the file, searches for a new process.
342+
* Searches for a new process if the file is stale or unreadable.
344343
*/
345344
private async monitorNetwork(): Promise<void> {
346345
const { networkInfoPath, networkPollInterval, logger } = this.options;
347346
const staleThreshold = networkPollInterval * 5;
348347
const maxReadFailures = 5;
349-
let consecutiveReadFailures = 0;
348+
let readFailures = 0;
350349

351350
while (!this.disposed && this.currentPid !== undefined) {
352-
const networkInfoFile = path.join(
353-
networkInfoPath,
354-
`${this.currentPid}.json`,
355-
);
356-
357-
let searchReason = "";
351+
const filePath = path.join(networkInfoPath, `${this.currentPid}.json`);
352+
let search: { needed: true; reason: string } | { needed: false } = {
353+
needed: false,
354+
};
358355

359356
try {
360-
const stats = await fs.stat(networkInfoFile);
357+
const stats = await fs.stat(filePath);
361358
const ageMs = Date.now() - stats.mtime.getTime();
362-
363-
consecutiveReadFailures = 0;
359+
readFailures = 0;
364360

365361
if (ageMs > staleThreshold) {
366-
searchReason = `Network info stale (${Math.round(ageMs / 1000)}s old)`;
362+
search = {
363+
needed: true,
364+
reason: `Network info stale (${Math.round(ageMs / 1000)}s old)`,
365+
};
367366
} else {
368-
const content = await fs.readFile(networkInfoFile, "utf8");
367+
const content = await fs.readFile(filePath, "utf8");
369368
const network = JSON.parse(content) as NetworkInfo;
370369
const isStale = ageMs > networkPollInterval * 2;
371370
this.updateStatusBar(network, isStale);
372371
}
373372
} catch (error) {
374-
consecutiveReadFailures++;
373+
readFailures++;
375374
logger.debug(
376-
`Failed to read network info (attempt ${consecutiveReadFailures}): ${(error as Error).message}`,
375+
`Failed to read network info (attempt ${readFailures}): ${(error as Error).message}`,
377376
);
378-
379-
if (consecutiveReadFailures >= maxReadFailures) {
380-
searchReason = `Network info missing for ${consecutiveReadFailures} attempts`;
377+
if (readFailures >= maxReadFailures) {
378+
search = {
379+
needed: true,
380+
reason: `Network info missing for ${readFailures} attempts`,
381+
};
381382
}
382383
}
383384

384-
if (searchReason) {
385-
// Throttle: don't search too frequently
385+
// Search for new process if needed (with throttling)
386+
if (search.needed) {
386387
const timeSinceLastSearch = Date.now() - this.lastStaleSearchTime;
387388
if (timeSinceLastSearch < staleThreshold) {
388389
await this.delay(staleThreshold - timeSinceLastSearch);
389390
continue;
390391
}
391392

392-
logger.debug(`${searchReason}, searching for new SSH process`);
393+
logger.debug(`${search.reason}, searching for new SSH process`);
393394
// searchForProcess will update PID if a different process is found
394395
this.lastStaleSearchTime = Date.now();
395396
await this.searchForProcess();

test/unit/remote/sshProcess.test.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -424,12 +424,11 @@ describe("SshProcessMonitor", () => {
424424
});
425425

426426
describe("cleanup old network files", () => {
427-
const TWO_HOURS_AGO = Date.now() - 2 * 60 * 60 * 1000;
428-
429-
function setOldMtime(filePath: string): void {
427+
const setOldMtime = (filePath: string) => {
430428
// Default cleanup is 1 hour; set mtime to 2 hours ago to mark as old
429+
const TWO_HOURS_AGO = Date.now() - 2 * 60 * 60 * 1000;
431430
vol.utimesSync(filePath, TWO_HOURS_AGO / 1000, TWO_HOURS_AGO / 1000);
432-
}
431+
};
433432

434433
it("deletes old .json files but preserves recent and non-.json files", async () => {
435434
vol.fromJSON({
@@ -486,11 +485,22 @@ describe("SshProcessMonitor", () => {
486485
vol.fromJSON({
487486
"/logs/ms-vscode-remote.remote-ssh/1-Remote - SSH.log":
488487
"-> socksPort 12345 ->",
488+
"/network/789.json": "{}",
489489
});
490+
// Set mtime far into the future so 789.json is always considered fresh
491+
const FRESH_MTIME = Date.now() + 1_000_000;
492+
vol.utimesSync(
493+
"/network/789.json",
494+
FRESH_MTIME / 1000,
495+
FRESH_MTIME / 1000,
496+
);
490497

491498
vi.mocked(find)
492-
.mockResolvedValueOnce([{ pid: 999, ppid: 1, name: "ssh", cmd: "ssh" }])
493-
.mockResolvedValue([{ pid: 888, ppid: 1, name: "ssh", cmd: "ssh" }]);
499+
.mockResolvedValueOnce([{ pid: 123, ppid: 1, name: "ssh", cmd: "ssh" }])
500+
.mockResolvedValueOnce([{ pid: 456, ppid: 1, name: "ssh", cmd: "ssh" }])
501+
.mockResolvedValueOnce([{ pid: 789, ppid: 1, name: "ssh", cmd: "ssh" }])
502+
// This will not be found since `789.json` is found and is not stale!
503+
.mockResolvedValue([{ pid: 999, ppid: 1, name: "ssh", cmd: "ssh" }]);
494504

495505
const pollInterval = 10;
496506
const monitor = createMonitor({
@@ -499,15 +509,13 @@ describe("SshProcessMonitor", () => {
499509
networkPollInterval: pollInterval,
500510
});
501511

502-
await vi.advanceTimersByTimeAsync(pollInterval);
503-
504512
const pids: (number | undefined)[] = [];
505513
monitor.onPidChange((pid) => pids.push(pid));
506514

507-
// 5 failures at pollInterval each, then staleThreshold wait before search
508-
await vi.advanceTimersByTimeAsync(pollInterval * 5 * 2);
515+
// Advance enough time for the monitor to cycle through PIDs 123, 456, and find 789
516+
await vi.advanceTimersByTimeAsync(pollInterval * 100);
509517

510-
expect(pids).toContain(888);
518+
expect(pids).toEqual([123, 456, 789]);
511519
});
512520
});
513521

0 commit comments

Comments
 (0)