@@ -102,7 +102,10 @@ export abstract class WorkingCopyBackupTracker extends Disposable {
102
102
private readonly mapWorkingCopyToContentVersion = new Map < IWorkingCopy , number > ( ) ;
103
103
104
104
// A map of scheduled pending backup operations for working copies
105
- protected readonly pendingBackupOperations = new Map < IWorkingCopy , IDisposable > ( ) ;
105
+ // Given https://github.com/microsoft/vscode/issues/158038, we explicitly
106
+ // do not store `IWorkingCopy` but the identifier in the map, since it
107
+ // looks like GC is not runnin for the working copy otherwise.
108
+ protected readonly pendingBackupOperations = new Map < IWorkingCopyIdentifier , IDisposable > ( ) ;
106
109
107
110
private suspended = false ;
108
111
@@ -174,6 +177,7 @@ export abstract class WorkingCopyBackupTracker extends Disposable {
174
177
this . logService . trace ( `[backup tracker] scheduling backup` , workingCopy . resource . toString ( ) , workingCopy . typeId ) ;
175
178
176
179
// Schedule new backup
180
+ const workingCopyIdentifier = { resource : workingCopy . resource , typeId : workingCopy . typeId } ;
177
181
const cts = new CancellationTokenSource ( ) ;
178
182
const handle = setTimeout ( async ( ) => {
179
183
if ( cts . token . isCancellationRequested ) {
@@ -203,12 +207,12 @@ export abstract class WorkingCopyBackupTracker extends Disposable {
203
207
// Clear disposable unless we got canceled which would
204
208
// indicate another operation has started meanwhile
205
209
if ( ! cts . token . isCancellationRequested ) {
206
- this . pendingBackupOperations . delete ( workingCopy ) ;
210
+ this . pendingBackupOperations . delete ( workingCopyIdentifier ) ;
207
211
}
208
212
} , this . getBackupScheduleDelay ( workingCopy ) ) ;
209
213
210
214
// Keep in map for disposal as needed
211
- this . pendingBackupOperations . set ( workingCopy , toDisposable ( ( ) => {
215
+ this . pendingBackupOperations . set ( workingCopyIdentifier , toDisposable ( ( ) => {
212
216
this . logService . trace ( `[backup tracker] clearing pending backup creation` , workingCopy . resource . toString ( ) , workingCopy . typeId ) ;
213
217
214
218
cts . dispose ( true ) ;
@@ -235,35 +239,54 @@ export abstract class WorkingCopyBackupTracker extends Disposable {
235
239
this . cancelBackupOperation ( workingCopy ) ;
236
240
237
241
// Schedule backup discard asap
242
+ const workingCopyIdentifier = { resource : workingCopy . resource , typeId : workingCopy . typeId } ;
238
243
const cts = new CancellationTokenSource ( ) ;
239
- ( async ( ) => {
240
- this . logService . trace ( `[backup tracker] discarding backup` , workingCopy . resource . toString ( ) , workingCopy . typeId ) ;
241
-
242
- // Discard backup
243
- try {
244
- await this . workingCopyBackupService . discardBackup ( workingCopy , cts . token ) ;
245
- } catch ( error ) {
246
- this . logService . error ( error ) ;
247
- }
248
-
249
- // Clear disposable unless we got canceled which would
250
- // indicate another operation has started meanwhile
251
- if ( ! cts . token . isCancellationRequested ) {
252
- this . pendingBackupOperations . delete ( workingCopy ) ;
253
- }
254
- } ) ( ) ;
244
+ this . doDiscardBackup ( workingCopyIdentifier , cts ) ;
255
245
256
246
// Keep in map for disposal as needed
257
- this . pendingBackupOperations . set ( workingCopy , toDisposable ( ( ) => {
247
+ this . pendingBackupOperations . set ( workingCopyIdentifier , toDisposable ( ( ) => {
258
248
this . logService . trace ( `[backup tracker] clearing pending backup discard` , workingCopy . resource . toString ( ) , workingCopy . typeId ) ;
259
249
260
250
cts . dispose ( true ) ;
261
251
} ) ) ;
262
252
}
263
253
254
+ private async doDiscardBackup ( workingCopyIdentifier : IWorkingCopyIdentifier , cts : CancellationTokenSource ) {
255
+ this . logService . trace ( `[backup tracker] discarding backup` , workingCopyIdentifier . resource . toString ( ) , workingCopyIdentifier . typeId ) ;
256
+
257
+ // Discard backup
258
+ try {
259
+ await this . workingCopyBackupService . discardBackup ( workingCopyIdentifier , cts . token ) ;
260
+ } catch ( error ) {
261
+ this . logService . error ( error ) ;
262
+ }
263
+
264
+ // Clear disposable unless we got canceled which would
265
+ // indicate another operation has started meanwhile
266
+ if ( ! cts . token . isCancellationRequested ) {
267
+ this . pendingBackupOperations . delete ( workingCopyIdentifier ) ;
268
+ }
269
+ }
270
+
264
271
private cancelBackupOperation ( workingCopy : IWorkingCopy ) : void {
265
- dispose ( this . pendingBackupOperations . get ( workingCopy ) ) ;
266
- this . pendingBackupOperations . delete ( workingCopy ) ;
272
+
273
+ // Given a working copy we want to find the matching
274
+ // identifier in our pending operations map because
275
+ // we cannot use the working copy directly, as the
276
+ // identifier might have different object identity.
277
+
278
+ let workingCopyIdentifier : IWorkingCopyIdentifier | undefined = undefined ;
279
+ for ( const [ identifier ] of this . pendingBackupOperations ) {
280
+ if ( identifier . resource . toString ( ) === workingCopy . resource . toString ( ) && identifier . typeId === workingCopy . typeId ) {
281
+ workingCopyIdentifier = identifier ;
282
+ break ;
283
+ }
284
+ }
285
+
286
+ if ( workingCopyIdentifier ) {
287
+ dispose ( this . pendingBackupOperations . get ( workingCopyIdentifier ) ) ;
288
+ this . pendingBackupOperations . delete ( workingCopyIdentifier ) ;
289
+ }
267
290
}
268
291
269
292
protected cancelBackupOperations ( ) : void {
0 commit comments