@jbrowse/core
Version:
JBrowse 2 core libraries used by plugins
114 lines (113 loc) • 4.43 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const mobx_state_tree_1 = require("mobx-state-tree");
const configuration_1 = require("../configuration");
const util_1 = require("../util");
function isCloneable(thing) {
return !(typeof thing === 'function') && !(thing instanceof Error);
}
function detectHardwareConcurrency() {
const mainThread = typeof window !== 'undefined';
const canDetect = mainThread && 'hardwareConcurrency' in window.navigator;
return mainThread && canDetect ? window.navigator.hardwareConcurrency : 1;
}
class LazyWorker {
constructor(driver) {
this.driver = driver;
}
async getWorker() {
if (!this.workerP) {
this.workerP = this.driver.makeWorker().catch((e) => {
this.workerP = undefined;
throw e;
});
}
return this.workerP;
}
}
class BaseRpcDriver {
constructor(args) {
this.lastWorkerAssignment = -1;
this.workerAssignments = new Map();
this.maxPingTime = 30000;
this.workerCheckFrequency = 5000;
this.config = args.config;
}
filterArgs(thing, sessionId) {
if (Array.isArray(thing)) {
return thing
.filter(thing => isCloneable(thing))
.map(t => this.filterArgs(t, sessionId));
}
else if (typeof thing === 'object' && thing !== null) {
if ((0, mobx_state_tree_1.isStateTreeNode)(thing) && !(0, mobx_state_tree_1.isAlive)(thing)) {
throw new Error('dead state tree node passed to RPC call');
}
else if (thing instanceof File) {
return thing;
}
else {
return Object.fromEntries(Object.entries(thing)
.filter(e => isCloneable(e[1]))
.map(([k, v]) => [k, this.filterArgs(v, sessionId)]));
}
}
else {
return thing;
}
}
async remoteAbort(sessionId, functionName, stopTokenId) {
const worker = await this.getWorker(sessionId);
await worker.call(functionName, { stopTokenId }, { timeout: 1000000, rpcDriverClassName: this.name });
}
createWorkerPool() {
const hardwareConcurrency = detectHardwareConcurrency();
const workerCount = (0, configuration_1.readConfObject)(this.config, 'workerCount') ||
(0, util_1.clamp)(1, Math.max(1, hardwareConcurrency - 1), 5);
const workers = [];
for (let i = 0; i < workerCount; i++) {
workers.push(new LazyWorker(this));
}
return workers;
}
getWorkerPool() {
if (!this.workerPool) {
const res = this.createWorkerPool();
this.workerPool = res;
return res;
}
return this.workerPool;
}
async getWorker(sessionId) {
const workers = this.getWorkerPool();
let workerNumber = this.workerAssignments.get(sessionId);
if (workerNumber === undefined) {
const workerAssignment = (this.lastWorkerAssignment + 1) % workers.length;
this.workerAssignments.set(sessionId, workerAssignment);
this.lastWorkerAssignment = workerAssignment;
workerNumber = workerAssignment;
}
return workers[workerNumber].getWorker();
}
async call(pluginManager, sessionId, functionName, args, options = {}) {
if (!sessionId) {
throw new TypeError('sessionId is required');
}
const unextendedWorker = await this.getWorker(sessionId);
const worker = pluginManager.evaluateExtensionPoint('Core-extendWorker', unextendedWorker);
const rpcMethod = pluginManager.getRpcMethodType(functionName);
if (!rpcMethod) {
throw new Error(`unknown RPC method ${functionName}`);
}
const serializedArgs = await rpcMethod.serializeArguments(args, this.name);
const filteredAndSerializedArgs = this.filterArgs(serializedArgs, sessionId);
const call = await worker.call(functionName, filteredAndSerializedArgs, {
timeout: 5 * 60 * 1000,
statusCallback: args.statusCallback,
rpcDriverClassName: this.name,
...options,
});
return rpcMethod.deserializeReturn(call, args, this.name);
}
}
exports.default = BaseRpcDriver;