DjDeveloperr / skia_canvas

Fast HTML Canvas API implementation for Deno using Google Skia
https://deno.land/x/skia_canvas
Apache License 2.0
124 stars 7 forks source link

skiaCanvas import in worker causes worker to freeze #57

Closed backspaces closed 1 year ago

backspaces commented 1 year ago

When I import skiaCanvas in a worker, the import works but then the worker does not get messages from it's parent. Importing in the parent works fine.

Two files: skia.js:

import * as skiaCanvas from 'https://deno.land/x/skia_canvas@0.5.2/mod.ts'

const workerURL = new URL('./skiaWorker.js', import.meta.url).href
const worker = new Worker(workerURL, { type: 'module' })

worker.postMessage({ foo: 42 })
worker.onmessage = e => {
    console.log('main worker results', e.data)
}

skiaWorker:

// Works .. but freezes the onmessage below. Comment these out and it works
import * as skiaCanvas from 'https://deno.land/x/skia_canvas@0.5.2/mod.ts'
console.log(Object.keys(skiaCanvas))

self.onmessage = e => {
    console.log('worker msg', e.data)

    self.postMessage(e.data)
    self.close()
}

This runs the worker with silly messages and skiaCanvas import:

deno run -A --unstable skia.js

The worker imports skiaCanvas but does not receive a message from it's parrent.

Comment out the top import/console.log and the worker runs as expected

m0ose commented 1 year ago

I think there may be something asynchronous going on here. When I add the below code to the skia.js file it works.

setTimeout(() => {
    worker.postMessage({ foo: 42 })
}, 1000)
DjDeveloperr commented 1 year ago

You are right, we're performing some async operations at top level hence the worker evaluation is asynchronous which does not seem to be finished after the Worker constructor itself. I would recommend first sending a message from worker to parent after the skia canvas import and waiting for that message in parent before sending any messages to the worker, otherwise they get dropped.

DjDeveloperr commented 1 year ago

Here's the corrected code:

worker_parent.js

import * as skiaCanvas from "https://deno.land/x/skia_canvas@0.5.2/mod.ts";

const workerURL = new URL("./worker_child.js", import.meta.url).href;
const worker = new Worker(workerURL, { type: "module" });

worker.onmessage = (e) => {
  console.log("main worker results", e.data);

  if (e.data === "init") {
    worker.postMessage({ foo: 42 })
  }
};

worker_child.js

// Works .. but freezes the onmessage below. Comment these out and it works
import * as skiaCanvas from "https://deno.land/x/skia_canvas@0.5.2/mod.ts";
console.log(Object.keys(skiaCanvas));

self.postMessage("init");

self.onmessage = (e) => {
  console.log("worker msg", e.data);

  self.postMessage(e.data);
  self.close();
};
DjDeveloperr commented 1 year ago

Another alternative is to dynamically import skia_canvas after registering message handler in the worker.

DjDeveloperr commented 1 year ago

All in all, this isn't a library issue. I'll close this issue as not planned.

backspaces commented 1 year ago

Thanks!

backspaces commented 1 year ago

Now I have to figure exactly how m0ose did it. He outlined it at work but maybe there's more. This was fun!

DjDeveloperr commented 1 year ago

m0ose's solution adds timeout that allows for the module resolution in worker to complete before posting a message. Otherwise, the message gets dropped because message handler was not registered while the imported module was evaluating. Hard coded delay is not a very optimal solution though!