Skip to content

Commit d4e5ecb

Browse files
fix(debug): clean up variable refresh disposal
1 parent 7221942 commit d4e5ecb

2 files changed

Lines changed: 80 additions & 5 deletions

File tree

packages/debug/__tests__/browser/view/variables/debug-variables-tree.model.service.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,42 @@ describe('Debug Variables Tree Model', () => {
292292
}
293293
});
294294

295+
it('cancels pending queued refresh work when disposed', async () => {
296+
jest.useFakeTimers();
297+
try {
298+
const watcher = {
299+
callback: jest.fn(async () => {}),
300+
};
301+
const root = {
302+
path: '/disposeRoot',
303+
children: [],
304+
watchEvents: new Map([['/disposeRoot', watcher]]),
305+
};
306+
const refreshed = jest.fn();
307+
308+
(debugVariablesModelService as any)._activeTreeModel = {
309+
root,
310+
};
311+
debugVariablesModelService.onDidRefreshed(refreshed);
312+
313+
const refreshPromise = debugVariablesModelService.refresh(root as any);
314+
await Promise.resolve();
315+
316+
debugVariablesModelService.dispose();
317+
318+
expect(debugVariablesModelService.flushEventQueuePromise).toBeFalsy();
319+
320+
await jest.advanceTimersByTimeAsync(100);
321+
await refreshPromise;
322+
323+
expect(watcher.callback).not.toHaveBeenCalled();
324+
expect(refreshed).not.toHaveBeenCalled();
325+
} finally {
326+
(debugVariablesModelService as any)._disposed = false;
327+
jest.useRealTimers();
328+
}
329+
});
330+
295331
it('queues another flush when a refresh arrives during an active flush', async () => {
296332
jest.useFakeTimers();
297333
try {

packages/debug/src/browser/view/variables/debug-variables-tree.model.service.ts

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ export class DebugVariablesModelService {
141141
private _changeEventDispatchQueue: string[] = [];
142142
private _pendingFlushCallbacks: Array<() => Promise<void> | void> = [];
143143
private _isFlushingEventQueue = false;
144+
private _disposed = false;
144145

145146
// 装饰器
146147
private selectedDecoration: Decoration = new Decoration(styles.mod_selected); // 选中态
@@ -214,12 +215,25 @@ export class DebugVariablesModelService {
214215
}
215216

216217
dispose() {
218+
this._disposed = true;
219+
this.disposeEventQueue();
217220
this.disposeTreeListeners();
218221
if (!this.currentSessionDisposableCollection.disposed) {
219222
this.currentSessionDisposableCollection.dispose();
220223
}
221224
}
222225

226+
private disposeEventQueue() {
227+
clearTimeout(this._eventFlushTimeout);
228+
this._changeEventDispatchQueue = [];
229+
this._pendingFlushCallbacks = [];
230+
this._isFlushingEventQueue = false;
231+
232+
const flushEventQueueDeferred = this.flushEventQueueDeferred;
233+
this.flushEventQueueDeferred = null;
234+
flushEventQueueDeferred?.resolve();
235+
}
236+
223237
private disposeTreeListeners() {
224238
if (!this.disposableCollection.disposed) {
225239
this.disposableCollection.dispose();
@@ -229,11 +243,17 @@ export class DebugVariablesModelService {
229243

230244
listenViewModelChange() {
231245
this.viewModel.onDidChange(async () => {
246+
if (this._disposed) {
247+
return;
248+
}
232249
this.listenCurrentSessionVariableChange();
233250
if (!this.flushDispatchChangeDelayer.isTriggered()) {
234251
this.flushDispatchChangeDelayer.cancel();
235252
}
236253
this.flushDispatchChangeDelayer.trigger(async () => {
254+
if (this._disposed) {
255+
return;
256+
}
237257
if (this.viewModel && this.viewModel.currentSession && !this.viewModel.currentSession.terminated) {
238258
const currentTreeModel = await this.initTreeModel(this.viewModel.currentSession);
239259
this._activeTreeModel = currentTreeModel;
@@ -345,6 +365,9 @@ export class DebugVariablesModelService {
345365
* 刷新指定节点下的所有子节点
346366
*/
347367
async refresh(node?: ExpressionContainer) {
368+
if (this._disposed) {
369+
return;
370+
}
348371
if (!this.isActiveTreeModelForCurrentSession()) {
349372
return;
350373
}
@@ -376,6 +399,9 @@ export class DebugVariablesModelService {
376399
}
377400

378401
private queueChangeEvent(path: string, callback: () => Promise<void> | void) {
402+
if (this._disposed) {
403+
return Promise.resolve();
404+
}
379405
if (this._changeEventDispatchQueue.indexOf(path) === -1) {
380406
this._changeEventDispatchQueue.push(path);
381407
}
@@ -386,23 +412,36 @@ export class DebugVariablesModelService {
386412
clearTimeout(this._eventFlushTimeout);
387413
this._eventFlushTimeout = setTimeout(async () => {
388414
try {
415+
if (this._disposed) {
416+
return;
417+
}
389418
this._isFlushingEventQueue = true;
390-
while (this._changeEventDispatchQueue.length > 0 || this._pendingFlushCallbacks.length > 0) {
419+
while (
420+
!this._disposed &&
421+
(this._changeEventDispatchQueue.length > 0 || this._pendingFlushCallbacks.length > 0)
422+
) {
391423
const pendingFlushCallbacks = [...this._pendingFlushCallbacks];
392424
this._pendingFlushCallbacks = [];
393425

394426
await this.flushQueuedEventBatch();
427+
if (this._disposed) {
428+
return;
429+
}
395430
await pSeries(
396431
pendingFlushCallbacks.map((pendingFlushCallback) => async () => {
397-
await pendingFlushCallback();
432+
if (!this._disposed) {
433+
await pendingFlushCallback();
434+
}
398435
return null;
399436
}),
400437
);
401438
}
402439

403440
this.flushEventQueueDeferred?.resolve();
404441
} catch (error) {
405-
this.flushEventQueueDeferred?.reject(error);
442+
if (!this._disposed) {
443+
this.flushEventQueueDeferred?.reject(error);
444+
}
406445
} finally {
407446
this._isFlushingEventQueue = false;
408447
this.flushEventQueueDeferred = null;
@@ -420,14 +459,14 @@ export class DebugVariablesModelService {
420459
}
421460

422461
public flushEventQueue = () => {
423-
if (this._isFlushingEventQueue) {
462+
if (this._disposed || this._isFlushingEventQueue) {
424463
return;
425464
}
426465
return this.flushQueuedEventBatch();
427466
};
428467

429468
private flushQueuedEventBatch = () => {
430-
if (!this._changeEventDispatchQueue || this._changeEventDispatchQueue.length === 0) {
469+
if (this._disposed || !this._changeEventDispatchQueue || this._changeEventDispatchQueue.length === 0) {
431470
return;
432471
}
433472

0 commit comments

Comments
 (0)