66 lines
2.5 KiB
TypeScript
66 lines
2.5 KiB
TypeScript
import initOxiPNG, {
|
|
worker_initializer,
|
|
start_main_thread,
|
|
optimise,
|
|
} from './pkg-parallel';
|
|
// @ts-ignore
|
|
import wasmUrl from './pkg-parallel/squoosh_oxipng_bg.wasm';
|
|
import { WorkerInit } from './worker';
|
|
|
|
function initWorker(worker: Worker, workerInit: WorkerInit) {
|
|
return new Promise((resolve) => {
|
|
worker.postMessage(workerInit);
|
|
worker.addEventListener('message', () => resolve(), { once: true });
|
|
});
|
|
}
|
|
|
|
async function startMainThread() {
|
|
const num = navigator.hardwareConcurrency;
|
|
|
|
// First, let browser fetch and spawn Workers for our pool in the background.
|
|
// This is fairly expensive, so we want to start it as early as possible.
|
|
const workers = Array.from({ length: num }, () => new Promise<Worker>((resolve) => {
|
|
const w = new Worker('./worker', { type: 'module' });
|
|
w.addEventListener('message', () => resolve(w), { once: true });
|
|
}));
|
|
|
|
// Meanwhile, asynchronously compile, instantiate and initialise Wasm on our main thread.
|
|
await initOxiPNG(fetch(wasmUrl), undefined as any);
|
|
|
|
// Get module+memory from the Wasm instance.
|
|
//
|
|
// Ideally we wouldn't go via Wasm bindings here, since both are just JS variables, but memory is
|
|
// currently not exposed on the Wasm instance correctly by wasm-bindgen.
|
|
const workerInit: WorkerInit = worker_initializer(num);
|
|
|
|
// Once done, we want to send module+memory to each Worker so that they instantiate Wasm too.
|
|
// While doing so, we need to wait for Workers to acknowledge that they have received our message.
|
|
// Ideally this shouldn't be necessary, but Chromium currently doesn't conform to the spec:
|
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=1075645
|
|
//
|
|
// If we didn't do this ping-pong game, the `start_main_thread` below would block the current
|
|
// thread on an atomic before even *sending* the `postMessage` containing memory,
|
|
// so Workers would never be able to unblock us back.
|
|
|
|
// TODO: remove this line.
|
|
// It waits for all workers to be created just to repro a possible V8 bug.
|
|
const resolvedWorkers = await Promise.all(workers);
|
|
|
|
await Promise.all(resolvedWorkers.map(worker => initWorker(worker, workerInit)));
|
|
|
|
// Finally, instantiate rayon pool - this will use shared Wasm memory to send tasks to the
|
|
// Workers and then block until they're all ready.
|
|
start_main_thread();
|
|
|
|
return {
|
|
optimise,
|
|
freeWorkers: () => {
|
|
for (const worker of resolvedWorkers) {
|
|
worker.terminate();
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
export default () => startMainThread();
|