diff options
author | Nayeem Rahman <nayeemrmn99@gmail.com> | 2020-12-17 16:37:57 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-17 17:37:57 +0100 |
commit | ffb5f7a4e1d5d4ac488058ca3ec3c0805587fe44 (patch) | |
tree | dc7b79a699732680fa309d97e5b4c2bc5f486a4a /runtime/js/11_workers.js | |
parent | 55dc467b419b8e5897b1c832b04d63e383253d84 (diff) |
refactor: Rename runtime/rt to runtime/js (#8806)
Diffstat (limited to 'runtime/js/11_workers.js')
-rw-r--r-- | runtime/js/11_workers.js | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/runtime/js/11_workers.js b/runtime/js/11_workers.js new file mode 100644 index 000000000..62210dfae --- /dev/null +++ b/runtime/js/11_workers.js @@ -0,0 +1,206 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +((window) => { + const core = window.Deno.core; + const { Window } = window.__bootstrap.globalInterfaces; + const { log } = window.__bootstrap.util; + const { defineEventHandler } = window.__bootstrap.webUtil; + + function createWorker( + specifier, + hasSourceCode, + sourceCode, + useDenoNamespace, + name, + ) { + return core.jsonOpSync("op_create_worker", { + specifier, + hasSourceCode, + sourceCode, + name, + useDenoNamespace, + }); + } + + function hostTerminateWorker(id) { + core.jsonOpSync("op_host_terminate_worker", { id }); + } + + function hostPostMessage(id, data) { + core.jsonOpSync("op_host_post_message", { id }, data); + } + + function hostGetMessage(id) { + return core.jsonOpAsync("op_host_get_message", { id }); + } + + const encoder = new TextEncoder(); + const decoder = new TextDecoder(); + + function encodeMessage(data) { + const dataJson = JSON.stringify(data); + return encoder.encode(dataJson); + } + + function decodeMessage(dataIntArray) { + const dataJson = decoder.decode(dataIntArray); + return JSON.parse(dataJson); + } + + class Worker extends EventTarget { + #id = 0; + #name = ""; + #terminated = false; + + constructor(specifier, options) { + super(); + const { type = "classic", name = "unknown" } = options ?? {}; + + if (type !== "module") { + throw new Error( + 'Not yet implemented: only "module" type workers are supported', + ); + } + + this.#name = name; + const hasSourceCode = false; + const sourceCode = decoder.decode(new Uint8Array()); + + const useDenoNamespace = options ? !!options.deno : false; + + const { id } = createWorker( + specifier, + hasSourceCode, + sourceCode, + useDenoNamespace, + options?.name, + ); + this.#id = id; + this.#poll(); + } + + #handleMessage = (msgData) => { + let data; + try { + data = decodeMessage(new Uint8Array(msgData)); + } catch (e) { + const msgErrorEvent = new MessageEvent("messageerror", { + cancelable: false, + data, + }); + return; + } + + const msgEvent = new MessageEvent("message", { + cancelable: false, + data, + }); + + this.dispatchEvent(msgEvent); + }; + + #handleError = (e) => { + const event = new ErrorEvent("error", { + cancelable: true, + message: e.message, + lineno: e.lineNumber ? e.lineNumber + 1 : undefined, + colno: e.columnNumber ? e.columnNumber + 1 : undefined, + filename: e.fileName, + error: null, + }); + + let handled = false; + + this.dispatchEvent(event); + if (event.defaultPrevented) { + handled = true; + } + + return handled; + }; + + #poll = async () => { + while (!this.#terminated) { + const event = await hostGetMessage(this.#id); + + // If terminate was called then we ignore all messages + if (this.#terminated) { + return; + } + + const type = event.type; + + if (type === "terminalError") { + this.#terminated = true; + if (!this.#handleError(event.error)) { + if (globalThis instanceof Window) { + throw new Error("Unhandled error event reached main worker."); + } else { + core.jsonOpSync( + "op_host_unhandled_error", + { message: event.error.message }, + ); + } + } + continue; + } + + if (type === "msg") { + this.#handleMessage(event.data); + continue; + } + + if (type === "error") { + if (!this.#handleError(event.error)) { + if (globalThis instanceof Window) { + throw new Error("Unhandled error event reached main worker."); + } else { + core.jsonOpSync( + "op_host_unhandled_error", + { message: event.error.message }, + ); + } + } + continue; + } + + if (type === "close") { + log(`Host got "close" message from worker: ${this.#name}`); + this.#terminated = true; + return; + } + + throw new Error(`Unknown worker event: "${type}"`); + } + }; + + postMessage(message, transferOrOptions) { + if (transferOrOptions) { + throw new Error( + "Not yet implemented: `transfer` and `options` are not supported.", + ); + } + + if (this.#terminated) { + return; + } + + hostPostMessage(this.#id, encodeMessage(message)); + } + + terminate() { + if (!this.#terminated) { + this.#terminated = true; + hostTerminateWorker(this.#id); + } + } + } + + defineEventHandler(Worker.prototype, "error"); + defineEventHandler(Worker.prototype, "message"); + defineEventHandler(Worker.prototype, "messageerror"); + + window.__bootstrap.worker = { + Worker, + }; +})(this); |