wip: send message ports upfront

This commit is contained in:
João Moreno 2021-11-24 21:38:32 +01:00
parent 68117af61a
commit 2ff9b87972
No known key found for this signature in database
GPG key ID: 896B853774D1A575
11 changed files with 30 additions and 170 deletions

View file

@ -74,7 +74,6 @@ import './mainThreadAuthentication';
import './mainThreadTimeline';
import './mainThreadTesting';
import './mainThreadSecretState';
import './mainThreadIPC';
export class ExtensionPoints implements IWorkbenchContribution {

View file

@ -1,65 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, IExtHostContext, MainContext, MainThreadIPCShape, IPCHandle, ExtHostIPCShape } from '../common/extHost.protocol';
import { VSBuffer } from 'vs/base/common/buffer';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { Event, Emitter } from 'vs/base/common/event';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
@extHostNamedCustomer(MainContext.MainThreadIPC)
export class MainThreadIPC implements MainThreadIPCShape {
private static Handles = 0;
private readonly emitter = new Emitter<{ handle: IPCHandle, message: VSBuffer }>();
private readonly disposables = new Map<IPCHandle, IDisposable>();
private proxy: ExtHostIPCShape | undefined;
constructor(
extHostContext: IExtHostContext,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
) {
if (!environmentService.options?.ipcProvider) {
return;
}
this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostIPC);
}
async $register(extensionId: ExtensionIdentifier): Promise<IPCHandle | undefined> {
if (!this.proxy) {
return undefined;
}
const ipc = await this.environmentService.options?.ipcProvider?.getMessagePassingProtocol(extensionId.value);
if (!ipc) {
return undefined;
}
const handle = MainThreadIPC.Handles++;
const disposables = new DisposableStore();
ipc.onDidReceiveMessage(message => this.proxy!.$sendMessage(handle, VSBuffer.wrap(message)), undefined, disposables);
Event.chain(this.emitter.event)
.filter(e => e.handle === handle)
.map(({ message }) => message.buffer)
.event(message => ipc.sendMessage(message), undefined, disposables);
this.disposables.set(handle, disposables);
return handle;
}
async $sendMessage(handle: IPCHandle, message: VSBuffer): Promise<void> {
this.emitter.fire({ handle, message });
}
dispose(): void {
this.emitter.dispose();
}
}

View file

@ -92,7 +92,6 @@ import { ExtHostNotebookDocuments } from 'vs/workbench/api/common/extHostNoteboo
import { ExtHostInteractive } from 'vs/workbench/api/common/extHostInteractive';
import { combinedDisposable } from 'vs/base/common/lifecycle';
import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { ExtHostIPC } from 'vs/workbench/api/common/extHostIPC';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@ -178,7 +177,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostWebviewViews = rpcProtocol.set(ExtHostContext.ExtHostWebviewViews, new ExtHostWebviewViews(rpcProtocol, extHostWebviews));
const extHostTesting = rpcProtocol.set(ExtHostContext.ExtHostTesting, new ExtHostTesting(rpcProtocol, extHostCommands));
const extHostUriOpeners = rpcProtocol.set(ExtHostContext.ExtHostUriOpeners, new ExtHostUriOpeners(rpcProtocol));
const extHostIPC = rpcProtocol.set(ExtHostContext.ExtHostIPC, new ExtHostIPC(rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands));
// Check that no named customers are missing
@ -762,10 +760,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
getInlineCompletionItemController<T extends vscode.InlineCompletionItem>(provider: vscode.InlineCompletionItemProvider<T>): vscode.InlineCompletionController<T> {
checkProposedApiEnabled(extension, 'inlineCompletions');
return InlineCompletionController.get(provider);
},
getMessagePassingProtocol() {
checkProposedApiEnabled(extension, 'ipc');
return extHostIPC.getMessagePassingProtocol(extension);
}
};

View file

@ -113,6 +113,7 @@ export interface IInitData {
autoStart: boolean;
remote: { isRemote: boolean; authority: string | undefined; connectionData: IRemoteConnectionData | null; };
uiKind: UIKind;
messagePorts?: ReadonlyMap<string, MessagePort>;
}
export interface IConfigurationInitData extends IConfigurationData {
@ -2195,17 +2196,6 @@ export interface MainThreadTestingShape {
$finishedExtensionTestRun(runId: string): void;
}
export type IPCHandle = number;
export interface ExtHostIPCShape {
$sendMessage(handle: IPCHandle, message: VSBuffer): Promise<void>;
}
export interface MainThreadIPCShape {
$register(extensionId: ExtensionIdentifier): Promise<IPCHandle | undefined>;
$sendMessage(handle: IPCHandle, message: VSBuffer): Promise<void>;
}
// --- proxy identifiers
export const MainContext = {
@ -2265,7 +2255,6 @@ export const MainContext = {
MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService'),
MainThreadTimeline: createMainId<MainThreadTimelineShape>('MainThreadTimeline'),
MainThreadTesting: createMainId<MainThreadTestingShape>('MainThreadTesting'),
MainThreadIPC: createMainId<MainThreadIPCShape>('MainThreadIPC'),
};
export const ExtHostContext = {
@ -2320,5 +2309,4 @@ export const ExtHostContext = {
ExtHostTimeline: createMainId<ExtHostTimelineShape>('ExtHostTimeline'),
ExtHostTesting: createMainId<ExtHostTestingShape>('ExtHostTesting'),
ExtHostTelemetry: createMainId<ExtHostTelemetryShape>('ExtHostTelemetry'),
ExtHostIPC: createMainId<ExtHostIPCShape>('ExtHostIPC'),
};

View file

@ -447,7 +447,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
checkProposedApiEnabled(extensionDescription, 'extensionRuntime');
return that.extensionRuntime;
},
get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); }
get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); },
messagePort: this._initData.messagePorts?.get(ExtensionIdentifier.toKey(extensionDescription.identifier))
});
});
}

View file

@ -1,49 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { VSBuffer } from 'vs/base/common/buffer';
import { ExtHostIPCShape, IPCHandle, MainContext, MainThreadIPCShape } from 'vs/workbench/api/common/extHost.protocol';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { MessagePassingProtocol } from 'vscode';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
export class ExtHostIPC implements ExtHostIPCShape {
private readonly emitter = new Emitter<{ handle: IPCHandle, message: VSBuffer }>();
private proxy: MainThreadIPCShape;
constructor(@IExtHostRpcService rpc: IExtHostRpcService) {
this.proxy = rpc.getProxy(MainContext.MainThreadIPC);
}
async getMessagePassingProtocol(extension: IExtensionDescription): Promise<MessagePassingProtocol | undefined> {
const handle = await this.proxy.$register(extension.identifier);
if (handle === undefined) {
return undefined;
}
const onDidReceiveMessage = Event.chain(this.emitter.event)
.filter(e => e.handle === handle)
.map(({ message }) => message.buffer)
.event;
return {
onDidReceiveMessage,
sendMessage: (message) => {
this.proxy.$sendMessage(handle, VSBuffer.wrap(message));
}
};
}
async $sendMessage(handle: IPCHandle, message: VSBuffer): Promise<void> {
this.emitter.fire({ handle, message });
}
dispose(): void {
this.emitter.dispose();
}
}

View file

@ -295,6 +295,9 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
}
);
const messagePorts = this._environmentService.options?.messagePorts ?? new Map();
worker.postMessage(messagePorts as any, [...messagePorts.values()]);
// await MessagePort and use it to directly communicate
// with the worker extension host
await barrier.wait();

View file

@ -45,7 +45,8 @@ export class ExtensionHostMain {
protocol: IMessagePassingProtocol,
initData: IInitData,
hostUtils: IHostUtils,
uriTransformer: IURITransformer | null
uriTransformer: IURITransformer | null,
messagePorts?: ReadonlyMap<string, MessagePort>
) {
this._isTerminating = false;
this._hostUtils = hostUtils;
@ -56,7 +57,7 @@ export class ExtensionHostMain {
// bootstrap services
const services = new ServiceCollection(...getSingletonServiceDescriptors());
services.set(IExtHostInitDataService, { _serviceBrand: undefined, ...initData });
services.set(IExtHostInitDataService, { _serviceBrand: undefined, ...initData, messagePorts });
services.set(IExtHostRpcService, new ExtHostRpcService(this._rpcProtocol));
services.set(IURITransformerService, new URITransformerService(uriTransformer));
services.set(IHostUtils, hostUtils);

View file

@ -219,18 +219,24 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise<IRenderer
let onTerminate = (reason: string) => nativeClose();
export function create(): void {
const res = new ExtensionWorker();
export function create(): { onmessage: (message: any) => void } {
performance.mark(`code/extHost/willConnectToRenderer`);
connectToRenderer(res.protocol).then(data => {
performance.mark(`code/extHost/didWaitForInitData`);
const extHostMain = new ExtensionHostMain(
data.protocol,
data.initData,
hostUtil,
null,
);
onTerminate = (reason: string) => extHostMain.terminate(reason);
});
return {
onmessage(messagePorts: ReadonlyMap<string, MessagePort>) {
const res = new ExtensionWorker();
connectToRenderer(res.protocol).then(data => {
performance.mark(`code/extHost/didWaitForInitData`);
const extHostMain = new ExtensionHostMain(
data.protocol,
data.initData,
hostUtil,
null,
messagePorts
);
onTerminate = (reason: string) => extHostMain.terminate(reason);
});
}
};
}

View file

@ -337,17 +337,6 @@ interface ISettingsSyncOptions {
enablementHandler?(enablement: boolean): void;
}
export type Message = Uint8Array;
export interface IMessagePassingProtocol {
readonly onDidReceiveMessage: Event<Message>;
sendMessage(message: Message): void;
}
export interface IIPCProvider {
getMessagePassingProtocol(extensionId: string): Promise<IMessagePassingProtocol | undefined>;
}
interface IWorkbenchConstructionOptions {
//#region Connection related configuration
@ -538,7 +527,7 @@ interface IWorkbenchConstructionOptions {
//#region IPC
readonly ipcProvider?: IIPCProvider;
readonly messagePorts?: ReadonlyMap<ExtensionId, MessagePort>;
//#endregion

View file

@ -5,14 +5,7 @@
declare module 'vscode' {
export type Message = Uint8Array;
export interface MessagePassingProtocol {
readonly onDidReceiveMessage: Event<Message>;
sendMessage(message: Message): void;
}
export namespace window {
export function getMessagePassingProtocol(): Thenable<MessagePassingProtocol | undefined>;
export interface ExtensionContext {
readonly messagePort: MessagePort | undefined;
}
}