Skip to content

Commit fa8d4cb

Browse files
committed
Move the polling settings to sys instead of watch utilities
1 parent 09caaa3 commit fa8d4cb

7 files changed

Lines changed: 139 additions & 119 deletions

File tree

src/compiler/sys.ts

Lines changed: 104 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ namespace ts {
3232
}
3333

3434
/* @internal */
35-
export enum WatchPriority {
36-
High,
37-
Medium,
38-
Low
35+
export enum PollingInterval {
36+
High = 2000,
37+
Medium = 500,
38+
Low = 250
3939
}
4040

4141
function getPriorityValues(highPriorityValue: number): [number, number, number] {
@@ -44,36 +44,47 @@ namespace ts {
4444
return [highPriorityValue, mediumPriorityValue, lowPriorityValue];
4545
}
4646

47-
const pollingIntervalsForPriority = getPriorityValues(250);
48-
function pollingInterval(watchPriority: WatchPriority): number {
47+
function pollingInterval(watchPriority: PollingInterval): number {
4948
return pollingIntervalsForPriority[watchPriority];
5049
}
5150

51+
const pollingIntervalsForPriority = getPriorityValues(250);
52+
5253
/* @internal */
53-
export function watchFileUsingPriorityPollingInterval(host: System, fileName: string, callback: FileWatcherCallback, watchPriority: WatchPriority): FileWatcher {
54+
export function watchFileUsingPriorityPollingInterval(host: System, fileName: string, callback: FileWatcherCallback, watchPriority: PollingInterval): FileWatcher {
5455
return host.watchFile(fileName, callback, pollingInterval(watchPriority));
5556
}
5657

5758
/* @internal */
58-
export interface DynamicPriorityPollingStatsSet {
59-
watchFile(fileName: string, callback: FileWatcherCallback, defaultPriority: WatchPriority): FileWatcher;
60-
}
59+
export type HostWatchFile = (fileName: string, callback: FileWatcherCallback, pollingInterval: PollingInterval) => FileWatcher;
6160

6261
/* @internal */
6362
export const missingFileModifiedTime = new Date(0); // Any subsequent modification will occur after this time
6463

65-
const chunkSizeOrUnchangedThresholdsForPriority = getPriorityValues(32);
66-
function chunkSize(watchPriority: WatchPriority) {
67-
return chunkSizeOrUnchangedThresholdsForPriority[watchPriority];
64+
enum ChunkSize {
65+
Low = 32,
66+
Medium = 64,
67+
High = 256
68+
}
69+
70+
function chunkSize(pollingInterval: PollingInterval) {
71+
switch (pollingInterval) {
72+
case PollingInterval.Low:
73+
return ChunkSize.Low;
74+
case PollingInterval.Medium:
75+
return ChunkSize.Medium;
76+
case PollingInterval.High:
77+
return ChunkSize.High;
78+
}
6879
}
6980

7081
/*@internal*/
71-
export function unChangedThreshold(watchPriority: WatchPriority) {
72-
return chunkSizeOrUnchangedThresholdsForPriority[watchPriority];
82+
export function unChangedThreshold(pollingInterval: PollingInterval) {
83+
return chunkSize(pollingInterval);
7384
}
7485

7586
/* @internal */
76-
export function createDynamicPriorityPollingStatsSet(host: System): DynamicPriorityPollingStatsSet {
87+
export function createDynamicPriorityPollingWatchFile(host: System): HostWatchFile {
7788
if (!host.getModifiedTime || !host.setTimeout) {
7889
throw notImplemented();
7990
}
@@ -83,20 +94,20 @@ namespace ts {
8394
unchangedPolls: number;
8495
}
8596

86-
interface WatchPriorityQueue extends Array<WatchedFile> {
87-
watchPriority: WatchPriority;
97+
interface PollingIntervalQueue extends Array<WatchedFile> {
98+
pollingInterval: PollingInterval;
8899
pollIndex: number;
89100
pollScheduled: boolean;
90101
}
91102

92103
const watchedFiles: WatchedFile[] = [];
93104
const changedFilesInLastPoll: WatchedFile[] = [];
94-
const priorityQueues = [createPriorityQueue(WatchPriority.High), createPriorityQueue(WatchPriority.Medium), createPriorityQueue(WatchPriority.Low)];
95-
return {
96-
watchFile
97-
};
105+
const lowPollingIntervalQueue = createPollingIntervalQueue(PollingInterval.Low);
106+
const mediumPollingIntervalQueue = createPollingIntervalQueue(PollingInterval.Medium);
107+
const highPollingIntervalQueue = createPollingIntervalQueue(PollingInterval.High);
108+
return watchFile;
98109

99-
function watchFile(fileName: string, callback: FileWatcherCallback, defaultPriority: WatchPriority): FileWatcher {
110+
function watchFile(fileName: string, callback: FileWatcherCallback, defaultPollingInterval: PollingInterval): FileWatcher {
100111
const file: WatchedFile = {
101112
fileName,
102113
callback,
@@ -105,52 +116,51 @@ namespace ts {
105116
};
106117
watchedFiles.push(file);
107118

108-
addToPriorityQueue(file, defaultPriority);
119+
addToPollingIntervalQueue(file, defaultPollingInterval);
109120
return {
110121
close: () => {
111122
file.isClosed = true;
112123
// Remove from watchedFiles
113124
unorderedRemoveItem(watchedFiles, file);
114-
// Do not update priority queue since that will happen as part of polling
125+
// Do not update polling interval queue since that will happen as part of polling
115126
}
116127
};
117128
}
118129

119-
function createPriorityQueue(watchPriority: WatchPriority): WatchPriorityQueue {
120-
const queue = [] as WatchPriorityQueue;
121-
queue.watchPriority = watchPriority;
130+
function createPollingIntervalQueue(pollingInterval: PollingInterval): PollingIntervalQueue {
131+
const queue = [] as PollingIntervalQueue;
132+
queue.pollingInterval = pollingInterval;
122133
queue.pollIndex = 0;
123134
queue.pollScheduled = false;
124135
return queue;
125136
}
126137

127-
function pollPriorityQueue(queue: WatchPriorityQueue) {
128-
const priority = queue.watchPriority;
129-
queue.pollIndex = pollQueue(queue, priority, queue.pollIndex, chunkSize(priority));
138+
function pollPollingIntervalQueue(queue: PollingIntervalQueue) {
139+
queue.pollIndex = pollQueue(queue, queue.pollingInterval, queue.pollIndex, chunkSize(queue.pollingInterval));
130140
// Set the next polling index and timeout
131141
if (queue.length) {
132-
scheduleNextPoll(priority);
142+
scheduleNextPoll(queue.pollingInterval);
133143
}
134144
else {
135145
Debug.assert(queue.pollIndex === 0);
136146
queue.pollScheduled = false;
137147
}
138148
}
139149

140-
function pollHighPriorityQueue(queue: WatchPriorityQueue) {
150+
function pollLowPollingIntervalQueue(queue: PollingIntervalQueue) {
141151
// Always poll complete list of changedFilesInLastPoll
142-
pollQueue(changedFilesInLastPoll, WatchPriority.High, /*pollIndex*/ 0, changedFilesInLastPoll.length);
152+
pollQueue(changedFilesInLastPoll, PollingInterval.Low, /*pollIndex*/ 0, changedFilesInLastPoll.length);
143153

144154
// Finally do the actual polling of the queue
145-
pollPriorityQueue(queue);
155+
pollPollingIntervalQueue(queue);
146156
// Schedule poll if there are files in changedFilesInLastPoll but no files in the actual queue
147-
// as pollPriorityQueue wont schedule for next poll
157+
// as pollPollingIntervalQueue wont schedule for next poll
148158
if (!queue.pollScheduled && changedFilesInLastPoll.length) {
149-
scheduleNextPoll(WatchPriority.High);
159+
scheduleNextPoll(PollingInterval.Low);
150160
}
151161
}
152162

153-
function pollQueue(queue: WatchedFile[], priority: WatchPriority, pollIndex: number, chunkSize: number) {
163+
function pollQueue(queue: WatchedFile[], pollingInterval: PollingInterval, pollIndex: number, chunkSize: number) {
154164
// Max visit would be all elements of the queue
155165
let needsVisit = queue.length;
156166
let definedValueCopyToIndex = pollIndex;
@@ -175,22 +185,22 @@ namespace ts {
175185
// Changed files go to changedFilesInLastPoll queue
176186
if (queue !== changedFilesInLastPoll) {
177187
queue[pollIndex] = undefined;
178-
addChangedFileToHighPriorityQueue(watchedFile);
188+
addChangedFileToLowPollingIntervalQueue(watchedFile);
179189
}
180190
}
181-
else if (watchedFile.unchangedPolls !== unChangedThreshold(priority)) {
191+
else if (watchedFile.unchangedPolls !== unChangedThreshold(pollingInterval)) {
182192
watchedFile.unchangedPolls++;
183193
}
184194
else if (queue === changedFilesInLastPoll) {
185-
// Restart unchangedPollCount for unchanged file and move to high priority queue
195+
// Restart unchangedPollCount for unchanged file and move to low polling interval queue
186196
watchedFile.unchangedPolls = 1;
187197
queue[pollIndex] = undefined;
188-
addToPriorityQueue(watchedFile, WatchPriority.High);
198+
addToPollingIntervalQueue(watchedFile, PollingInterval.Low);
189199
}
190-
else if (priority !== WatchPriority.Low) {
200+
else if (pollingInterval !== PollingInterval.High) {
191201
watchedFile.unchangedPolls++;
192202
queue[pollIndex] = undefined;
193-
addToPriorityQueue(watchedFile, priority + 1);
203+
addToPollingIntervalQueue(watchedFile, pollingInterval === PollingInterval.Low ? PollingInterval.Medium : PollingInterval.High);
194204
}
195205

196206
if (queue[pollIndex]) {
@@ -219,24 +229,35 @@ namespace ts {
219229
}
220230
}
221231

222-
function addToPriorityQueue(file: WatchedFile, priority: WatchPriority) {
223-
priorityQueues[priority].push(file);
224-
scheduleNextPollIfNotAlreadyScheduled(priority);
232+
function pollingIntervalQueue(pollingInterval: PollingInterval) {
233+
switch (pollingInterval) {
234+
case PollingInterval.Low:
235+
return lowPollingIntervalQueue;
236+
case PollingInterval.Medium:
237+
return mediumPollingIntervalQueue;
238+
case PollingInterval.High:
239+
return highPollingIntervalQueue;
240+
}
241+
}
242+
243+
function addToPollingIntervalQueue(file: WatchedFile, pollingInterval: PollingInterval) {
244+
pollingIntervalQueue(pollingInterval).push(file);
245+
scheduleNextPollIfNotAlreadyScheduled(pollingInterval);
225246
}
226247

227-
function addChangedFileToHighPriorityQueue(file: WatchedFile) {
248+
function addChangedFileToLowPollingIntervalQueue(file: WatchedFile) {
228249
changedFilesInLastPoll.push(file);
229-
scheduleNextPollIfNotAlreadyScheduled(WatchPriority.High);
250+
scheduleNextPollIfNotAlreadyScheduled(PollingInterval.Low);
230251
}
231252

232-
function scheduleNextPollIfNotAlreadyScheduled(priority: WatchPriority) {
233-
if (!priorityQueues[priority].pollScheduled) {
234-
scheduleNextPoll(priority);
253+
function scheduleNextPollIfNotAlreadyScheduled(pollingInterval: PollingInterval) {
254+
if (!pollingIntervalQueue(pollingInterval).pollScheduled) {
255+
scheduleNextPoll(pollingInterval);
235256
}
236257
}
237258

238-
function scheduleNextPoll(priority: WatchPriority) {
239-
priorityQueues[priority].pollScheduled = host.setTimeout(priority === WatchPriority.High ? pollHighPriorityQueue : pollPriorityQueue, pollingInterval(priority), priorityQueues[priority]);
259+
function scheduleNextPoll(pollingInterval: PollingInterval) {
260+
pollingIntervalQueue(pollingInterval).pollScheduled = host.setTimeout(pollingInterval === PollingInterval.Low ? pollLowPollingIntervalQueue : pollPollingIntervalQueue, pollingInterval, pollingIntervalQueue(pollingInterval));
240261
}
241262

242263
function getModifiedTime(fileName: string) {
@@ -381,6 +402,7 @@ namespace ts {
381402
}
382403

383404
const useNonPollingWatchers = process.env.TSC_NONPOLLING_WATCHER;
405+
const tscWatchOption = process.env.TSC_WATCHOPTION;
384406

385407
const nodeSystem: System = {
386408
args: process.argv.slice(2),
@@ -391,20 +413,6 @@ namespace ts {
391413
},
392414
readFile,
393415
writeFile,
394-
watchFile: useNonPollingWatchers ? createNonPollingWatchFile() : fsWatchFile,
395-
watchDirectory: (directoryName, callback, recursive) => {
396-
// Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
397-
// (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
398-
return fsWatchDirectory(directoryName, (eventName, relativeFileName) => {
399-
// In watchDirectory we only care about adding and removing files (when event name is
400-
// "rename"); changes made within files are handled by corresponding fileWatchers (when
401-
// event name is "change")
402-
if (eventName === "rename") {
403-
// When deleting a file, the passed baseFileName is null
404-
callback(!relativeFileName ? relativeFileName : normalizePath(combinePaths(directoryName, relativeFileName)));
405-
}
406-
}, recursive);
407-
},
408416
resolvePath: path => _path.resolve(path),
409417
fileExists,
410418
directoryExists,
@@ -474,6 +482,20 @@ namespace ts {
474482
process.stdout.write("\x1Bc");
475483
}
476484
};
485+
nodeSystem.watchFile = getWatchFile();
486+
nodeSystem.watchDirectory = (directoryName, callback, recursive) => {
487+
// Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
488+
// (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
489+
return fsWatchDirectory(directoryName, (eventName, relativeFileName) => {
490+
// In watchDirectory we only care about adding and removing files (when event name is
491+
// "rename"); changes made within files are handled by corresponding fileWatchers (when
492+
// event name is "change")
493+
if (eventName === "rename") {
494+
// When deleting a file, the passed baseFileName is null
495+
callback(!relativeFileName ? relativeFileName : normalizePath(combinePaths(directoryName, relativeFileName)));
496+
}
497+
}, recursive);
498+
};
477499
return nodeSystem;
478500

479501
function isFileSystemCaseSensitive(): boolean {
@@ -493,6 +515,20 @@ namespace ts {
493515
});
494516
}
495517

518+
function getWatchFile(): HostWatchFile {
519+
switch (tscWatchOption) {
520+
case "PriorityPollingInterval":
521+
// Use polling interval based on priority when create watch using host.watchFile
522+
return fsWatchFile;
523+
case "DynamicPriorityPolling":
524+
return createDynamicPriorityPollingWatchFile(nodeSystem);
525+
}
526+
return useNonPollingWatchers ?
527+
createNonPollingWatchFile() :
528+
// Default to do not use polling interval as it is before this experiment branch
529+
(fileName, callback) => fsWatchFile(fileName, callback);
530+
}
531+
496532
function createNonPollingWatchFile() {
497533
// One file can have multiple watchers
498534
const fileWatcherCallbacks = createMultiMap<FileWatcherCallback>();

src/compiler/watch.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,11 @@ namespace ts {
257257
const writeLog: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? s => { system.write(s); system.write(system.newLine); } : noop;
258258
watchingHost = watchingHost || createWatchingSystemHost(compilerOptions.pretty);
259259
const { system, parseConfigFile, reportDiagnostic, reportWatchDiagnostic, beforeCompile, afterCompile } = watchingHost;
260-
const { watchFile, watchFilePath, watchDirectory: watchDirectoryWorker } = getWatchFactory(system, watchLogLevel, writeLog);
260+
const { watchFile, watchFilePath, watchDirectory: watchDirectoryWorker } = getWatchFactory(watchLogLevel, writeLog);
261261

262262
const directoryStructureHost = configFileName ? createCachedDirectoryStructureHost(system) : system;
263263
if (configFileName) {
264-
watchFile(system, configFileName, scheduleProgramReload, WatchPriority.Low);
264+
watchFile(system, configFileName, scheduleProgramReload, PollingInterval.High);
265265
}
266266

267267
const getCurrentDirectory = memoize(() => directoryStructureHost.getCurrentDirectory());
@@ -414,7 +414,7 @@ namespace ts {
414414
hostSourceFile.sourceFile = sourceFile;
415415
sourceFile.version = hostSourceFile.version.toString();
416416
if (!hostSourceFile.fileWatcher) {
417-
hostSourceFile.fileWatcher = watchFilePath(system, fileName, onSourceFileChange, WatchPriority.High, path);
417+
hostSourceFile.fileWatcher = watchFilePath(system, fileName, onSourceFileChange, PollingInterval.Low, path);
418418
}
419419
}
420420
else {
@@ -427,7 +427,7 @@ namespace ts {
427427
let fileWatcher: FileWatcher;
428428
if (sourceFile) {
429429
sourceFile.version = "0";
430-
fileWatcher = watchFilePath(system, fileName, onSourceFileChange, WatchPriority.High, path);
430+
fileWatcher = watchFilePath(system, fileName, onSourceFileChange, PollingInterval.Low, path);
431431
sourceFilesCache.set(path, { sourceFile, version: 0, fileWatcher });
432432
}
433433
else {
@@ -601,7 +601,7 @@ namespace ts {
601601
}
602602

603603
function watchMissingFilePath(missingFilePath: Path) {
604-
return watchFilePath(system, missingFilePath, onMissingFileChange, WatchPriority.Medium, missingFilePath);
604+
return watchFilePath(system, missingFilePath, onMissingFileChange, PollingInterval.Medium, missingFilePath);
605605
}
606606

607607
function onMissingFileChange(fileName: string, eventKind: FileWatcherEventKind, missingFilePath: Path) {

0 commit comments

Comments
 (0)