From c0d5c29080e242f85a55e52e42f61065042d1730 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Mon, 28 Jun 2021 15:45:35 -0700 Subject: [PATCH] Reduce exceptions (#44710) * Don't visit non-existent basePaths * Stop trying to add file watchers after hitting the system limit * Update tests --- src/compiler/sys.ts | 12 ++++++++++-- src/compiler/utilities.ts | 6 ++++-- src/compiler/watchUtilities.ts | 2 +- src/harness/fakesHosts.ts | 2 +- src/harness/virtualFileSystemWithWatch.ts | 2 +- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 86029e971ccad..e08890c0bab4e 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -1266,6 +1266,7 @@ namespace ts { let activeSession: import("inspector").Session | "stopping" | undefined; let profilePath = "./profile.cpuprofile"; + let hitSystemWatcherLimit = false; const Buffer: { new (input: string, encoding?: string): any; @@ -1619,6 +1620,12 @@ namespace ts { options = { persistent: true }; } } + + if (hitSystemWatcherLimit) { + sysLog(`sysLog:: ${fileOrDirectory}:: Defaulting to fsWatchFile`); + return watchPresentFileSystemEntryWithFsWatchFile(); + } + try { const presentWatcher = _fs.watch( fileOrDirectory, @@ -1635,6 +1642,8 @@ namespace ts { // Catch the exception and use polling instead // Eg. on linux the number of watches are limited and one could easily exhaust watches and the exception ENOSPC is thrown when creating watcher at that point // so instead of throwing error, use fs.watchFile + hitSystemWatcherLimit ||= e.code === "ENOSPC"; + sysLog(`sysLog:: ${fileOrDirectory}:: Changing to fsWatchFile`); return watchPresentFileSystemEntryWithFsWatchFile(); } } @@ -1656,7 +1665,6 @@ namespace ts { * Eg. on linux the number of watches are limited and one could easily exhaust watches and the exception ENOSPC is thrown when creating watcher at that point */ function watchPresentFileSystemEntryWithFsWatchFile(): FileWatcher { - sysLog(`sysLog:: ${fileOrDirectory}:: Changing to fsWatchFile`); return watchFile( fileOrDirectory, createFileWatcherCallback(callback), @@ -1796,7 +1804,7 @@ namespace ts { } function readDirectory(path: string, extensions?: readonly string[], excludes?: readonly string[], includes?: readonly string[], depth?: number): string[] { - return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), depth, getAccessibleFileSystemEntries, realpath); + return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), depth, getAccessibleFileSystemEntries, realpath, directoryExists); } function fileSystemEntryExists(path: string, entryKind: FileSystemEntryKind): boolean { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 368662ec80529..52ed8f1d77c40 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6515,7 +6515,7 @@ namespace ts { } /** @param path directory of the tsconfig.json */ - export function matchFiles(path: string, extensions: readonly string[] | undefined, excludes: readonly string[] | undefined, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries, realpath: (path: string) => string): string[] { + export function matchFiles(path: string, extensions: readonly string[] | undefined, excludes: readonly string[] | undefined, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries, realpath: (path: string) => string, directoryExists: (path: string) => boolean): string[] { path = normalizePath(path); currentDirectory = normalizePath(currentDirectory); @@ -6531,7 +6531,9 @@ namespace ts { const visited = new Map(); const toCanonical = createGetCanonicalFileName(useCaseSensitiveFileNames); for (const basePath of patterns.basePaths) { - visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth); + if (directoryExists(basePath)) { + visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth); + } } return flatten(results); diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index 647c0ffdd14d7..39256b39acb68 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -184,7 +184,7 @@ namespace ts { const rootResult = tryReadDirectory(rootDir, rootDirPath); let rootSymLinkResult: FileSystemEntries | undefined; if (rootResult !== undefined) { - return matchFiles(rootDir, extensions, excludes, includes, useCaseSensitiveFileNames, currentDirectory, depth, getFileSystemEntries, realpath); + return matchFiles(rootDir, extensions, excludes, includes, useCaseSensitiveFileNames, currentDirectory, depth, getFileSystemEntries, realpath, directoryExists); } return host.readDirectory!(rootDir, extensions, excludes, includes, depth); diff --git a/src/harness/fakesHosts.ts b/src/harness/fakesHosts.ts index 58b29a4c7ef98..898cfc048c32e 100644 --- a/src/harness/fakesHosts.ts +++ b/src/harness/fakesHosts.ts @@ -95,7 +95,7 @@ namespace fakes { } public readDirectory(path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number): string[] { - return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, path => this.getAccessibleFileSystemEntries(path), path => this.realpath(path)); + return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, path => this.getAccessibleFileSystemEntries(path), path => this.realpath(path), path => this.directoryExists(path)); } public getAccessibleFileSystemEntries(path: string): ts.FileSystemEntries { diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 62c6cd042ef93..77e3741d1fce8 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -922,7 +922,7 @@ interface Array { length: number; [n: number]: T; }` }); } return { directories, files }; - }, path => this.realpath(path)); + }, path => this.realpath(path), path => this.directoryExists(path)); } createHash(s: string): string {