UNPKG

snarkjs

Version:

zkSNARKs implementation in JavaScript

233 lines (182 loc) 6.08 kB
/* Copyright 2018 0KIMS association. This file is part of snarkJS. snarkJS is a free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. snarkJS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with snarkJS. If not, see <https://www.gnu.org/licenses/>. */ const inBrowser = (typeof window !== "undefined"); let NodeWorker; if (!inBrowser) { NodeWorker = require("worker_threads").Worker; } class Deferred { constructor() { this.promise = new Promise((resolve, reject)=> { this.reject = reject; this.resolve = resolve; }); } } function thread(self, fn, modules) { const ctx = { modules: modules }; self.onmessage = function(e) { let data; if (e.data) { data = e.data; } else { data = e; } if (data.cmd == "INIT") { ctx.processId = data.processId; } if (data.cmd == "TERMINATE") { self.postMessage({cmd: "TERMINATE"}); process.exit(); return; } let res = fn(ctx, data); res = res || {}; res.cmd = data.cmd; if (res) { if (res.buff) { self.postMessage(res, [res.buff.buffer]); } else { self.postMessage(res); } } }; } async function buildTaskManager(fn, mods, initTask) { let concurrency; if ((typeof(navigator) === "object") && navigator.hardwareConcurrency) { concurrency = navigator.hardwareConcurrency; } else { const os = require("os"); concurrency = os.cpus().length; } const tm = { workers: [] }; let S = "{"; const keys = Object.keys(mods); for (let i=0; i<keys.length; i++) { const key= keys[i]; S += `${key}: require('${mods[key]}'), `; } S += "}"; function getOnMsg(i) { return function(e) { function finishTask() { if ( (tm.waitingTask && tm.terminateDeferred)) throw new Error("It can not be a waiting task and it's terminating"); if (tm.terminateDeferred) { tm.workers[i].worker.postMessage({cmd: "TERMINATE"}); return; } tm.workers[i].state = "READY"; if (tm.waitingTask) { processTask(i, tm.waitingTask.task, tm.waitingTask.asyncCb); const d = tm.waitingTask.deferral; tm.waitingTask = null; d.resolve(); } } let data; if ((e)&&(e.data)) { data = e.data; } else { data = e; } if (data.cmd == "TERMINATE") { tm.workers[i].state = "TERMINATED"; tm.tryTerminate(); return; } if (tm.workers[i].asyncCb) { tm.workers[i].asyncCb(data).then(()=> { finishTask(); }); } else { finishTask(); } }; } function processTask(i, task, asyncCb) { if (tm.workers[i].state != "READY") throw new Error("Worker is not ready"); tm.workers[i].asyncCb = asyncCb; tm.workers[i].state = "WORKING"; if (task.buff) { tm.workers[i].worker.postMessage(task, [task.buff.buffer]); } else { tm.workers[i].worker.postMessage(task); } } for (let i=0; i<concurrency; i++) { const worker = new NodeWorker(`(${thread.toString()})(require('worker_threads').parentPort, ${fn.toString()},${S});`, {eval: true}); worker.on("message", getOnMsg(i)); tm.workers[i] = { state: "READY", worker: worker, taskPromise: null }; } for (let i=0; i<concurrency; i++) { initTask.cmd = "INIT"; initTask.processId = i; processTask(i, initTask); } tm.finish = function() { const self = this; if (self.terminatePromise != null) throw new Error("Task manager already terminated"); self.terminateDeferred = new Deferred(); for (let i=0; i<concurrency; i++) { if (self.workers[i].state == "READY") { self.workers[i].worker.postMessage({cmd: "TERMINATE"}); } } return self.terminateDeferred.promise; }; tm.addTask = function (task, asyncCb) { const self = this; if (self.waitingTask) throw new Error("Waiting task pending"); if (self.terminateDeferred) throw new Error("New task after task manager terminated"); const deferral = new Deferred(); let i; for (i=0; i<tm.workers.length; i++) { if (self.workers[i].state == "READY") break; } if (i<tm.workers.length) { processTask(i, task, asyncCb); deferral.resolve(); } else { self.waitingTask = { task: task, deferral: deferral, asyncCb: asyncCb }; } return deferral.promise; }; tm.tryTerminate = function() { const self = this; if (!self.terminateDeferred) return; for (let i=0; i<concurrency; i++) { if (self.workers[i].state != "TERMINATED") return; } self.terminateDeferred.resolve(); }; return tm; } module.exports = buildTaskManager;