Merge pull request #15493 from Microsoft/tyriar/hot_exit/15260

Move backup unload logic to an event
This commit is contained in:
Benjamin Pasero
2016-11-17 20:37:05 +01:00
committed by GitHub
11 changed files with 151 additions and 71 deletions

View File

@@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
export enum ReadyState {
/**
* This window has not loaded any HTML yet
*/
NONE,
/**
* This window is loading HTML
*/
LOADING,
/**
* This window is navigating to another HTML
*/
NAVIGATING,
/**
* This window is done loading HTML
*/
READY
}
export interface IVSCodeWindow {
id: number;
readyState: ReadyState;
win: Electron.BrowserWindow;
send(channel: string, ...args: any[]): void;
}

View File

@@ -5,39 +5,21 @@
'use strict';
import Uri from 'vs/base/common/uri';
import { EventEmitter } from 'events';
import { ipcMain as ipc, app } from 'electron';
import { TPromise, TValueCallback } from 'vs/base/common/winjs.base';
import { ReadyState, VSCodeWindow } from 'vs/code/electron-main/window';
import { ReadyState, IVSCodeWindow } from 'vs/code/common/window';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IBackupMainService } from 'vs/platform/backup/common/backup';
import { ILogService } from 'vs/code/electron-main/log';
import { IStorageService } from 'vs/code/electron-main/storage';
import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle';
const EventTypes = {
BEFORE_CLOSE: 'before-close',
BEFORE_QUIT: 'before-quit'
};
export const ILifecycleService = createDecorator<ILifecycleService>('lifecycleService');
export interface ILifecycleService {
_serviceBrand: any;
/**
* Will be true if an update was applied. Will only be true for each update once.
*/
wasUpdated: boolean;
onBeforeQuit(clb: () => void): () => void;
ready(): void;
registerWindow(vscodeWindow: VSCodeWindow): void;
unload(vscodeWindow: VSCodeWindow): TPromise<boolean /* veto */>;
quit(fromUpdate?: boolean): TPromise<boolean /* veto */>;
}
export class LifecycleService implements ILifecycleService {
export class LifecycleService implements ILifecycleMainService {
_serviceBrand: any;
@@ -54,8 +36,7 @@ export class LifecycleService implements ILifecycleService {
constructor(
@IEnvironmentService private environmentService: IEnvironmentService,
@ILogService private logService: ILogService,
@IStorageService private storageService: IStorageService,
@IBackupMainService private backupService: IBackupMainService
@IStorageService private storageService: IStorageService
) {
this.windowToCloseRequest = Object.create(null);
this.quitRequested = false;
@@ -88,6 +69,12 @@ export class LifecycleService implements ILifecycleService {
return () => this.eventEmitter.removeListener(EventTypes.BEFORE_QUIT, clb);
}
onAfterUnload(clb: (vscodeWindow: IVSCodeWindow) => void): () => void {
this.eventEmitter.addListener(EventTypes.BEFORE_CLOSE, clb);
return () => this.eventEmitter.removeListener(EventTypes.BEFORE_CLOSE, clb);
}
public ready(): void {
this.registerListeners();
}
@@ -118,7 +105,7 @@ export class LifecycleService implements ILifecycleService {
});
}
public registerWindow(vscodeWindow: VSCodeWindow): void {
public registerWindow(vscodeWindow: IVSCodeWindow): void {
// Window Before Closing: Main -> Renderer
vscodeWindow.win.on('close', (e) => {
@@ -148,7 +135,7 @@ export class LifecycleService implements ILifecycleService {
});
}
public unload(vscodeWindow: VSCodeWindow): TPromise<boolean /* veto */> {
public unload(vscodeWindow: IVSCodeWindow): TPromise<boolean /* veto */> {
// Always allow to unload a window that is not yet ready
if (vscodeWindow.readyState !== ReadyState.READY) {
@@ -163,13 +150,7 @@ export class LifecycleService implements ILifecycleService {
const oneTimeCancelEvent = 'vscode:cancel' + oneTimeEventToken;
ipc.once(oneTimeOkEvent, () => {
// Clear out any workspace backups from workspaces.json that don't have any backups
if (vscodeWindow.openedWorkspacePath) {
const workspaceResource = Uri.file(vscodeWindow.openedWorkspacePath);
if (!this.backupService.hasWorkspaceBackup(workspaceResource)) {
this.backupService.removeWorkspaceBackupPathSync(workspaceResource);
}
}
this.eventEmitter.emit(EventTypes.BEFORE_CLOSE, vscodeWindow);
c(false); // no veto
});

View File

@@ -15,7 +15,8 @@ import { IWindowsMainService, WindowsManager } from 'vs/code/electron-main/windo
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc';
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
import { ILifecycleService, LifecycleService } from 'vs/code/electron-main/lifecycle';
import { LifecycleService } from 'vs/code/electron-main/lifecycle';
import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle';
import { VSCodeMenu } from 'vs/code/electron-main/menus';
import { IUpdateService } from 'vs/platform/update/common/update';
import { UpdateChannel } from 'vs/platform/update/common/updateIpc';
@@ -35,7 +36,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ILogService, MainLogService } from 'vs/code/electron-main/log';
import { IStorageService, StorageService } from 'vs/code/electron-main/storage';
import { IBackupMainService } from 'vs/platform/backup/common/backup';
import { BackupMainService } from 'vs/platform/backup/node/backupMainService';
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -81,7 +82,7 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo
const instantiationService = accessor.get(IInstantiationService);
const logService = accessor.get(ILogService);
const environmentService = accessor.get(IEnvironmentService);
const lifecycleService = accessor.get(ILifecycleService);
const lifecycleService = accessor.get(ILifecycleMainService);
const configurationService = accessor.get(IConfigurationService) as ConfigurationService<any>;
let windowsMainService: IWindowsMainService;
@@ -423,7 +424,7 @@ function createServices(args): IInstantiationService {
services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService, args, process.execPath));
services.set(ILogService, new SyncDescriptor(MainLogService));
services.set(ILifecycleService, new SyncDescriptor(LifecycleService));
services.set(ILifecycleMainService, new SyncDescriptor(LifecycleService));
services.set(IStorageService, new SyncDescriptor(StorageService));
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
services.set(IRequestService, new SyncDescriptor(RequestService));

View File

@@ -20,6 +20,7 @@ import { getCommonHTTPHeaders } from 'vs/platform/environment/node/http';
import { IBackupMainService } from 'vs/platform/backup/common/backup';
import { IWindowSettings } from 'vs/platform/windows/common/windows';
import Uri from 'vs/base/common/uri';
import { ReadyState, IVSCodeWindow } from 'vs/code/common/window';
export interface IWindowState {
width?: number;
@@ -51,29 +52,6 @@ export const defaultWindowState = function (mode = WindowMode.Normal): IWindowSt
};
};
export enum ReadyState {
/**
* This window has not loaded any HTML yet
*/
NONE,
/**
* This window is loading HTML
*/
LOADING,
/**
* This window is navigating to another HTML
*/
NAVIGATING,
/**
* This window is done loading HTML
*/
READY
}
export interface IPath {
// the workspace spath for a VSCode instance which can be null
@@ -116,7 +94,7 @@ export interface IWindowConfiguration extends ParsedArgs {
untitledToRestore?: IPath[];
}
export class VSCodeWindow {
export class VSCodeWindow implements IVSCodeWindow {
public static menuBarHiddenKey = 'menuBarHidden';
public static colorThemeStorageKey = 'theme';

View File

@@ -17,10 +17,11 @@ import { IBackupMainService } from 'vs/platform/backup/common/backup';
import { trim } from 'vs/base/common/strings';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { IStorageService } from 'vs/code/electron-main/storage';
import { IPath, VSCodeWindow, ReadyState, IWindowConfiguration, IWindowState as ISingleWindowState, defaultWindowState } from 'vs/code/electron-main/window';
import { IPath, VSCodeWindow, IWindowConfiguration, IWindowState as ISingleWindowState, defaultWindowState } from 'vs/code/electron-main/window';
import { ReadyState } from 'vs/code/common/window';
import { ipcMain as ipc, app, screen, BrowserWindow, dialog } from 'electron';
import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/electron-main/paths';
import { ILifecycleService } from 'vs/code/electron-main/lifecycle';
import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/code/electron-main/log';
import { getPathLabel } from 'vs/base/common/labels';
@@ -152,7 +153,7 @@ export class WindowsManager implements IWindowsMainService {
@ILogService private logService: ILogService,
@IStorageService private storageService: IStorageService,
@IEnvironmentService private environmentService: IEnvironmentService,
@ILifecycleService private lifecycleService: ILifecycleService,
@ILifecycleMainService private lifecycleService: ILifecycleMainService,
@IBackupMainService private backupService: IBackupMainService,
@IConfigurationService private configurationService: IConfigurationService,
@ITelemetryService private telemetryService: ITelemetryService

View File

@@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle';
import { IVSCodeWindow } from 'vs/code/common/window';
import { TPromise } from 'vs/base/common/winjs.base';
export class TestLifecycleService implements ILifecycleMainService {
public _serviceBrand: any;
public get wasUpdated(): boolean {
return false;
}
public onBeforeQuit(clb: () => void): () => void {
return () => { };
}
public onAfterUnload(clb: (vscodeWindow: IVSCodeWindow) => void): () => void {
return () => { };
}
public ready(): void {
}
public registerWindow(vscodeWindow: IVSCodeWindow): void {
}
public unload(vscodeWindow: IVSCodeWindow): TPromise<boolean /* veto */> {
return TPromise.as(false);
}
public quit(fromUpdate?: boolean): TPromise<boolean /* veto */> {
return TPromise.as(false);
}
}

View File

@@ -10,6 +10,8 @@ import Uri from 'vs/base/common/uri';
import { readdirSync } from 'vs/base/node/extfs';
import { IBackupWorkspacesFormat, IBackupMainService } from 'vs/platform/backup/common/backup';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle';
import { VSCodeWindow } from 'vs/code/electron-main/window';
export class BackupMainService implements IBackupMainService {
@@ -21,13 +23,27 @@ export class BackupMainService implements IBackupMainService {
private workspacesJsonContent: IBackupWorkspacesFormat;
constructor(
@IEnvironmentService environmentService: IEnvironmentService
@IEnvironmentService environmentService: IEnvironmentService,
@ILifecycleMainService lifecycleService: ILifecycleMainService
) {
this.backupHome = environmentService.backupHome;
this.workspacesJsonPath = environmentService.backupWorkspacesPath;
lifecycleService.onAfterUnload(this.onAfterUnloadWindow.bind(this));
this.loadSync();
}
private onAfterUnloadWindow(vscodeWindow: VSCodeWindow) {
if (vscodeWindow.openedWorkspacePath) {
// Clear out workspace from workspaces.json if it doesn't have any backups
const workspaceResource = Uri.file(vscodeWindow.openedWorkspacePath);
if (!this.hasWorkspaceBackup(workspaceResource)) {
this.removeWorkspaceBackupPathSync(workspaceResource);
}
}
}
public getWorkspaceBackupPaths(): string[] {
return this.workspacesJsonContent.folderWorkspaces;
}

View File

@@ -15,12 +15,13 @@ import extfs = require('vs/base/node/extfs');
import pfs = require('vs/base/node/pfs');
import Uri from 'vs/base/common/uri';
import { TestEnvironmentService } from 'vs/test/utils/servicesTestUtils';
import { BackupMainService } from 'vs/platform/backup/node/backupMainService';
import { TestLifecycleService } from 'vs/code/test/electron-main/servicesTestUtils';
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
import { IBackupWorkspacesFormat } from 'vs/platform/backup/common/backup';
class TestBackupMainService extends BackupMainService {
constructor(backupHome: string, backupWorkspacesPath: string) {
super(TestEnvironmentService);
super(TestEnvironmentService, new TestLifecycleService());
this.backupHome = backupHome;
this.workspacesJsonPath = backupWorkspacesPath;

View File

@@ -0,0 +1,27 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IVSCodeWindow } from 'vs/code/common/window';
import { TPromise } from 'vs/base/common/winjs.base';
export const ILifecycleMainService = createDecorator<ILifecycleMainService>('lifecycleMainService');
export interface ILifecycleMainService {
_serviceBrand: any;
/**
* Will be true if an update was applied. Will only be true for each update once.
*/
wasUpdated: boolean;
onBeforeQuit(clb: () => void): () => void;
onAfterUnload(clb: (vscodeWindow: IVSCodeWindow) => void): () => void;
ready(): void;
registerWindow(vscodeWindow: IVSCodeWindow): void;
unload(vscodeWindow: IVSCodeWindow): TPromise<boolean /* veto */>;
quit(fromUpdate?: boolean): TPromise<boolean /* veto */>;
}

View File

@@ -15,7 +15,7 @@ import { mkdirp } from 'vs/base/node/extfs';
import { isString } from 'vs/base/common/types';
import { Promise, TPromise } from 'vs/base/common/winjs.base';
import { download, asJson } from 'vs/base/node/request';
import { ILifecycleService } from 'vs/code/electron-main/lifecycle';
import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle';
import { IRequestService } from 'vs/platform/request/node/request';
import { IAutoUpdater } from 'vs/platform/update/common/update';
import product from 'vs/platform/product';
@@ -36,7 +36,7 @@ export class Win32AutoUpdaterImpl extends EventEmitter implements IAutoUpdater {
private updatePackagePath: string = null;
constructor(
@ILifecycleService private lifecycleService: ILifecycleService,
@ILifecycleMainService private lifecycleService: ILifecycleMainService,
@IRequestService private requestService: IRequestService
) {
super();

View File

@@ -16,7 +16,7 @@ import { fromEventEmitter } from 'vs/base/node/event';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Win32AutoUpdaterImpl } from './auto-updater.win32';
import { LinuxAutoUpdaterImpl } from './auto-updater.linux';
import { ILifecycleService } from 'vs/code/electron-main/lifecycle';
import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import product from 'vs/platform/product';
import { TPromise } from 'vs/base/common/winjs.base';
@@ -85,7 +85,7 @@ export class UpdateService implements IUpdateService {
constructor(
@IInstantiationService instantiationService: IInstantiationService,
@ILifecycleService private lifecycleService: ILifecycleService,
@ILifecycleMainService private lifecycleService: ILifecycleMainService,
@IConfigurationService private configurationService: IConfigurationService,
@ITelemetryService private telemetryService: ITelemetryService
) {