Path: blob/main/src/vs/workbench/services/lifecycle/common/lifecycleService.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Emitter } from '../../../../base/common/event.js';6import { Barrier } from '../../../../base/common/async.js';7import { Disposable } from '../../../../base/common/lifecycle.js';8import { ILifecycleService, WillShutdownEvent, StartupKind, LifecyclePhase, LifecyclePhaseToString, ShutdownReason, BeforeShutdownErrorEvent, InternalBeforeShutdownEvent } from './lifecycle.js';9import { ILogService } from '../../../../platform/log/common/log.js';10import { mark } from '../../../../base/common/performance.js';11import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from '../../../../platform/storage/common/storage.js';1213export abstract class AbstractLifecycleService extends Disposable implements ILifecycleService {1415private static readonly LAST_SHUTDOWN_REASON_KEY = 'lifecyle.lastShutdownReason';1617declare readonly _serviceBrand: undefined;1819protected readonly _onBeforeShutdown = this._register(new Emitter<InternalBeforeShutdownEvent>());20readonly onBeforeShutdown = this._onBeforeShutdown.event;2122protected readonly _onWillShutdown = this._register(new Emitter<WillShutdownEvent>());23readonly onWillShutdown = this._onWillShutdown.event;2425protected readonly _onDidShutdown = this._register(new Emitter<void>());26readonly onDidShutdown = this._onDidShutdown.event;2728protected readonly _onBeforeShutdownError = this._register(new Emitter<BeforeShutdownErrorEvent>());29readonly onBeforeShutdownError = this._onBeforeShutdownError.event;3031protected readonly _onShutdownVeto = this._register(new Emitter<void>());32readonly onShutdownVeto = this._onShutdownVeto.event;3334private _startupKind: StartupKind;35get startupKind(): StartupKind { return this._startupKind; }3637private _phase = LifecyclePhase.Starting;38get phase(): LifecyclePhase { return this._phase; }3940protected _willShutdown = false;41get willShutdown(): boolean { return this._willShutdown; }4243private readonly phaseWhen = new Map<LifecyclePhase, Barrier>();4445protected shutdownReason: ShutdownReason | undefined;4647constructor(48@ILogService protected readonly logService: ILogService,49@IStorageService protected readonly storageService: IStorageService50) {51super();5253// Resolve startup kind54this._startupKind = this.resolveStartupKind();5556// Save shutdown reason to retrieve on next startup57this._register(this.storageService.onWillSaveState(e => {58if (e.reason === WillSaveStateReason.SHUTDOWN) {59this.storageService.store(AbstractLifecycleService.LAST_SHUTDOWN_REASON_KEY, this.shutdownReason, StorageScope.WORKSPACE, StorageTarget.MACHINE);60}61}));62}6364private resolveStartupKind(): StartupKind {65const startupKind = this.doResolveStartupKind() ?? StartupKind.NewWindow;66this.logService.trace(`[lifecycle] starting up (startup kind: ${startupKind})`);6768return startupKind;69}7071protected doResolveStartupKind(): StartupKind | undefined {7273// Retrieve and reset last shutdown reason74const lastShutdownReason = this.storageService.getNumber(AbstractLifecycleService.LAST_SHUTDOWN_REASON_KEY, StorageScope.WORKSPACE);75this.storageService.remove(AbstractLifecycleService.LAST_SHUTDOWN_REASON_KEY, StorageScope.WORKSPACE);7677// Convert into startup kind78let startupKind: StartupKind | undefined = undefined;79switch (lastShutdownReason) {80case ShutdownReason.RELOAD:81startupKind = StartupKind.ReloadedWindow;82break;83case ShutdownReason.LOAD:84startupKind = StartupKind.ReopenedWindow;85break;86}8788return startupKind;89}9091set phase(value: LifecyclePhase) {92if (value < this.phase) {93throw new Error('Lifecycle cannot go backwards');94}9596if (this._phase === value) {97return;98}99100this.logService.trace(`lifecycle: phase changed (value: ${value})`);101102this._phase = value;103mark(`code/LifecyclePhase/${LifecyclePhaseToString(value)}`);104105const barrier = this.phaseWhen.get(this._phase);106if (barrier) {107barrier.open();108this.phaseWhen.delete(this._phase);109}110}111112async when(phase: LifecyclePhase): Promise<void> {113if (phase <= this._phase) {114return;115}116117let barrier = this.phaseWhen.get(phase);118if (!barrier) {119barrier = new Barrier();120this.phaseWhen.set(phase, barrier);121}122123await barrier.wait();124}125126/**127* Subclasses to implement the explicit shutdown method.128*/129abstract shutdown(): Promise<void>;130}131132133