Skip to content

Commit fd05c0c

Browse files
authoredSep 6, 2022
Make useFsEvents as default strategy for the watching (#50366)
* Remove unnecessary parameter * Dont store name unnecessarily in the watchers * Polled watches and not files * Use fs events as default watching * Some refactoring * Make single per directory native watchers now that we are using it as default * Rename * Comment
1 parent 5c2f770 commit fd05c0c

File tree

404 files changed

+13762
-14412
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

404 files changed

+13762
-14412
lines changed
 

‎src/compiler/sys.ts

+86-74
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ namespace ts {
3535

3636
export type FileWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, modifiedTime?: Date) => void;
3737
export type DirectoryWatcherCallback = (fileName: string) => void;
38-
/*@internal*/
39-
export interface WatchedFile {
38+
interface WatchedFile {
4039
readonly fileName: string;
4140
readonly callback: FileWatcherCallback;
4241
mtime: Date;
@@ -81,8 +80,7 @@ namespace ts {
8180
/* @internal */
8281
export let unchangedPollThresholds = createPollingIntervalBasedLevels(defaultChunkLevels);
8382

84-
/* @internal */
85-
export function setCustomPollingValues(system: System) {
83+
function setCustomPollingValues(system: System) {
8684
if (!system.getEnvironmentVariable) {
8785
return;
8886
}
@@ -189,31 +187,28 @@ namespace ts {
189187
}
190188
}
191189

192-
/* @internal */
193-
export function createDynamicPriorityPollingWatchFile(host: {
190+
interface WatchedFileWithUnchangedPolls extends WatchedFileWithIsClosed {
191+
unchangedPolls: number;
192+
}
193+
function createDynamicPriorityPollingWatchFile(host: {
194194
getModifiedTime: NonNullable<System["getModifiedTime"]>;
195195
setTimeout: NonNullable<System["setTimeout"]>;
196196
}): HostWatchFile {
197-
interface WatchedFile extends ts.WatchedFile {
198-
isClosed?: boolean;
199-
unchangedPolls: number;
200-
}
201-
202-
interface PollingIntervalQueue extends Array<WatchedFile> {
197+
interface PollingIntervalQueue extends Array<WatchedFileWithUnchangedPolls> {
203198
pollingInterval: PollingInterval;
204199
pollIndex: number;
205200
pollScheduled: boolean;
206201
}
207202

208-
const watchedFiles: WatchedFile[] = [];
209-
const changedFilesInLastPoll: WatchedFile[] = [];
203+
const watchedFiles: WatchedFileWithUnchangedPolls[] = [];
204+
const changedFilesInLastPoll: WatchedFileWithUnchangedPolls[] = [];
210205
const lowPollingIntervalQueue = createPollingIntervalQueue(PollingInterval.Low);
211206
const mediumPollingIntervalQueue = createPollingIntervalQueue(PollingInterval.Medium);
212207
const highPollingIntervalQueue = createPollingIntervalQueue(PollingInterval.High);
213208
return watchFile;
214209

215210
function watchFile(fileName: string, callback: FileWatcherCallback, defaultPollingInterval: PollingInterval): FileWatcher {
216-
const file: WatchedFile = {
211+
const file: WatchedFileWithUnchangedPolls = {
217212
fileName,
218213
callback,
219214
unchangedPolls: 0,
@@ -233,7 +228,7 @@ namespace ts {
233228
}
234229

235230
function createPollingIntervalQueue(pollingInterval: PollingInterval): PollingIntervalQueue {
236-
const queue = [] as WatchedFile[] as PollingIntervalQueue;
231+
const queue = [] as WatchedFileWithUnchangedPolls[] as PollingIntervalQueue;
237232
queue.pollingInterval = pollingInterval;
238233
queue.pollIndex = 0;
239234
queue.pollScheduled = false;
@@ -265,7 +260,7 @@ namespace ts {
265260
}
266261
}
267262

268-
function pollQueue(queue: (WatchedFile | undefined)[], pollingInterval: PollingInterval, pollIndex: number, chunkSize: number) {
263+
function pollQueue(queue: (WatchedFileWithUnchangedPolls | undefined)[], pollingInterval: PollingInterval, pollIndex: number, chunkSize: number) {
269264
return pollWatchedFileQueue(
270265
host,
271266
queue,
@@ -274,7 +269,7 @@ namespace ts {
274269
onWatchFileStat
275270
);
276271

277-
function onWatchFileStat(watchedFile: WatchedFile, pollIndex: number, fileChanged: boolean) {
272+
function onWatchFileStat(watchedFile: WatchedFileWithUnchangedPolls, pollIndex: number, fileChanged: boolean) {
278273
if (fileChanged) {
279274
watchedFile.unchangedPolls = 0;
280275
// Changed files go to changedFilesInLastPoll queue
@@ -311,12 +306,12 @@ namespace ts {
311306
}
312307
}
313308

314-
function addToPollingIntervalQueue(file: WatchedFile, pollingInterval: PollingInterval) {
309+
function addToPollingIntervalQueue(file: WatchedFileWithUnchangedPolls, pollingInterval: PollingInterval) {
315310
pollingIntervalQueue(pollingInterval).push(file);
316311
scheduleNextPollIfNotAlreadyScheduled(pollingInterval);
317312
}
318313

319-
function addChangedFileToLowPollingIntervalQueue(file: WatchedFile) {
314+
function addChangedFileToLowPollingIntervalQueue(file: WatchedFileWithUnchangedPolls) {
320315
changedFilesInLastPoll.push(file);
321316
scheduleNextPollIfNotAlreadyScheduled(PollingInterval.Low);
322317
}
@@ -423,59 +418,50 @@ namespace ts {
423418
}
424419
}
425420

426-
/* @internal */
427-
export function createSingleFileWatcherPerName(
428-
watchFile: HostWatchFile,
429-
useCaseSensitiveFileNames: boolean
430-
): HostWatchFile {
431-
interface SingleFileWatcher {
432-
watcher: FileWatcher;
433-
refCount: number;
434-
}
435-
const cache = new Map<string, SingleFileWatcher>();
436-
const callbacksCache = createMultiMap<FileWatcherCallback>();
421+
interface SingleFileWatcher<T extends FileWatcherCallback | FsWatchCallback>{
422+
watcher: FileWatcher;
423+
callbacks: T[];
424+
}
425+
function createSingleWatcherPerName<T extends FileWatcherCallback | FsWatchCallback>(
426+
cache: Map<SingleFileWatcher<T>>,
427+
useCaseSensitiveFileNames: boolean,
428+
name: string,
429+
callback: T,
430+
createWatcher: (callback: T) => FileWatcher,
431+
): FileWatcher {
437432
const toCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
433+
const path = toCanonicalFileName(name);
434+
const existing = cache.get(path);
435+
if (existing) {
436+
existing.callbacks.push(callback);
437+
}
438+
else {
439+
cache.set(path, {
440+
watcher: createWatcher((
441+
// Cant infer types correctly so lets satisfy checker
442+
(param1: any, param2: never, param3: any) => cache.get(path)?.callbacks.slice().forEach(cb => cb(param1, param2, param3))
443+
) as T),
444+
callbacks: [callback]
445+
});
446+
}
438447

439-
return (fileName, callback, pollingInterval, options) => {
440-
const path = toCanonicalFileName(fileName);
441-
const existing = cache.get(path);
442-
if (existing) {
443-
existing.refCount++;
444-
}
445-
else {
446-
cache.set(path, {
447-
watcher: watchFile(
448-
fileName,
449-
(fileName, eventKind, modifiedTime) => forEach(
450-
callbacksCache.get(path),
451-
cb => cb(fileName, eventKind, modifiedTime)
452-
),
453-
pollingInterval,
454-
options
455-
),
456-
refCount: 1
457-
});
448+
return {
449+
close: () => {
450+
const watcher = cache.get(path);
451+
// Watcher is not expected to be undefined, but if it is normally its because
452+
// exception was thrown somewhere else and watch state is not what it should be
453+
if (!watcher) return;
454+
if (!orderedRemoveItem(watcher.callbacks, callback) || watcher.callbacks.length) return;
455+
cache.delete(path);
456+
closeFileWatcherOf(watcher);
458457
}
459-
callbacksCache.add(path, callback);
460-
461-
return {
462-
close: () => {
463-
const watcher = Debug.checkDefined(cache.get(path));
464-
callbacksCache.remove(path, callback);
465-
watcher.refCount--;
466-
if (watcher.refCount) return;
467-
cache.delete(path);
468-
closeFileWatcherOf(watcher);
469-
}
470-
};
471458
};
472459
}
473460

474461
/**
475462
* Returns true if file status changed
476463
*/
477-
/*@internal*/
478-
export function onWatchedFileStat(watchedFile: WatchedFile, modifiedTime: Date): boolean {
464+
function onWatchedFileStat(watchedFile: WatchedFile, modifiedTime: Date): boolean {
479465
const oldTime = watchedFile.mtime.getTime();
480466
const newTime = modifiedTime.getTime();
481467
if (oldTime !== newTime) {
@@ -512,8 +498,7 @@ namespace ts {
512498
curSysLog = logger;
513499
}
514500

515-
/*@internal*/
516-
export interface RecursiveDirectoryWatcherHost {
501+
interface RecursiveDirectoryWatcherHost {
517502
watchDirectory: HostWatchDirectory;
518503
useCaseSensitiveFileNames: boolean;
519504
getCurrentDirectory: System["getCurrentDirectory"];
@@ -529,8 +514,7 @@ namespace ts {
529514
* that means if this is recursive watcher, watch the children directories as well
530515
* (eg on OS that dont support recursive watch using fs.watch use fs.watchFile)
531516
*/
532-
/*@internal*/
533-
export function createDirectoryWatcherSupportingRecursive({
517+
function createDirectoryWatcherSupportingRecursive({
534518
watchDirectory,
535519
useCaseSensitiveFileNames,
536520
getCurrentDirectory,
@@ -792,8 +776,7 @@ namespace ts {
792776
Directory,
793777
}
794778

795-
/*@internal*/
796-
export function createFileWatcherCallback(callback: FsWatchCallback): FileWatcherCallback {
779+
function createFileWatcherCallback(callback: FsWatchCallback): FileWatcherCallback {
797780
return (_fileName, eventKind, modifiedTime) => callback(eventKind === FileWatcherEventKind.Changed ? "change" : "rename", "", modifiedTime);
798781
}
799782

@@ -854,7 +837,7 @@ namespace ts {
854837
/*@internal*/
855838
export interface CreateSystemWatchFunctions {
856839
// Polling watch file
857-
pollingWatchFile: HostWatchFile;
840+
pollingWatchFileWorker: HostWatchFile;
858841
// For dynamic polling watch file
859842
getModifiedTime: NonNullable<System["getModifiedTime"]>;
860843
setTimeout: NonNullable<System["setTimeout"]>;
@@ -878,7 +861,7 @@ namespace ts {
878861

879862
/*@internal*/
880863
export function createSystemWatchFunctions({
881-
pollingWatchFile,
864+
pollingWatchFileWorker,
882865
getModifiedTime,
883866
setTimeout,
884867
clearTimeout,
@@ -896,6 +879,9 @@ namespace ts {
896879
inodeWatching,
897880
sysLog,
898881
}: CreateSystemWatchFunctions): { watchFile: HostWatchFile; watchDirectory: HostWatchDirectory; } {
882+
const pollingWatches = new Map<string, SingleFileWatcher<FileWatcherCallback>>();
883+
const fsWatches = new Map<string, SingleFileWatcher<FsWatchCallback>>();
884+
const fsWatchesRecursive = new Map<string, SingleFileWatcher<FsWatchCallback>>();
899885
let dynamicPollingWatchFile: HostWatchFile | undefined;
900886
let fixedChunkSizePollingWatchFile: HostWatchFile | undefined;
901887
let nonPollingWatchFile: HostWatchFile | undefined;
@@ -968,7 +954,7 @@ namespace ts {
968954
// Use notifications from FS to watch with falling back to fs.watchFile
969955
generateWatchFileOptions(WatchFileKind.UseFsEventsOnParentDirectory, PollingWatchKind.PriorityInterval, options) :
970956
// Default to do not use fixed polling interval
971-
{ watchFile: defaultWatchFileKind?.() || WatchFileKind.FixedPollingInterval };
957+
{ watchFile: defaultWatchFileKind?.() || WatchFileKind.UseFsEvents };
972958
}
973959
}
974960

@@ -1073,13 +1059,39 @@ namespace ts {
10731059
}
10741060
}
10751061

1062+
function pollingWatchFile(fileName: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined) {
1063+
return createSingleWatcherPerName(
1064+
pollingWatches,
1065+
useCaseSensitiveFileNames,
1066+
fileName,
1067+
callback,
1068+
cb => pollingWatchFileWorker(fileName, cb, pollingInterval, options),
1069+
);
1070+
}
10761071
function fsWatch(
10771072
fileOrDirectory: string,
10781073
entryKind: FileSystemEntryKind,
10791074
callback: FsWatchCallback,
10801075
recursive: boolean,
10811076
fallbackPollingInterval: PollingInterval,
10821077
fallbackOptions: WatchOptions | undefined
1078+
): FileWatcher {
1079+
return createSingleWatcherPerName(
1080+
recursive ? fsWatchesRecursive : fsWatches,
1081+
useCaseSensitiveFileNames,
1082+
fileOrDirectory,
1083+
callback,
1084+
cb => fsWatchHandlingExistenceOnHost(fileOrDirectory, entryKind, cb, recursive, fallbackPollingInterval, fallbackOptions),
1085+
);
1086+
}
1087+
1088+
function fsWatchHandlingExistenceOnHost(
1089+
fileOrDirectory: string,
1090+
entryKind: FileSystemEntryKind,
1091+
callback: FsWatchCallback,
1092+
recursive: boolean,
1093+
fallbackPollingInterval: PollingInterval,
1094+
fallbackOptions: WatchOptions | undefined
10831095
): FileWatcher {
10841096
let lastDirectoryPartWithDirectorySeparator: string | undefined;
10851097
let lastDirectoryPart: string | undefined;
@@ -1445,7 +1457,7 @@ namespace ts {
14451457
const fsSupportsRecursiveFsWatch = isNode4OrLater && (process.platform === "win32" || process.platform === "darwin");
14461458
const getCurrentDirectory = memoize(() => process.cwd());
14471459
const { watchFile, watchDirectory } = createSystemWatchFunctions({
1448-
pollingWatchFile: createSingleFileWatcherPerName(fsWatchFileWorker, useCaseSensitiveFileNames),
1460+
pollingWatchFileWorker: fsWatchFileWorker,
14491461
getModifiedTime,
14501462
setTimeout,
14511463
clearTimeout,

0 commit comments

Comments
 (0)
Please sign in to comment.