Skip to content

Commit 213c601

Browse files
bpaseromustard-mh
authored andcommitted
High CPU on large workspace with many TypeScript watched folders (fix microsoft#245366) (microsoft#245670)
* High CPU on large workspace with many TypeScript watched folders (fix microsoft#245366) * reduce throttle delay
1 parent 6d0d2c5 commit 213c601

File tree

3 files changed

+37
-5
lines changed

3 files changed

+37
-5
lines changed

src/vs/base/common/async.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,7 @@ export class ThrottledWorker<T> extends Disposable {
13031303
override dispose(): void {
13041304
super.dispose();
13051305

1306+
this.pendingWork.length = 0;
13061307
this.disposed = true;
13071308
}
13081309
}

src/vs/platform/files/node/watcher/nodejs/nodejsWatcher.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { BaseWatcher } from '../baseWatcher.js';
99
import { isLinux } from '../../../../../base/common/platform.js';
1010
import { INonRecursiveWatchRequest, INonRecursiveWatcher, IRecursiveWatcherWithSubscribe } from '../../../common/watcher.js';
1111
import { NodeJSFileWatcherLibrary } from './nodejsWatcherLib.js';
12+
import { ThrottledWorker } from '../../../../../base/common/async.js';
13+
import { MutableDisposable } from '../../../../../base/common/lifecycle.js';
1214

1315
export interface INodeJSWatcherInstance {
1416

@@ -30,6 +32,8 @@ export class NodeJSWatcher extends BaseWatcher implements INonRecursiveWatcher {
3032
private readonly _watchers = new Map<string /* path */ | number /* correlation ID */, INodeJSWatcherInstance>();
3133
get watchers() { return this._watchers.values(); }
3234

35+
private readonly worker = this._register(new MutableDisposable<ThrottledWorker<INonRecursiveWatchRequest>>());
36+
3337
constructor(protected readonly recursiveWatcher: IRecursiveWatcherWithSubscribe | undefined) {
3438
super();
3539
}
@@ -61,15 +65,36 @@ export class NodeJSWatcher extends BaseWatcher implements INonRecursiveWatcher {
6165
this.trace(`Request to stop watching: ${Array.from(watchersToStop).map(watcher => this.requestToString(watcher.request)).join(',')}`);
6266
}
6367

68+
// Stop the worker
69+
this.worker.clear();
70+
6471
// Stop watching as instructed
6572
for (const watcher of watchersToStop) {
6673
this.stopWatching(watcher);
6774
}
6875

6976
// Start watching as instructed
70-
for (const request of requestsToStart) {
71-
this.startWatching(request);
72-
}
77+
this.createWatchWorker().work(requestsToStart);
78+
}
79+
80+
private createWatchWorker(): ThrottledWorker<INonRecursiveWatchRequest> {
81+
82+
// We see very large amount of non-recursive file watcher requests
83+
// in large workspaces. To prevent the overhead of starting thousands
84+
// of watchers at once, we use a throttled worker to distribute this
85+
// work over time.
86+
87+
this.worker.value = new ThrottledWorker<INonRecursiveWatchRequest>({
88+
maxWorkChunkSize: 100, // only start 100 watchers at once before...
89+
throttleDelay: 100, // ...resting for 100ms until we start watchers again...
90+
maxBufferedWork: Number.MAX_VALUE // ...and never refuse any work.
91+
}, requests => {
92+
for (const request of requests) {
93+
this.startWatching(request);
94+
}
95+
});
96+
97+
return this.worker.value;
7398
}
7499

75100
private requestToWatcherKey(request: INonRecursiveWatchRequest): string | number {

src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,10 @@ export class NodeJSFileWatcherLibrary extends Disposable {
183183
private async doWatchWithNodeJS(isDirectory: boolean, disposables: DisposableStore): Promise<void> {
184184
const realPath = await this.realPath.value;
185185

186+
if (this.cts.token.isCancellationRequested) {
187+
return;
188+
}
189+
186190
// macOS: watching samba shares can crash VSCode so we do
187191
// a simple check for the file path pointing to /Volumes
188192
// (https://github.com/microsoft/vscode/issues/106879)
@@ -422,10 +426,12 @@ export class NodeJSFileWatcherLibrary extends Disposable {
422426
}
423427
});
424428
} catch (error) {
425-
if (!cts.token.isCancellationRequested) {
426-
this.error(`Failed to watch ${realPath} for changes using fs.watch() (${error.toString()})`);
429+
if (cts.token.isCancellationRequested) {
430+
return;
427431
}
428432

433+
this.error(`Failed to watch ${realPath} for changes using fs.watch() (${error.toString()})`);
434+
429435
this.notifyWatchFailed();
430436
}
431437
}

0 commit comments

Comments
 (0)