Path: blob/main/projects/draw-the-hill/scripts/dispatchworker.js
4624 views
"use strict";1self.inputPort = null;2self.jobQueue = [];3self.jobWorkers = [];4self.sentBlobs = [];5self.sentBuffers = [];6self.importedScripts = [];7self.lastBroadcasts = new Map();8class JobWorker {9constructor(port, number) {10this._port = port;11this._number = number;12this._isReady = false;13this._isBusy = false;14this._port.onmessage = (e) => this._OnMessage(e.data);15}16ImportScripts(scripts) {17this._port.postMessage({ type: "_import_scripts", scripts: scripts });18}19SendBlob(blob, id) {20this._port.postMessage({ type: "_send_blob", blob: blob, id: id });21}22SendBuffer(buffer, id) {23this._port.postMessage({ type: "_send_buffer", buffer: buffer, id: id });24}25SendJob(job) {26if (this._isBusy || !this._isReady) throw new Error("cannot take job");27this._isBusy = true;28this._port.postMessage(job, job["transferables"]);29}30_InitBroadcast(job) {31this._port.postMessage(job, job["transferables"]);32}33SendReady() {34this._port.postMessage({ type: "_ready" });35}36IsReady() {37return this._isReady;38}39_OnReady() {40this._isReady = true;41this.MaybeStartNextJob();42}43IsBusy() {44return this._isBusy;45}46GetNumber() {47return this._number;48}49_OnMessage(msg) {50const type = msg["type"];51switch (type) {52case "ready":53this._OnReady();54return;55case "done":56this._OnJobDone();57return;58default:59console.error("unknown message from worker '" + type + "'");60return;61}62}63_OnJobDone() {64this._isBusy = false;65this.MaybeStartNextJob();66}67MaybeStartNextJob() {68if (this._isBusy || !this._isReady) return;69const i = this._FindAvailableJob();70if (i === -1) return;71const job = self.jobQueue[i];72const isBroadcast = job["isBroadcast"];73if (isBroadcast) {74job["doneFlags"][this._number] = true;75if (job["doneFlags"].every((x) => x)) self.jobQueue.splice(i, 1);76} else self.jobQueue.splice(i, 1);77this.SendJob(job);78}79_FindAvailableJob() {80for (let i = 0, len = self.jobQueue.length; i < len; ++i) {81const job = self.jobQueue[i];82if (!job["isBroadcast"] || (this._number < job["doneFlags"].length && !job["doneFlags"][this._number])) return i;83}84return -1;85}86TestMessageChannel() {87this._port.postMessage({ type: "_testMessageChannel" });88}89}90let number = 0;91function AddJobWorker(port) {92const jobWorker = new JobWorker(port, number++);93self.jobWorkers.push(jobWorker);94for (const [blob, id] of self.sentBlobs) jobWorker.SendBlob(blob, id);95for (const [buffer, id] of self.sentBuffers) jobWorker.SendBuffer(buffer, id);96for (const scripts of self.importedScripts) jobWorker.ImportScripts(scripts);97for (const broadcastJob of self.lastBroadcasts.values()) jobWorker._InitBroadcast(broadcastJob);98jobWorker.SendReady();99}100function CancelJob(jobId) {101for (let i = 0, len = self.jobQueue.length; i < len; ++i)102if (self.jobQueue[i].jobId === jobId) {103self.jobQueue.splice(i, 1);104return;105}106}107self.addEventListener("message", (e) => {108const msg = e.data;109const type = msg["type"];110if (type === "_init") {111self.inputPort = msg["in-port"];112self.inputPort.onmessage = OnInputPortMessage;113} else if (type === "_addJobWorker") AddJobWorker(msg["port"]);114});115function OnInputPortMessage(e) {116const msg = e.data;117const type = msg["type"];118if (type === "_cancel") {119CancelJob(msg.jobId);120return;121} else if (type === "_import_scripts") {122const scripts = msg["scripts"];123for (const w of self.jobWorkers) w.ImportScripts(scripts);124self.importedScripts.push(scripts);125return;126} else if (type === "_send_blob") {127const blob = msg["blob"];128const id = msg["id"];129for (const w of self.jobWorkers) w.SendBlob(blob, id);130self.sentBlobs.push([blob, id]);131return;132} else if (type === "_send_buffer") {133const buffer = msg["buffer"];134const id = msg["id"];135for (const w of self.jobWorkers) w.SendBuffer(buffer, id);136self.sentBuffers.push([buffer, id]);137return;138} else if (type === "_no_more_workers") {139self.sentBlobs.length = 0;140self.sentBuffers.length = 0;141self.importedScripts.length = 0;142self.lastBroadcasts.clear();143return;144} else if (type === "_testMessageChannel") {145self.jobWorkers[0].TestMessageChannel();146return;147}148self.jobQueue.push(msg);149if (msg["isBroadcast"]) {150msg["doneFlags"] = new Array(self.jobWorkers.length).fill(false);151msg["transferables"] = [];152self.lastBroadcasts.set(msg["type"], msg);153}154for (const w of self.jobWorkers) w.MaybeStartNextJob();155}156157