/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; import winjs = require('vs/base/common/winjs.base'); import Browser = require('vs/base/browser/browser'); import remote = require('vs/base/common/remote'); import Types = require('vs/base/common/types'); import {IThreadServiceStatusListener, ThreadAffinity, Remotable, IRemotableCtorMap, IThreadSynchronizableObject, IDynamicProxy} from 'vs/platform/thread/common/thread'; import {THREAD_SERVICE_PROPERTY_NAME} from 'vs/platform/thread/common/threadService'; import instantiation = require('vs/platform/instantiation/common/instantiation'); import {SyncDescriptor, SyncDescriptor0, createSyncDescriptor, AsyncDescriptor0, AsyncDescriptor1, AsyncDescriptor2, AsyncDescriptor3} from 'vs/platform/instantiation/common/descriptors'; export interface IThreadServiceData { [id:string]:any; } class DynamicProxy implements IDynamicProxy { private _proxyDefinition: T; private _disposeDelegate: ()=>void; constructor(proxyDefinition:T, disposeDelegate:()=>void) { this._proxyDefinition = proxyDefinition; this._disposeDelegate = disposeDelegate; } public dispose(): void { return this._disposeDelegate(); } public getProxyDefinition(): T { return this._proxyDefinition; } } export abstract class AbstractThreadService implements remote.IManyHandler { private static _LAST_DYNAMIC_PROXY_ID:number = 0; private static generateDynamicProxyId(): string { return String(++this._LAST_DYNAMIC_PROXY_ID); } public isInMainThread:boolean; protected _instantiationService: instantiation.IInstantiationService; _boundObjects:{[id:string]:IThreadSynchronizableObject;}; _pendingObjects:winjs.Promise[]; private _localObjMap: { [id:string]: any; }; private _proxyObjMap: { [id:string]: any; }; constructor(isInMainThread: boolean) { this.isInMainThread = isInMainThread; this._boundObjects = {}; this._pendingObjects = []; this._localObjMap = Object.create(null); this._proxyObjMap = Object.create(null); } setInstantiationService(service:instantiation.IInstantiationService): void { this._instantiationService = service; } createInstance>(ctor:instantiation.IConstructorSignature0):T; createInstance>(ctor:instantiation.IConstructorSignature1, a1:A1):T; createInstance>(ctor:instantiation.IConstructorSignature2, a1:A1, a2:A2):T; createInstance>(ctor:instantiation.IConstructorSignature3, a1:A1, a2:A2, a3:A3):T; createInstance>(descriptor: AsyncDescriptor0): T; createInstance>(descriptor: AsyncDescriptor1, a1: A1): T; createInstance>(descriptor: AsyncDescriptor2, a1: A1, a2: A2): T; createInstance>(descriptor: AsyncDescriptor3, a1: A1, a2: A2, a3: A3): T; createInstance(...params:any[]):any { return this._doCreateInstance(params); } protected _doCreateInstance(params:any[]): any { var instanceOrPromise = this._instantiationService.createInstance.apply(this._instantiationService, params); if (winjs.Promise.is(instanceOrPromise)) { var objInstantiated = instanceOrPromise.then((instance: IThreadSynchronizableObject): any => { if (instance.asyncCtor) { var initPromise = instance.asyncCtor(); if (winjs.Promise.is(initPromise)) { return initPromise.then(() => { return instance; }); } } return instance; }); this._pendingObjects.push(objInstantiated); return objInstantiated.then((instance: IThreadSynchronizableObject) => { var r = this._finishInstance(instance); for (var i = 0; i < this._pendingObjects.length; i++) { if (this._pendingObjects[i] === objInstantiated) { this._pendingObjects.splice(i, 1); break; } } return r; }); } return this._finishInstance(>instanceOrPromise); } _finishInstance(instance:IThreadSynchronizableObject): IThreadSynchronizableObject { instance[THREAD_SERVICE_PROPERTY_NAME] = this; this._boundObjects[instance.getId()] = instance; if (instance.creationDone) { instance.creationDone(); } return instance; } registerInstance>(instance: T): void { this._finishInstance(instance); } public handle(rpcId:string, method:string, args:any[]): any { if (!this._localObjMap[rpcId]) { throw new Error('Unknown actor ' + rpcId); } var actor = this._localObjMap[rpcId]; return actor[method].apply(actor, args); } protected _getOrCreateProxyInstance(remoteCom: remote.IProxyHelper, id: string, descriptor: SyncDescriptor0): any { if (this._proxyObjMap[id]) { return this._proxyObjMap[id]; } var result = remote.createProxyFromCtor(remoteCom, id, descriptor.ctor); this._proxyObjMap[id] = result; return result; } protected _registerLocalInstance(id:string, obj:any): any { this._localObjMap[id] = obj; } protected _getOrCreateLocalInstance(id: string, descriptor: SyncDescriptor0): any { if (this._localObjMap[id]) { return this._localObjMap[id]; } var result = this._instantiationService.createInstance(descriptor); this._registerLocalInstance(id, result); return result; } public createDynamicProxyFromMethods(obj:T): IDynamicProxy { let id = AbstractThreadService.generateDynamicProxyId(); let proxyDefinition = this._proxifyMethods(id, obj); return new DynamicProxy(proxyDefinition, () => { delete this._localObjMap[id]; }); } public createDynamicProxyFromMembers(obj:T, allowedMembers:string[]): IDynamicProxy { let id = AbstractThreadService.generateDynamicProxyId(); let proxyDefinition = this._proxifyMembers(id, obj, allowedMembers); return new DynamicProxy(proxyDefinition, () => { delete this._localObjMap[id]; }); } private _proxifyMethods(uniqueIdentifier: string, obj:T): T { if (!Types.isObject(obj)) { return null; } this._localObjMap[uniqueIdentifier] = obj; var r: any = { $__CREATE__PROXY__REQUEST: uniqueIdentifier }; for (var prop in obj) { if (typeof obj[prop] === 'function') { r[prop] = obj[prop].bind(obj); } } return r; } private _proxifyMembers(uniqueIdentifier: string, obj:T, allowedMembers:string[]): T { if (!Types.isObject(obj)) { return null; } this._localObjMap[uniqueIdentifier] = obj; var r: any = { $__CREATE__PROXY__REQUEST: uniqueIdentifier }; for (var prop in obj) { if (allowedMembers.indexOf(prop) === -1) { continue; } if (typeof obj[prop] === 'function') { r[prop] = obj[prop].bind(obj); } else { r[prop] = obj[prop]; } } return r; } public isProxyObject(obj: T): boolean { return obj && !!((obj).$__IS_REMOTE_OBJ); } getRemotable(ctor: instantiation.INewConstructorSignature0): T { var id = Remotable.getId(ctor); if (!id) { throw new Error('Unknown Remotable: <<' + id + '>>'); } var desc = createSyncDescriptor(ctor); if (Remotable.Registry.MainContext[id]) { return this._registerAndInstantiateMainProcessActor(id, desc); } if (Remotable.Registry.PluginHostContext[id]) { return this._registerAndInstantiatePluginHostActor(id, desc); } if (Remotable.Registry.WorkerContext[id]) { return this._registerAndInstantiateWorkerActor(id, desc, Remotable.Registry.WorkerContext[id].affinity); } throw new Error('Unknown Remotable: <<' + id + '>>'); } registerRemotableInstance(ctor: any, instance: any): void { var id = Remotable.getId(ctor); if (!id) { throw new Error('Unknown Remotable: <<' + id + '>>'); } if (Remotable.Registry.MainContext[id]) { return this._registerMainProcessActor(id, instance); } if (Remotable.Registry.PluginHostContext[id]) { return this._registerPluginHostActor(id, instance); } if (Remotable.Registry.WorkerContext[id]) { return this._registerWorkerActor(id, instance); } throw new Error('Unknown Remotable: <<' + id + '>>'); } protected abstract _registerAndInstantiateMainProcessActor(id: string, descriptor: SyncDescriptor0): T; protected abstract _registerMainProcessActor(id: string, actor:T): void; protected abstract _registerAndInstantiatePluginHostActor(id: string, descriptor: SyncDescriptor0): T; protected abstract _registerPluginHostActor(id: string, actor:T): void; protected abstract _registerAndInstantiateWorkerActor(id: string, descriptor: SyncDescriptor0, whichWorker:ThreadAffinity): T; protected abstract _registerWorkerActor(id: string, actor:T): void; }