GitHunt
LA

laurmaedje/safari-worker-bug

Safari WebAssembly Worker CPU spin bug

Safari WebAssembly Worker CPU spin bug

Start with this:

wat2wasm --enable-threads bug.wat
npx serve

Then, open the page in Safari and click "Terminate". Observe CPU usage of the tab going to 100%. The CPU usage remains at 100% even if the tab is reloaded. Clicking "Terminate" again in the reloaded tab brings CPU usage to 200%. The resources are only released once the tab is fully closed.

This only happens in Safari, not Chrome or Firefox.

What happens?

The page starts a web worker, which loads WebAssembly with shared memory, and runs an export from the WebAssembly. This export never yields back to the event loop. Rather, it goes into a loop where it memory.atomic.wait32s for a memory location to change (which never changes).

During normal worker operation, this wait simply suspends and does not cause any CPU usage. However, as soon as the worker is killed via worker.terminate(), CPU usage goes all the way to 100%.

I can't say for sure what happens, but my best guess is that worker.terminate() doesn't correctly shut down the non-yielding WebAssembly, but does release enough resources for the memory wait operation to not work correctly anymore, making it return immediately instead, effectively making the WASM go into a spin loop.

In my view, the bug here is two-layered: The fact that the atomic instruction does not work correctly anymore is the symptom that causes high CPU usage, but the core problem is that neither worker.terminate() nor a page reload are able to kill a non-yielding WebAssembly function.

Languages

JavaScript53.9%WebAssembly33.2%HTML12.9%

Contributors

MIT License
Created March 13, 2025
Updated March 13, 2025
laurmaedje/safari-worker-bug | GitHunt