Path: blob/main/src/vs/workbench/services/extensions/common/lazyCreateExtensionHostManager.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 { 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}6768private _createActual(reason: string): ExtensionHostManager {69this._logService.info(`Creating lazy extension host (${this.friendyName}). Reason: ${reason}`);70this._actual = this._register(this._instantiationService.createInstance(ExtensionHostManager, this._extensionHost, this._initialActivationEvents, this._internalExtensionService));71this._register(this._actual.onDidChangeResponsiveState((e) => this._onDidChangeResponsiveState.fire(e)));72return this._actual;73}7475private async _getOrCreateActualAndStart(reason: string): Promise<ExtensionHostManager> {76if (this._actual) {77// already created/started78return this._actual;79}80const actual = this._createActual(reason);81await actual.ready();82return actual;83}8485public async ready(): Promise<void> {86await this._startCalled.wait();87if (this._actual) {88await this._actual.ready();89}90}9192public async disconnect(): Promise<void> {93await this._actual?.disconnect();94}9596public representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean {97return this._extensionHost.runningLocation.equals(runningLocation);98}99100public async deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {101await this._startCalled.wait();102if (this._actual) {103return this._actual.deltaExtensions(extensionsDelta);104}105if (extensionsDelta.myToAdd.length > 0) {106const actual = this._createActual(`contains ${extensionsDelta.myToAdd.length} new extension(s) (installed or enabled): ${extensionsDelta.myToAdd.map(extId => extId.value)}`);107await actual.ready();108return;109}110}111112public containsExtension(extensionId: ExtensionIdentifier): boolean {113return this._extensionHost.extensions?.containsExtension(extensionId) ?? false;114}115116public async activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {117await this._startCalled.wait();118if (this._actual) {119return this._actual.activate(extension, reason);120}121return false;122}123124public async activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {125if (activationKind === ActivationKind.Immediate) {126// this is an immediate request, so we cannot wait for start to be called127if (this._actual) {128return this._actual.activateByEvent(activationEvent, activationKind);129}130return;131}132await this._startCalled.wait();133if (this._actual) {134return this._actual.activateByEvent(activationEvent, activationKind);135}136}137138public activationEventIsDone(activationEvent: string): boolean {139if (!this._startCalled.isOpen()) {140return false;141}142if (this._actual) {143return this._actual.activationEventIsDone(activationEvent);144}145return true;146}147148public async getInspectPort(tryEnableInspector: boolean): Promise<IExtensionInspectInfo | undefined> {149await this._startCalled.wait();150return this._actual?.getInspectPort(tryEnableInspector);151}152153public async resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {154await this._startCalled.wait();155if (this._actual) {156return this._actual.resolveAuthority(remoteAuthority, resolveAttempt);157}158return {159type: 'error',160error: {161message: `Cannot resolve authority`,162code: RemoteAuthorityResolverErrorCode.Unknown,163detail: undefined164}165};166}167168public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null> {169await this._startCalled.wait();170if (this._actual) {171return this._actual.getCanonicalURI(remoteAuthority, uri);172}173throw new Error(`Cannot resolve canonical URI`);174}175176public async start(extensionRegistryVersionId: number, allExtensions: IExtensionDescription[], myExtensions: ExtensionIdentifier[]): Promise<void> {177if (myExtensions.length > 0) {178// there are actual extensions, so let's launch the extension host (auto-start)179const actual = this._createActual(`contains ${myExtensions.length} extension(s): ${myExtensions.map(extId => extId.value)}.`);180const result = actual.ready();181this._startCalled.open();182return result;183}184// there are no actual extensions running185this._startCalled.open();186}187188public async extensionTestsExecute(): Promise<number> {189await this._startCalled.wait();190const actual = await this._getOrCreateActualAndStart(`execute tests.`);191return actual.extensionTestsExecute();192}193194public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {195await this._startCalled.wait();196if (this._actual) {197return this._actual.setRemoteEnvironment(env);198}199}200}201202203