Path: blob/main/src/vs/workbench/services/extensions/common/lazyCreateExtensionHostManager.ts
5240 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 { Barrier } from '../../../../base/common/async.js';6import { Emitter, Event } from '../../../../base/common/event.js';7import { Disposable } from '../../../../base/common/lifecycle.js';8import { URI } from '../../../../base/common/uri.js';9import { ExtensionIdentifier, IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';10import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';11import { ILogService } from '../../../../platform/log/common/log.js';12import { RemoteAuthorityResolverErrorCode } from '../../../../platform/remote/common/remoteAuthorityResolver.js';13import { ExtensionHostKind } from './extensionHostKind.js';14import { ExtensionHostManager, friendlyExtHostName } from './extensionHostManager.js';15import { IExtensionHostManager } from './extensionHostManagers.js';16import { IExtensionDescriptionDelta } from './extensionHostProtocol.js';17import { IResolveAuthorityResult } from './extensionHostProxy.js';18import { ExtensionRunningLocation } from './extensionRunningLocation.js';19import { ActivationKind, ExtensionActivationReason, ExtensionHostStartup, IExtensionHost, IExtensionInspectInfo, IInternalExtensionService } from './extensions.js';20import { ResponsiveState } from './rpcProtocol.js';2122/**23* Waits until `start()` and only if it has extensions proceeds to really start.24*/25export class LazyCreateExtensionHostManager extends Disposable implements IExtensionHostManager {2627public readonly onDidExit: Event<[number, string | null]>;28private readonly _onDidChangeResponsiveState: Emitter<ResponsiveState> = this._register(new Emitter<ResponsiveState>());29public readonly onDidChangeResponsiveState: Event<ResponsiveState> = this._onDidChangeResponsiveState.event;3031private readonly _extensionHost: IExtensionHost;32private _startCalled: Barrier;33private _actual: ExtensionHostManager | null;3435public get pid(): number | null {36if (this._actual) {37return this._actual.pid;38}39return null;40}4142public get kind(): ExtensionHostKind {43return this._extensionHost.runningLocation.kind;44}4546public get startup(): ExtensionHostStartup {47return this._extensionHost.startup;48}4950public get friendyName(): string {51return friendlyExtHostName(this.kind, this.pid);52}5354constructor(55extensionHost: IExtensionHost,56private readonly _initialActivationEvents: string[],57private readonly _internalExtensionService: IInternalExtensionService,58@IInstantiationService private readonly _instantiationService: IInstantiationService,59@ILogService private readonly _logService: ILogService60) {61super();62this._extensionHost = extensionHost;63this.onDidExit = extensionHost.onExit;64this._startCalled = new Barrier();65this._actual = null;66}6768override dispose(): void {69if (!this._actual) {70this._extensionHost.dispose();71}72super.dispose();73}7475private _createActual(reason: string): ExtensionHostManager {76this._logService.info(`Creating lazy extension host (${this.friendyName}). Reason: ${reason}`);77this._actual = this._register(this._instantiationService.createInstance(ExtensionHostManager, this._extensionHost, this._initialActivationEvents, this._internalExtensionService));78this._register(this._actual.onDidChangeResponsiveState((e) => this._onDidChangeResponsiveState.fire(e)));79return this._actual;80}8182private async _getOrCreateActualAndStart(reason: string): Promise<ExtensionHostManager> {83if (this._actual) {84// already created/started85return this._actual;86}87const actual = this._createActual(reason);88await actual.ready();89return actual;90}9192public async ready(): Promise<void> {93await this._startCalled.wait();94if (this._actual) {95await this._actual.ready();96}97}9899public async disconnect(): Promise<void> {100await this._actual?.disconnect();101}102103public representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean {104return this._extensionHost.runningLocation.equals(runningLocation);105}106107public async deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {108await this._startCalled.wait();109if (this._actual) {110return this._actual.deltaExtensions(extensionsDelta);111}112if (extensionsDelta.myToAdd.length > 0) {113const actual = this._createActual(`contains ${extensionsDelta.myToAdd.length} new extension(s) (installed or enabled): ${extensionsDelta.myToAdd.map(extId => extId.value)}`);114await actual.ready();115return;116}117}118119public containsExtension(extensionId: ExtensionIdentifier): boolean {120return this._extensionHost.extensions?.containsExtension(extensionId) ?? false;121}122123public async activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {124await this._startCalled.wait();125if (this._actual) {126return this._actual.activate(extension, reason);127}128return false;129}130131public async activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {132if (activationKind === ActivationKind.Immediate) {133// this is an immediate request, so we cannot wait for start to be called134if (this._actual) {135return this._actual.activateByEvent(activationEvent, activationKind);136}137return;138}139await this._startCalled.wait();140if (this._actual) {141return this._actual.activateByEvent(activationEvent, activationKind);142}143}144145public activationEventIsDone(activationEvent: string): boolean {146if (!this._startCalled.isOpen()) {147return false;148}149if (this._actual) {150return this._actual.activationEventIsDone(activationEvent);151}152return true;153}154155public async getInspectPort(tryEnableInspector: boolean): Promise<IExtensionInspectInfo | undefined> {156await this._startCalled.wait();157return this._actual?.getInspectPort(tryEnableInspector);158}159160public async resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {161await this._startCalled.wait();162if (this._actual) {163return this._actual.resolveAuthority(remoteAuthority, resolveAttempt);164}165return {166type: 'error',167error: {168message: `Cannot resolve authority`,169code: RemoteAuthorityResolverErrorCode.Unknown,170detail: undefined171}172};173}174175public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null> {176await this._startCalled.wait();177if (this._actual) {178return this._actual.getCanonicalURI(remoteAuthority, uri);179}180throw new Error(`Cannot resolve canonical URI`);181}182183public async start(extensionRegistryVersionId: number, allExtensions: IExtensionDescription[], myExtensions: ExtensionIdentifier[]): Promise<void> {184if (myExtensions.length > 0) {185// there are actual extensions, so let's launch the extension host (auto-start)186const actual = this._createActual(`contains ${myExtensions.length} extension(s): ${myExtensions.map(extId => extId.value)}.`);187const result = actual.ready();188this._startCalled.open();189return result;190}191// there are no actual extensions running192this._startCalled.open();193}194195public async extensionTestsExecute(): Promise<number> {196await this._startCalled.wait();197const actual = await this._getOrCreateActualAndStart(`execute tests.`);198return actual.extensionTestsExecute();199}200201public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {202await this._startCalled.wait();203if (this._actual) {204return this._actual.setRemoteEnvironment(env);205}206}207}208209210