Skip to content

Commit

Permalink
fix memory leak in NsfwWatcher (#12144)
Browse files Browse the repository at this point in the history
Don't repeatedly call `Promise.race()`.

The code was calling `Promise.race()` repeatedly while waiting for a file
to come into existence. This added a `PromiseReaction` object each time,
which never was collected.

Contributed on behalf of STMicroelectronics

Signed-off-by: Thomas Mäder <[email protected]>
Co-authored-by: Paul Marechal <[email protected]>
  • Loading branch information
tsmaeder and paul-marechal authored Feb 7, 2023
1 parent 7f33d28 commit 6959b6a
Showing 1 changed file with 23 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
FileChangeType, FileSystemWatcherService, FileSystemWatcherServiceClient, WatchOptions
} from '../../common/filesystem-watcher-protocol';
import { FileChangeCollection } from '../file-change-collection';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { Deferred, timeout } from '@theia/core/lib/common/promise-util';

export interface NsfwWatcherOptions {
ignored: IMinimatch[]
Expand Down Expand Up @@ -196,22 +196,34 @@ export class NsfwWatcher {
return this.refsPerClient.size > 0;
}

/**
* @throws with {@link WatcherDisposal} if this instance is disposed.
*/
protected assertNotDisposed(): void {
if (this.disposed) {
throw WatcherDisposal;
}
}

/**
* When starting a watcher, we'll first check and wait for the path to exists
* before running an NSFW watcher.
*/
protected async start(): Promise<void> {
while (await this.orCancel(fsp.stat(this.fsPath).then(() => false, () => true))) {
await this.orCancel(new Promise(resolve => setTimeout(resolve, 500)));
while (await fsp.stat(this.fsPath).then(() => false, () => true)) {
await timeout(500);
this.assertNotDisposed();
}
this.assertNotDisposed();
const watcher = await this.createNsfw();
this.assertNotDisposed();
await watcher.start();
this.debug('STARTED', `disposed=${this.disposed}`);
// The watcher could be disposed while it was starting, make sure to check for this:
if (this.disposed) {
await this.stopNsfw(watcher);
throw WatcherDisposal;
}
const watcher = await this.orCancel(this.createNsfw());
await this.orCancel(watcher.start().then(() => {
this.debug('STARTED', `disposed=${this.disposed}`);
// The watcher could be disposed while it was starting, make sure to check for this:
if (this.disposed) {
this.stopNsfw(watcher);
}
}));
this.nsfw = watcher;
}

Expand Down Expand Up @@ -299,13 +311,6 @@ export class NsfwWatcher {
});
}

/**
* Wrap a promise to reject as soon as this handle gets disposed.
*/
protected async orCancel<T>(promise: Promise<T>): Promise<T> {
return Promise.race<T>([this.deferredDisposalDeferred.promise, promise]);
}

/**
* When references hit zero, we'll schedule disposal for a bit later.
*
Expand Down

0 comments on commit 6959b6a

Please sign in to comment.