UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

277 lines 11.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WorkerHandler = void 0; const threads_1 = require("threads"); const DataSerializer_1 = require("../data/DataSerializer"); const DataService_1 = require("../service/DataService"); const WorkerServiceProxy_1 = require("../service/WorkerServiceProxy"); const AsyncEventEmitter_1 = require("../_internal/AsyncEventEmitter"); const DummyDataService_1 = require("../service/DummyDataService"); const DummyService_1 = require("../service/DummyService"); class WorkerHandler extends AsyncEventEmitter_1.AsyncEventEmitter { constructor(model, options, config) { var _a; super(); this._serviceOutputResponse = new Map(); this.model = model; this.config = config; this.options = options; this.options.timeout = (_a = this.options.timeout) !== null && _a !== void 0 ? _a : 10000; } build() { return new Promise((resolve) => { if (typeof process.env.NODE_ENV === 'undefined') { // eslint-disable-next-line const NativeWorker = typeof __non_webpack_require__ === "function" ? __non_webpack_require__("worker_threads").Worker : eval("require")("worker_threads").Worker; if (NativeWorker) { // NodeJS NativeWorker.defaultMaxListeners = 0; const resolvedPath = require.resolve(this.options.worker); if (resolvedPath.match(/\.tsx?$/i)) { // Transpile this.options.worker = ` require('ts-node').register(); require(${JSON.stringify(resolvedPath)}); `; this.options.blob = true; } } } this._pool = (0, threads_1.Pool)(() => this._spawnWorker(), { size: this.options.poolSize || 4, concurrency: this.options.poolConcurrency || 2, }); this._pool.events().subscribe((value) => { if (value.type === 'initialized') { resolve(); } }); }); } destroy() { return new Promise((resolve, reject) => { if (this._pool === undefined) { return resolve(); } const timeout = setTimeout(() => { this._pool .terminate(true) .then(() => { resolve(); }) .catch((ex) => { reject(ex); }); }, 2500); this._pool .terminate() .then(() => { clearTimeout(timeout); resolve(); }) .catch((ex) => { clearTimeout(timeout); reject(ex); }); }); } pull(options) { return new Promise((resolve, reject) => { // Pass the pull request to the worker this._pool .queue((worker) => { const pullFn = worker.pull; return pullFn(options); }) .then(resolve) .catch(reject); }); } push(frame, options) { return new Promise((resolve, reject) => { this._pool .queue((worker) => { const pushFn = worker.push; return pushFn(DataSerializer_1.DataSerializer.serialize(frame), options); }) .then(() => { resolve(); }) .catch(reject); }); } invokeMethod(methodName, ...args) { return new Promise((resolve, reject) => { this._pool .queue((worker) => { const invokeMethod = worker.invokeMethod; return invokeMethod(methodName, ...args.map((a) => DataSerializer_1.DataSerializer.serialize(a))); }) .then((result) => { if (result !== undefined) { resolve(DataSerializer_1.DataSerializer.deserialize(result)); } else { resolve(result); } }) .catch(reject); }); } createWorker() { if (this.options.blob) { const worker = new threads_1.BlobWorker(this.options.worker, { type: this.options.type === 'typescript' ? 'classic' : this.options.type, }); return worker; } else { const worker = new threads_1.Worker(this.options.worker, { type: this.options.type === 'typescript' ? 'classic' : this.options.type, }); return worker; } } /** * Spawn a single worker * This method can be called multiple times in a pool * @returns {Promise<Thread>} Thread spawn promise */ _spawnWorker() { return new Promise((resolve, reject) => { const worker = this.createWorker(); (0, threads_1.spawn)(worker, { timeout: this.options.timeout, }) .then((thread) => { const init = thread.init; const pushOutput = thread.pushOutput; const pullOutput = thread.pullOutput; const serviceOutputCall = thread.serviceOutputCall; const serviceInputCall = thread.serviceInputCall; const eventOutput = thread.eventOutput; const findAllServices = thread.findAllServices; const threadId = worker.threadId; this._serviceOutputResponse.set(threadId, thread.serviceOutputResponse); // Subscribe to the workers pull, push and service functions pullOutput().subscribe(this._onWorkerPull.bind(this)); pushOutput().subscribe(this._onWorkerPush.bind(this)); serviceOutputCall().subscribe(this._onWorkerService.bind(this, threadId)); eventOutput().subscribe(this._onWorkerEvent.bind(this)); // Initialize the worker init(Object.assign({ directory: this.options.directory || __dirname, services: this._getServices(), imports: this.options.imports || [], args: this.options.args || {}, type: this.options.type || 'classic', methods: this.options.methods ? this.options.methods.map((method) => { return { name: method.name, handlerFn: method.handler.toString(), }; }) : [] }, this.config)) .then(() => { return findAllServices(); }) .then((services) => { this._addServices(services, serviceInputCall); resolve(thread); }) .catch(reject); }) .catch(reject); }); } /** * Serialize the services of this model * @returns {any[]} Services array */ _getServices() { // Serialize this model services to the worker const services = this.options.services || this.model.findAllServices(); const servicesArray = services.map((service) => { // Services are wrapped in a proxy. Get prototype const serviceBase = Object.getPrototypeOf(service); return { uid: service.uid, type: serviceBase.constructor.name, dataType: service instanceof DataService_1.DataService ? (service.dataType ? service.dataType.name : undefined) : undefined, }; }); return servicesArray; } _addServices(services, call) { const model = this.model; services .filter((service) => { const internalService = this.model.findService(service.name) || this.model.findDataService(service.name); return internalService === undefined; }) .forEach((service) => { if (service.dataType) { const DataType = DataSerializer_1.DataSerializer.findTypeByName(service.dataType); model.addService(new DummyDataService_1.DummyDataService(service.uid, DataType), new WorkerServiceProxy_1.WorkerServiceProxy({ uid: service.uid, callFunction: call, })); } else { model.addService(new DummyService_1.DummyService(service.uid), new WorkerServiceProxy_1.WorkerServiceProxy({ uid: service.uid, callFunction: call, })); } }); } _onWorkerService(threadId, value) { const service = this.model.findDataService(value.serviceUID) || this.model.findService(value.serviceUID); if (service[value.method]) { const serializedParams = value.parameters; const params = []; serializedParams.forEach((param) => { if (param['__type']) { params.push(DataSerializer_1.DataSerializer.deserialize(param)); } else { params.push(param); } }); const promise = service[value.method](...params); Promise.resolve(promise) .then((_) => { if (Array.isArray(_)) { const result = []; _.forEach((r) => { result.push(DataSerializer_1.DataSerializer.serialize(r)); }); this._serviceOutputResponse.get(threadId)({ id: value.id, success: true, result }); } else { const result = DataSerializer_1.DataSerializer.serialize(_); this._serviceOutputResponse.get(threadId)({ id: value.id, success: true, result }); } }) .catch((ex) => { this._serviceOutputResponse.get(threadId)({ id: value.id, success: false, result: ex }); }); } } _onWorkerEvent(value) { this.emit('event', value); } /** * Triggered for each worker that requests a pull * @param {PullOptions} options Pull options */ _onWorkerPull(options) { this.emit('pull', options); } /** * Triggered for each worker that pushes data * @param {any} value Serialized data * @param {PushOptions} options Push options */ _onWorkerPush(value, options) { const deserializedFrame = DataSerializer_1.DataSerializer.deserialize(value); this.emit('push', deserializedFrame, options); } } exports.WorkerHandler = WorkerHandler; //# sourceMappingURL=WorkerHandler.js.map