Skip to content

Commit c92430c

Browse files
author
Benjamin Pasero
committed
web - prevent unload while writing state to storage service
1 parent aef1a6c commit c92430c

5 files changed

Lines changed: 42 additions & 7 deletions

File tree

src/vs/platform/storage/browser/storageService.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,18 @@ export class BrowserStorageService extends Disposable implements IStorageService
2828
private globalStorage: IStorage;
2929
private workspaceStorage: IStorage;
3030

31+
private globalStorageDatabase: FileStorageDatabase;
32+
private workspaceStorageDatabase: FileStorageDatabase;
33+
3134
private globalStorageFile: URI;
3235
private workspaceStorageFile: URI;
3336

3437
private initializePromise: Promise<void>;
3538

39+
get hasPendingUpdate(): boolean {
40+
return this.globalStorageDatabase.hasPendingUpdate || this.workspaceStorageDatabase.hasPendingUpdate;
41+
}
42+
3643
constructor(
3744
@IEnvironmentService private readonly environmentService: IEnvironmentService,
3845
@IFileService private readonly fileService: IFileService
@@ -76,12 +83,14 @@ export class BrowserStorageService extends Disposable implements IStorageService
7683

7784
// Workspace Storage
7885
this.workspaceStorageFile = joinPath(stateRoot, `${payload.id}.json`);
79-
this.workspaceStorage = new Storage(this._register(new FileStorageDatabase(this.workspaceStorageFile, this.fileService)));
86+
this.workspaceStorageDatabase = this._register(new FileStorageDatabase(this.workspaceStorageFile, this.fileService));
87+
this.workspaceStorage = new Storage(this.workspaceStorageDatabase);
8088
this._register(this.workspaceStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.WORKSPACE })));
8189

8290
// Global Storage
8391
this.globalStorageFile = joinPath(stateRoot, 'global.json');
84-
this.globalStorage = new Storage(this._register(new FileStorageDatabase(this.globalStorageFile, this.fileService)));
92+
this.globalStorageDatabase = this._register(new FileStorageDatabase(this.globalStorageFile, this.fileService));
93+
this.globalStorage = new Storage(this.globalStorageDatabase);
8594
this._register(this.globalStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.GLOBAL })));
8695

8796
// Init both
@@ -134,5 +143,9 @@ export class BrowserStorageService extends Disposable implements IStorageService
134143

135144
// Signal as event so that clients can still store data
136145
this._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN });
146+
147+
// Close DBs
148+
this.globalStorage.close();
149+
this.workspaceStorage.close();
137150
}
138151
}

src/vs/platform/storage/common/storage.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ export class FileStorageDatabase extends Disposable implements IStorageDatabase
220220

221221
private pendingUpdate: Promise<void> = Promise.resolve();
222222

223+
private _hasPendingUpdate = false;
224+
get hasPendingUpdate(): boolean {
225+
return this._hasPendingUpdate;
226+
}
227+
223228
constructor(
224229
private readonly file: URI,
225230
private readonly fileService: IFileService
@@ -260,7 +265,13 @@ export class FileStorageDatabase extends Disposable implements IStorageDatabase
260265

261266
await this.pendingUpdate;
262267

263-
this.pendingUpdate = this.fileService.writeFile(this.file, VSBuffer.fromString(JSON.stringify(mapToSerializable(items)))).then();
268+
this._hasPendingUpdate = true;
269+
270+
this.pendingUpdate = this.fileService.writeFile(this.file, VSBuffer.fromString(JSON.stringify(mapToSerializable(items))))
271+
.then(() => undefined)
272+
.finally(() => {
273+
this._hasPendingUpdate = false;
274+
});
264275

265276
return this.pendingUpdate;
266277
}
@@ -312,4 +323,4 @@ export async function logStorage(global: Map<string, string>, workspace: Map<str
312323
console.groupEnd();
313324

314325
console.log(workspaceItemsParsed);
315-
}
326+
}

src/vs/workbench/browser/web.main.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,17 @@ class CodeRendererMain extends Disposable {
7373
this._register(addDisposableListener(window, EventType.RESIZE, () => workbench.layout()));
7474

7575
// Workbench Lifecycle
76-
this._register(workbench.onShutdown(() => this.dispose()));
76+
this._register(workbench.onBeforeShutdown(event => {
77+
if (services.storageService.hasPendingUpdate) {
78+
console.warn('Unload prevented: pending storage update');
79+
event.veto(true); // prevent data loss from pending storage update
80+
}
81+
}));
7782
this._register(workbench.onWillShutdown(() => {
7883
services.storageService.close();
7984
this.saveBaseTheme();
8085
}));
86+
this._register(workbench.onShutdown(() => this.dispose()));
8187

8288
// Startup
8389
workbench.startup();

src/vs/workbench/browser/workbench.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
2525
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
2626
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
2727
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
28-
import { LifecyclePhase, ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle';
28+
import { LifecyclePhase, ILifecycleService, WillShutdownEvent, BeforeShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle';
2929
import { INotificationService } from 'vs/platform/notification/common/notification';
3030
import { NotificationService } from 'vs/workbench/services/notification/common/notificationService';
3131
import { NotificationsCenter } from 'vs/workbench/browser/parts/notifications/notificationsCenter';
@@ -47,6 +47,9 @@ import { Layout } from 'vs/workbench/browser/layout';
4747

4848
export class Workbench extends Layout {
4949

50+
private readonly _onBeforeShutdown = this._register(new Emitter<BeforeShutdownEvent>());
51+
readonly onBeforeShutdown: Event<BeforeShutdownEvent> = this._onBeforeShutdown.event;
52+
5053
private readonly _onShutdown = this._register(new Emitter<void>());
5154
readonly onShutdown: Event<void> = this._onShutdown.event;
5255

@@ -225,6 +228,7 @@ export class Workbench extends Layout {
225228
): void {
226229

227230
// Lifecycle
231+
this._register(lifecycleService.onBeforeShutdown(event => this._onBeforeShutdown.fire(event)));
228232
this._register(lifecycleService.onWillShutdown(event => this._onWillShutdown.fire(event)));
229233
this._register(lifecycleService.onShutdown(() => {
230234
this._onShutdown.fire();

src/vs/workbench/services/textfile/browser/textFileService.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export class BrowserTextFileService extends TextFileService {
4646
}
4747

4848
if (!hasBackup) {
49+
console.warn('Unload prevented: pending backups');
4950
return true; // dirty without backup: veto
5051
}
5152
}
@@ -54,4 +55,4 @@ export class BrowserTextFileService extends TextFileService {
5455
}
5556
}
5657

57-
registerSingleton(ITextFileService, BrowserTextFileService);
58+
registerSingleton(ITextFileService, BrowserTextFileService);

0 commit comments

Comments
 (0)