Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/extensions/common/lazyCreateExtensionHostManager.ts
5240 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Barrier } from '../../../../base/common/async.js';
7
import { Emitter, Event } from '../../../../base/common/event.js';
8
import { Disposable } from '../../../../base/common/lifecycle.js';
9
import { URI } from '../../../../base/common/uri.js';
10
import { ExtensionIdentifier, IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';
11
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
12
import { ILogService } from '../../../../platform/log/common/log.js';
13
import { RemoteAuthorityResolverErrorCode } from '../../../../platform/remote/common/remoteAuthorityResolver.js';
14
import { ExtensionHostKind } from './extensionHostKind.js';
15
import { ExtensionHostManager, friendlyExtHostName } from './extensionHostManager.js';
16
import { IExtensionHostManager } from './extensionHostManagers.js';
17
import { IExtensionDescriptionDelta } from './extensionHostProtocol.js';
18
import { IResolveAuthorityResult } from './extensionHostProxy.js';
19
import { ExtensionRunningLocation } from './extensionRunningLocation.js';
20
import { ActivationKind, ExtensionActivationReason, ExtensionHostStartup, IExtensionHost, IExtensionInspectInfo, IInternalExtensionService } from './extensions.js';
21
import { ResponsiveState } from './rpcProtocol.js';
22
23
/**
24
* Waits until `start()` and only if it has extensions proceeds to really start.
25
*/
26
export class LazyCreateExtensionHostManager extends Disposable implements IExtensionHostManager {
27
28
public readonly onDidExit: Event<[number, string | null]>;
29
private readonly _onDidChangeResponsiveState: Emitter<ResponsiveState> = this._register(new Emitter<ResponsiveState>());
30
public readonly onDidChangeResponsiveState: Event<ResponsiveState> = this._onDidChangeResponsiveState.event;
31
32
private readonly _extensionHost: IExtensionHost;
33
private _startCalled: Barrier;
34
private _actual: ExtensionHostManager | null;
35
36
public get pid(): number | null {
37
if (this._actual) {
38
return this._actual.pid;
39
}
40
return null;
41
}
42
43
public get kind(): ExtensionHostKind {
44
return this._extensionHost.runningLocation.kind;
45
}
46
47
public get startup(): ExtensionHostStartup {
48
return this._extensionHost.startup;
49
}
50
51
public get friendyName(): string {
52
return friendlyExtHostName(this.kind, this.pid);
53
}
54
55
constructor(
56
extensionHost: IExtensionHost,
57
private readonly _initialActivationEvents: string[],
58
private readonly _internalExtensionService: IInternalExtensionService,
59
@IInstantiationService private readonly _instantiationService: IInstantiationService,
60
@ILogService private readonly _logService: ILogService
61
) {
62
super();
63
this._extensionHost = extensionHost;
64
this.onDidExit = extensionHost.onExit;
65
this._startCalled = new Barrier();
66
this._actual = null;
67
}
68
69
override dispose(): void {
70
if (!this._actual) {
71
this._extensionHost.dispose();
72
}
73
super.dispose();
74
}
75
76
private _createActual(reason: string): ExtensionHostManager {
77
this._logService.info(`Creating lazy extension host (${this.friendyName}). Reason: ${reason}`);
78
this._actual = this._register(this._instantiationService.createInstance(ExtensionHostManager, this._extensionHost, this._initialActivationEvents, this._internalExtensionService));
79
this._register(this._actual.onDidChangeResponsiveState((e) => this._onDidChangeResponsiveState.fire(e)));
80
return this._actual;
81
}
82
83
private async _getOrCreateActualAndStart(reason: string): Promise<ExtensionHostManager> {
84
if (this._actual) {
85
// already created/started
86
return this._actual;
87
}
88
const actual = this._createActual(reason);
89
await actual.ready();
90
return actual;
91
}
92
93
public async ready(): Promise<void> {
94
await this._startCalled.wait();
95
if (this._actual) {
96
await this._actual.ready();
97
}
98
}
99
100
public async disconnect(): Promise<void> {
101
await this._actual?.disconnect();
102
}
103
104
public representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean {
105
return this._extensionHost.runningLocation.equals(runningLocation);
106
}
107
108
public async deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {
109
await this._startCalled.wait();
110
if (this._actual) {
111
return this._actual.deltaExtensions(extensionsDelta);
112
}
113
if (extensionsDelta.myToAdd.length > 0) {
114
const actual = this._createActual(`contains ${extensionsDelta.myToAdd.length} new extension(s) (installed or enabled): ${extensionsDelta.myToAdd.map(extId => extId.value)}`);
115
await actual.ready();
116
return;
117
}
118
}
119
120
public containsExtension(extensionId: ExtensionIdentifier): boolean {
121
return this._extensionHost.extensions?.containsExtension(extensionId) ?? false;
122
}
123
124
public async activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {
125
await this._startCalled.wait();
126
if (this._actual) {
127
return this._actual.activate(extension, reason);
128
}
129
return false;
130
}
131
132
public async activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {
133
if (activationKind === ActivationKind.Immediate) {
134
// this is an immediate request, so we cannot wait for start to be called
135
if (this._actual) {
136
return this._actual.activateByEvent(activationEvent, activationKind);
137
}
138
return;
139
}
140
await this._startCalled.wait();
141
if (this._actual) {
142
return this._actual.activateByEvent(activationEvent, activationKind);
143
}
144
}
145
146
public activationEventIsDone(activationEvent: string): boolean {
147
if (!this._startCalled.isOpen()) {
148
return false;
149
}
150
if (this._actual) {
151
return this._actual.activationEventIsDone(activationEvent);
152
}
153
return true;
154
}
155
156
public async getInspectPort(tryEnableInspector: boolean): Promise<IExtensionInspectInfo | undefined> {
157
await this._startCalled.wait();
158
return this._actual?.getInspectPort(tryEnableInspector);
159
}
160
161
public async resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {
162
await this._startCalled.wait();
163
if (this._actual) {
164
return this._actual.resolveAuthority(remoteAuthority, resolveAttempt);
165
}
166
return {
167
type: 'error',
168
error: {
169
message: `Cannot resolve authority`,
170
code: RemoteAuthorityResolverErrorCode.Unknown,
171
detail: undefined
172
}
173
};
174
}
175
176
public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null> {
177
await this._startCalled.wait();
178
if (this._actual) {
179
return this._actual.getCanonicalURI(remoteAuthority, uri);
180
}
181
throw new Error(`Cannot resolve canonical URI`);
182
}
183
184
public async start(extensionRegistryVersionId: number, allExtensions: IExtensionDescription[], myExtensions: ExtensionIdentifier[]): Promise<void> {
185
if (myExtensions.length > 0) {
186
// there are actual extensions, so let's launch the extension host (auto-start)
187
const actual = this._createActual(`contains ${myExtensions.length} extension(s): ${myExtensions.map(extId => extId.value)}.`);
188
const result = actual.ready();
189
this._startCalled.open();
190
return result;
191
}
192
// there are no actual extensions running
193
this._startCalled.open();
194
}
195
196
public async extensionTestsExecute(): Promise<number> {
197
await this._startCalled.wait();
198
const actual = await this._getOrCreateActualAndStart(`execute tests.`);
199
return actual.extensionTestsExecute();
200
}
201
202
public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {
203
await this._startCalled.wait();
204
if (this._actual) {
205
return this._actual.setRemoteEnvironment(env);
206
}
207
}
208
}
209
210