extensionRunningLocationTracker.ts ×24

Frontier kind: Code frontier

unlabeled · c_5d7ca2ac9aa5

6 tests · 18460 LOC · 85 files · introduces 0 tests · 295 LOC · 3 files

Introduces — evidence that enters the hierarchy at this concept

Code
48 ranges295 lines · 3 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
2228 ranges18460 lines · 85 files · Browse complete extent
All tests (intent)
6 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

3 files ranked by introduced lines: 295 introduced LOC across 48 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/services/extensions/common/extensionRunningLocationTracker.ts 194 introduced LOC · 24 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extensionRunningLocationTracker.ts
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 { Schemas } from '../../../../base/common/network.js';
7 > import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
8 > import { ExtensionKind } from '../../../../platform/environment/common/environment.js';
9 > import { ExtensionIdentifier, ExtensionIdentifierMap, IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';
10 > import { ILogService } from '../../../../platform/log/common/log.js';
11 > import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';
12 > import { IReadOnlyExtensionDescriptionRegistry } from './extensionDescriptionRegistry.js';
13 > import { ExtensionHostKind, ExtensionRunningPreference, IExtensionHostKindPicker, determineExtensionHostKinds } from './extensionHostKind.js';
14 > import { IExtensionHostManager } from './extensionHostManagers.js';
15 > import { IExtensionManifestPropertiesService } from './extensionManifestPropertiesService.js';
16 > import { ExtensionRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation, RemoteRunningLocation } from './extensionRunningLocation.js';
17 > import { isProposedApiEnabled } from './extensions.js';
18 >
19 > export class ExtensionRunningLocationTracker {
20 >
21 > private _runningLocation = new ExtensionIdentifierMap<ExtensionRunningLocation | null>();
22 > private _maxLocalProcessAffinity: number = 0;
23 > private _maxLocalWebWorkerAffinity: number = 0;
24 >
25 > public get maxLocalProcessAffinity(): number {
26 > return this._maxLocalProcessAffinity;
27 > }
28 >
29 > public get maxLocalWebWorkerAffinity(): number {
30 return this._maxLocalWebWorkerAffinity;
31 }
33 > constructor(
34 > private readonly _registry: IReadOnlyExtensionDescriptionRegistry,
35 > private readonly _extensionHostKindPicker: IExtensionHostKindPicker,
36 > @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
37 > @IConfigurationService private readonly _configurationService: IConfigurationService,
38 > @ILogService private readonly _logService: ILogService,
39 > @IExtensionManifestPropertiesService private readonly _extensionManifestPropertiesService: IExtensionManifestPropertiesService,
40 > ) { }
41 >
42 > public set(extensionId: ExtensionIdentifier, runningLocation: ExtensionRunningLocation) {
43 this._runningLocation.set(extensionId, runningLocation);
44 }
46 > public readExtensionKinds(extensionDescription: IExtensionDescription): ExtensionKind[] {
47 > if (extensionDescription.isUnderDevelopment && this._environmentService.extensionDevelopmentKind) {
48 return this._environmentService.extensionDevelopmentKind;
49 }
51 > return this._extensionManifestPropertiesService.getExtensionKind(extensionDescription);
52 > }
53 >
54 > public getRunningLocation(extensionId: ExtensionIdentifier): ExtensionRunningLocation | null {
55 return this._runningLocation.get(extensionId) || null;
56 }
58 > public filterByRunningLocation(extensions: readonly IExtensionDescription[], desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] {
59 return filterExtensionDescriptions(extensions, this._runningLocation, extRunningLocation => desiredRunningLocation.equals(extRunningLocation));
60 }
62 > public filterByExtensionHostKind(extensions: readonly IExtensionDescription[], desiredExtensionHostKind: ExtensionHostKind): IExtensionDescription[] {
63 return filterExtensionDescriptions(extensions, this._runningLocation, extRunningLocation => extRunningLocation.kind === desiredExtensionHostKind);
64 }
66 > public filterByExtensionHostManager(extensions: readonly IExtensionDescription[], extensionHostManager: IExtensionHostManager): IExtensionDescription[] {
67 return filterExtensionDescriptions(extensions, this._runningLocation, extRunningLocation => extensionHostManager.representsRunningLocation(extRunningLocation));
68 }
70 > private _computeAffinity(inputExtensions: IExtensionDescription[], extensionHostKind: ExtensionHostKind, isInitialAllocation: boolean): { affinities: ExtensionIdentifierMap<number>; maxAffinity: number } {
71 > // Only analyze extensions that can execute
72 > const extensions = new ExtensionIdentifierMap<IExtensionDescription>();
73 > for (const extension of inputExtensions) {
74 > if (extension.main || extension.browser) {
75 > extensions.set(extension.identifier, extension);
76 > }
77 > }
78 > // Also add existing extensions of the same kind that can execute
79 > for (const extension of this._registry.getAllExtensionDescriptions()) {
80 > if (extension.main || extension.browser) {
81 > const runningLocation = this._runningLocation.get(extension.identifier);
82 > if (runningLocation && runningLocation.kind === extensionHostKind) {
83 extensions.set(extension.identifier, extension);
84 }
86 > }
87 >
88 > // Initially, each extension belongs to its own group
89 > const groups = new ExtensionIdentifierMap<number>();
90 > let groupNumber = 0;
91 > for (const [_, extension] of extensions) {
92 > groups.set(extension.identifier, ++groupNumber);
93 > }
94 >
95 > const changeGroup = (from: number, to: number) => {
96 for (const [key, group] of groups) {
97 if (group === from) {
100 }
101 };
103 > // We will group things together when there are dependencies
104 > for (const [_, extension] of extensions) {
105 > if (!extension.extensionDependencies) {
106 > continue;
107 > }
108 const myGroup = groups.get(extension.identifier)!;
109 for (const depId of extension.extensionDependencies) {
122 }
123 }
125 > // We will also group things together when there are extensionAffinity declarations
126 > for (const [_, extension] of extensions) {
127 > if (!extension.extensionAffinity) {
128 > continue;
129 > }
130 > if (!isProposedApiEnabled(extension, 'extensionAffinity')) {
131 this._logService.warn(`Extension '${extension.identifier.value}' declares 'extensionAffinity' in its package.json but does not enable the 'extensionAffinity' API proposal. Add '"enabledApiProposals": ["extensionAffinity"]' to the extension's package.json to use this feature.`);
132 continue;
133 }
134 > const myGroup = groups.get(extension.identifier)!; extensionRunningLocationTracker.ts
135 > for (const colocateId of extension.extensionAffinity) {
136 > const colocateGroup = groups.get(colocateId);
137 > if (!colocateGroup) {
138 // the extension is not installed or can't execute, so it has no impact
139 continue;
147 changeGroup(colocateGroup, myGroup);
148 }
150 >
151 > // Initialize with existing affinities
152 > const resultingAffinities = new Map<number, number>();
153 > let lastAffinity = 0;
154 > for (const [_, extension] of extensions) {
155 > const runningLocation = this._runningLocation.get(extension.identifier);
156 > if (runningLocation) {
157 const group = groups.get(extension.identifier)!;
158 resultingAffinities.set(group, runningLocation.affinity);
159 lastAffinity = Math.max(lastAffinity, runningLocation.affinity);
160 }
162 >
163 > // When doing extension host debugging, we will ignore the configured affinity
164 > // because we can currently debug a single extension host
165 > if (!this._environmentService.isExtensionDevelopment) {
166 > // Go through each configured affinity and try to accomodate it
167 > const configuredAffinities = this._configurationService.getValue<{ [extensionId: string]: number } | undefined>('extensions.experimental.affinity') || {};
168 > const configuredExtensionIds = Object.keys(configuredAffinities);
169 > const configuredAffinityToResultingAffinity = new Map<number, number>();
170 > for (const extensionId of configuredExtensionIds) {
171 const configuredAffinity = configuredAffinities[extensionId];
172 if (typeof configuredAffinity !== 'number' || configuredAffinity <= 0 || Math.floor(configuredAffinity) !== configuredAffinity) {
203 resultingAffinities.set(group, affinity3);
204 }
206 >
207 > const result = new ExtensionIdentifierMap<number>();
208 > for (const extension of inputExtensions) {
209 > const group = groups.get(extension.identifier) || 0;
210 > const affinity = resultingAffinities.get(group) || 0;
211 > result.set(extension.identifier, affinity);
212 > }
213 >
214 > if (lastAffinity > 0 && isInitialAllocation) {
215 for (let affinity = 1; affinity <= lastAffinity; affinity++) {
216 const extensionIds: ExtensionIdentifier[] = [];
223 }
224 }
226 > return { affinities: result, maxAffinity: lastAffinity };
227 > }
228 >
229 > public computeRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], isInitialAllocation: boolean): ExtensionIdentifierMap<ExtensionRunningLocation | null> {
230 > return this._doComputeRunningLocation(this._runningLocation, localExtensions, remoteExtensions, isInitialAllocation).runningLocation;
231 > }
232 >
233 > private _doComputeRunningLocation(existingRunningLocation: ExtensionIdentifierMap<ExtensionRunningLocation | null>, localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], isInitialAllocation: boolean): { runningLocation: ExtensionIdentifierMap<ExtensionRunningLocation | null>; maxLocalProcessAffinity: number; maxLocalWebWorkerAffinity: number } {
234 > // Skip extensions that have an existing running location
235 > localExtensions = localExtensions.filter(extension => !existingRunningLocation.has(extension.identifier));
236 > remoteExtensions = remoteExtensions.filter(extension => !existingRunningLocation.has(extension.identifier));
237 >
238 > const extensionHostKinds = determineExtensionHostKinds(
239 > localExtensions,
240 > remoteExtensions,
241 > (extension) => this.readExtensionKinds(extension),
242 > (extensionId, extensionKinds, isInstalledLocally, isInstalledRemotely, preference) => this._extensionHostKindPicker.pickExtensionHostKind(extensionId, extensionKinds, isInstalledLocally, isInstalledRemotely, preference)
243 > );
244 >
245 > const extensions = new ExtensionIdentifierMap<IExtensionDescription>();
246 > for (const extension of localExtensions) {
247 > extensions.set(extension.identifier, extension);
248 > }
249 > for (const extension of remoteExtensions) {
250 extensions.set(extension.identifier, extension);
251 }
253 > const result = new ExtensionIdentifierMap<ExtensionRunningLocation | null>();
254 > const localProcessExtensions: IExtensionDescription[] = [];
255 > const localWebWorkerExtensions: IExtensionDescription[] = [];
256 > for (const [extensionIdKey, extensionHostKind] of extensionHostKinds) {
257 > let runningLocation: ExtensionRunningLocation | null = null;
258 > if (extensionHostKind === ExtensionHostKind.LocalProcess) {
259 > const extensionDescription = extensions.get(extensionIdKey);
260 > if (extensionDescription) {
261 > localProcessExtensions.push(extensionDescription);
262 > }
263 > } else if (extensionHostKind === ExtensionHostKind.LocalWebWorker) {
264 const extensionDescription = extensions.get(extensionIdKey);
265 if (extensionDescription) {
269 runningLocation = new RemoteRunningLocation();
270 }
271 > result.set(extensionIdKey, runningLocation); extensionRunningLocationTracker.ts
272 > }
273 >
274 > const { affinities, maxAffinity } = this._computeAffinity(localProcessExtensions, ExtensionHostKind.LocalProcess, isInitialAllocation);
275 > for (const extension of localProcessExtensions) {
276 > const affinity = affinities.get(extension.identifier) || 0;
277 > result.set(extension.identifier, new LocalProcessRunningLocation(affinity));
278 > }
279 > const { affinities: localWebWorkerAffinities, maxAffinity: maxLocalWebWorkerAffinity } = this._computeAffinity(localWebWorkerExtensions, ExtensionHostKind.LocalWebWorker, isInitialAllocation);
280 > for (const extension of localWebWorkerExtensions) {
281 const affinity = localWebWorkerAffinities.get(extension.identifier) || 0;
282 result.set(extension.identifier, new LocalWebWorkerRunningLocation(affinity));
283 }
285 > // Add extensions that already have an existing running location
286 > for (const [extensionIdKey, runningLocation] of existingRunningLocation) {
287 if (runningLocation) {
288 result.set(extensionIdKey, runningLocation);
289 }
290 }
292 > return { runningLocation: result, maxLocalProcessAffinity: maxAffinity, maxLocalWebWorkerAffinity: maxLocalWebWorkerAffinity };
293 > }
294 >
295 > public initializeRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[]): void {
296 const { runningLocation, maxLocalProcessAffinity, maxLocalWebWorkerAffinity } = this._doComputeRunningLocation(this._runningLocation, localExtensions, remoteExtensions, true);
297 this._runningLocation = runningLocation;
299 this._maxLocalWebWorkerAffinity = maxLocalWebWorkerAffinity;
300 }
302 > /**
303 > * Returns the running locations for the removed extensions.
304 > */
305 > public deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): ExtensionIdentifierMap<ExtensionRunningLocation | null> {
306 // Remove old running location
307 const removedRunningLocation = new ExtensionIdentifierMap<ExtensionRunningLocation | null>();
317 return removedRunningLocation;
318 }
320 > /**
321 > * Update `this._runningLocation` with running locations for newly enabled/installed extensions.
322 > */
323 > private _updateRunningLocationForAddedExtensions(toAdd: IExtensionDescription[]): void {
324 // Determine new running location
325 const localProcessExtensions: IExtensionDescription[] = [];
352 }
353 }
355 >
356 > export function filterExtensionDescriptions(extensions: readonly IExtensionDescription[], runningLocation: ExtensionIdentifierMap<ExtensionRunningLocation | null>, predicate: (extRunningLocation: ExtensionRunningLocation) => boolean): IExtensionDescription[] {
357 return extensions.filter((ext) => {
358 const extRunningLocation = runningLocation.get(ext.identifier);
360 });
361 }
363 > export function filterExtensionIdentifiers(extensions: readonly ExtensionIdentifier[], runningLocation: ExtensionIdentifierMap<ExtensionRunningLocation | null>, predicate: (extRunningLocation: ExtensionRunningLocation) => boolean): ExtensionIdentifier[] {
364 return extensions.filter((ext) => {
365 const extRunningLocation = runningLocation.get(ext);
src/vs/workbench/services/extensions/common/extensionHostKind.ts 72 introduced LOC · 15 ranges

Open complete file

46
47 export function determineExtensionHostKinds(
48 > _localExtensions: IExtensionDescription[], extensionHostKind.ts
49 > _remoteExtensions: IExtensionDescription[],
50 > getExtensionKind: (extensionDescription: IExtensionDescription) => ExtensionKind[],
51 > pickExtensionHostKind: (extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference) => ExtensionHostKind | null
52 > ): Map<string, ExtensionHostKind | null> {
53 > const localExtensions = toExtensionWithKind(_localExtensions, getExtensionKind);
54 > const remoteExtensions = toExtensionWithKind(_remoteExtensions, getExtensionKind);
55 >
56 > const allExtensions = new Map<string, ExtensionInfo>();
57 > const collectExtension = (ext: ExtensionWithKind) => {
58 > if (allExtensions.has(ext.key)) {
59 return;
60 }
61 > const local = localExtensions.get(ext.key) || null; extensionHostKind.ts
62 > const remote = remoteExtensions.get(ext.key) || null;
63 > const info = new ExtensionInfo(local, remote);
64 > allExtensions.set(info.key, info);
65 > };
66 > localExtensions.forEach((ext) => collectExtension(ext));
67 > remoteExtensions.forEach((ext) => collectExtension(ext));
68 >
69 > const extensionHostKinds = new Map<string, ExtensionHostKind | null>();
70 > allExtensions.forEach((ext) => {
71 > const isInstalledLocally = Boolean(ext.local);
72 > const isInstalledRemotely = Boolean(ext.remote);
73 >
74 > const isLocallyUnderDevelopment = Boolean(ext.local && ext.local.isUnderDevelopment);
75 > const isRemotelyUnderDevelopment = Boolean(ext.remote && ext.remote.isUnderDevelopment);
76 >
77 > let preference = ExtensionRunningPreference.None;
78 > if (isLocallyUnderDevelopment && !isRemotelyUnderDevelopment) {
79 preference = ExtensionRunningPreference.Local;
80 > } else if (isRemotelyUnderDevelopment && !isLocallyUnderDevelopment) { extensionHostKind.ts
81 preference = ExtensionRunningPreference.Remote;
82 }
84 > extensionHostKinds.set(ext.key, pickExtensionHostKind(ext.identifier, ext.kind, isInstalledLocally, isInstalledRemotely, preference));
85 > });
86 >
87 > return extensionHostKinds;
88 > }
89
90 > function toExtensionWithKind( extensionHostKind.ts
91 > extensions: IExtensionDescription[],
92 > getExtensionKind: (extensionDescription: IExtensionDescription) => ExtensionKind[]
93 > ): Map<string, ExtensionWithKind> {
94 > const result = new Map<string, ExtensionWithKind>();
95 > extensions.forEach((desc) => {
96 > const ext = new ExtensionWithKind(desc, getExtensionKind(desc));
97 > result.set(ext.key, ext);
98 > });
99 > return result;
100 > }
101
102 class ExtensionWithKind {
103
104 constructor(
105 > public readonly desc: IExtensionDescription, extensionHostKind.ts
106 > public readonly kind: ExtensionKind[]
107 > ) { }
108
109 public get key(): string {
110 > return ExtensionIdentifier.toKey(this.desc.identifier); extensionHostKind.ts
111 > }
112
113 public get isUnderDevelopment(): boolean {
114 > return this.desc.isUnderDevelopment; extensionHostKind.ts
115 > }
116 }
117
119
120 constructor(
121 > public readonly local: ExtensionWithKind | null, extensionHostKind.ts
122 > public readonly remote: ExtensionWithKind | null,
123 > ) { }
124
125 public get key(): string {
126 > if (this.local) { extensionHostKind.ts
127 > return this.local.key;
128 > }
129 return this.remote!.key;
131
132 public get identifier(): ExtensionIdentifier {
133 > if (this.local) { extensionHostKind.ts
134 > return this.local.desc.identifier;
135 > }
136 return this.remote!.desc.identifier;
138
139 public get kind(): ExtensionKind[] {
140 > // in case of disagreements between extension kinds, it is always extensionHostKind.ts
141 > // better to pick the local extension because it has a much higher
142 > // chance of being up-to-date
143 > if (this.local) {
144 > return this.local.kind;
145 > }
146 return this.remote!.kind;
148 }
src/vs/workbench/services/extensions/common/extensionRunningLocation.ts 29 introduced LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extensionRunningLocation.ts
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 { ExtensionHostKind } from './extensionHostKind.js';
7 >
8 > export class LocalProcessRunningLocation {
9 > public readonly kind = ExtensionHostKind.LocalProcess;
10 > constructor(
11 > public readonly affinity: number
12 > ) { }
13 > public equals(other: ExtensionRunningLocation) {
14 return (this.kind === other.kind && this.affinity === other.affinity);
15 }
16 > public asString(): string { extensionRunningLocation.ts
17 if (this.affinity === 0) {
18 return 'LocalProcess';
20 return `LocalProcess${this.affinity}`;
21 }
23 >
24 > export class LocalWebWorkerRunningLocation {
25 > public readonly kind = ExtensionHostKind.LocalWebWorker;
26 > constructor(
27 public readonly affinity: number
28 ) { }
29 > public equals(other: ExtensionRunningLocation) { extensionRunningLocation.ts
30 return (this.kind === other.kind && this.affinity === other.affinity);
31 }
32 > public asString(): string { extensionRunningLocation.ts
33 if (this.affinity === 0) {
34 return 'LocalWebWorker';
36 return `LocalWebWorker${this.affinity}`;
37 }
39 >
40 > export class RemoteRunningLocation {
41 public readonly kind = ExtensionHostKind.Remote;
42 public readonly affinity = 0;
43 > public equals(other: ExtensionRunningLocation) { extensionRunningLocation.ts
44 return (this.kind === other.kind);
45 }
46 > public asString(): string { extensionRunningLocation.ts
47 return 'Remote';
48 }
50 >
51 > export type ExtensionRunningLocation = LocalProcessRunningLocation | LocalWebWorkerRunningLocation | RemoteRunningLocation;