From 64555a132ed4ba3c3dea353590d78c21b40e8135 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 22 May 2024 16:55:04 -0700 Subject: [PATCH 1/2] Prevent using disposed of timers If a timer has been disposed of, warn and don't schedule the callback. This is needed as otherwise the timer very likely ends up being leaked --- src/vs/base/common/async.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 02e911ebd32ef..53bfc4d95b748 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -880,6 +880,7 @@ export class ResourceQueue implements IDisposable { export class TimeoutTimer implements IDisposable { private _token: any; + private isDisposed = false; constructor(); constructor(runner: () => void, timeout: number); @@ -893,6 +894,7 @@ export class TimeoutTimer implements IDisposable { dispose(): void { this.cancel(); + this.isDisposed = true; } cancel(): void { @@ -903,6 +905,11 @@ export class TimeoutTimer implements IDisposable { } cancelAndSet(runner: () => void, timeout: number): void { + if (this.isDisposed) { + console.warn(`Calling 'cancelAndSet' on a disposed TimeoutTimer. Callback will not be scheduled.`); + return; + } + this.cancel(); this._token = setTimeout(() => { this._token = -1; @@ -911,6 +918,11 @@ export class TimeoutTimer implements IDisposable { } setIfNotSet(runner: () => void, timeout: number): void { + if (this.isDisposed) { + console.warn(`Calling 'setIfNotSet' on a disposed TimeoutTimer. Callback will not be scheduled.`); + return; + } + if (this._token !== -1) { // timer is already set return; @@ -925,6 +937,7 @@ export class TimeoutTimer implements IDisposable { export class IntervalTimer implements IDisposable { private disposable: IDisposable | undefined = undefined; + private isDisposed = false; cancel(): void { this.disposable?.dispose(); @@ -932,6 +945,11 @@ export class IntervalTimer implements IDisposable { } cancelAndSet(runner: () => void, interval: number, context = globalThis): void { + if (this.isDisposed) { + console.warn(`Trying to schedule on a disposed IntervalTimer. Callback will not be scheduled.`); + return; + } + this.cancel(); const handle = context.setInterval(() => { runner(); @@ -945,6 +963,7 @@ export class IntervalTimer implements IDisposable { dispose(): void { this.cancel(); + this.isDisposed = true; } } From fa922eb29a320c89a64113b22a0bead6a87bf34d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 22 May 2024 17:04:01 -0700 Subject: [PATCH 2/2] Throw errors instead --- src/vs/base/common/async.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 53bfc4d95b748..bf6342078da35 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -880,7 +880,7 @@ export class ResourceQueue implements IDisposable { export class TimeoutTimer implements IDisposable { private _token: any; - private isDisposed = false; + private _isDisposed = false; constructor(); constructor(runner: () => void, timeout: number); @@ -894,7 +894,7 @@ export class TimeoutTimer implements IDisposable { dispose(): void { this.cancel(); - this.isDisposed = true; + this._isDisposed = true; } cancel(): void { @@ -905,9 +905,8 @@ export class TimeoutTimer implements IDisposable { } cancelAndSet(runner: () => void, timeout: number): void { - if (this.isDisposed) { - console.warn(`Calling 'cancelAndSet' on a disposed TimeoutTimer. Callback will not be scheduled.`); - return; + if (this._isDisposed) { + throw new BugIndicatingError(`Calling 'cancelAndSet' on a disposed TimeoutTimer`); } this.cancel(); @@ -918,9 +917,8 @@ export class TimeoutTimer implements IDisposable { } setIfNotSet(runner: () => void, timeout: number): void { - if (this.isDisposed) { - console.warn(`Calling 'setIfNotSet' on a disposed TimeoutTimer. Callback will not be scheduled.`); - return; + if (this._isDisposed) { + throw new BugIndicatingError(`Calling 'setIfNotSet' on a disposed TimeoutTimer`); } if (this._token !== -1) { @@ -946,8 +944,7 @@ export class IntervalTimer implements IDisposable { cancelAndSet(runner: () => void, interval: number, context = globalThis): void { if (this.isDisposed) { - console.warn(`Trying to schedule on a disposed IntervalTimer. Callback will not be scheduled.`); - return; + throw new BugIndicatingError(`Calling 'cancelAndSet' on a disposed IntervalTimer`); } this.cancel();