diff --git a/codecs/oxipng/src/parallel.rs b/codecs/oxipng/src/parallel.rs index e511f596..e5ced704 100644 --- a/codecs/oxipng/src/parallel.rs +++ b/codecs/oxipng/src/parallel.rs @@ -9,6 +9,32 @@ extern "C" { fn array_of_2(a: JsValue, b: JsValue) -> JsValue; } +// This is one of the parts that work around Chromium incorrectly implementing postMessage: +// https://bugs.chromium.org/p/chromium/issues/detail?id=1075645 +// +// rayon::ThreadPoolBuilder (used below) executes spawn handler to populate the worker pool, +// and then blocks the current thread until each worker unblocks its (opaque) lock. +// +// Normally, we could use postMessage directly inside the spawn handler to +// post module + memory + threadPtr to each worker, and the block the current thread. +// +// However, that bug means that postMessage is currently delayed until the next event loop, +// which will never spin since we block the current thread, and so the other workers will +// never be able to unblock us. +// +// To work around this problem, we: +// 1) Expose `worker_initializer` that returns module + memory pair (without threadPtr) +// that workers can be initialised with to become native threads. +// JavaScript can postMessage this pair in advance, and asynchronously wait for workers +// to acknowledge the receipt. +// 2) Create a global communication channel on the Rust side using crossbeam. +// It will be used to send threadPtr to the pre-initialised workers +// instead of postMessage. +// 3) Provide a separate `start_main_thread` that expects all workers to be ready, +// and just uses the provided channel to send `threadPtr`s using the +// shared memory and blocks the current thread until they're all grabbed. +// 4) Provide a `worker_initializer` that is expected to be invoked from various workers, +// reads one `threadPtr` from the shared channel and starts running it. static CHANNEL: OnceCell<(Sender, Receiver)> = OnceCell::new(); #[wasm_bindgen]