This repository contains a toy project which reproduces an out-of-memory error that seems to happen only but systematically in Safari (macOS and iOS) when compiling C++ code to WASM with Emscripten using the
-pthread
flag.
- The problem seems to occur only on Safari (iOS and macOS).
- The problem seems to occur only if the memory is shared (
pthread
enabled) - The error seems to be related to the
MAXIMUM_MEMORY
value (or to theINITIAL_MEMORY
value ifALLOW_MEMORY_GROWTH
is disabled). - The memory seems to be never deallocated when the page is reloaded and therefore creates an out-of-memory after several reloads.
- The problem seems to be related to the auto-generated Emscripten script (and not Safari), as it does not occur when shared memory is allocated manually (see Preliminary investigations).
You must have emscripten installed and active in your current terminal (see emscripten documentation) and cmake installed (on macOS, brew install cmake
).
At the root of this project, compile the C++ code to WASM:
emcmake cmake -S . -B build
cmake --build build
The output files will be placed in the dist/
folder, next to the index.html
file.
Host the contents of the dist/
folder on a server and run dist/index.html
in Safari.
For instance, to run dist/index.html
on a local web server in Safari with emscripten, run:
emrun dist/index.html --browser safari
dist/
folder must be "cross-origin isolated" using COOP and COEP (see this for more informations) since the code uses SharedArrayBuffer (because of the -pthread
flag). You can check if your website is cross-origin isolated with the crossOriginIsolated variable.
Open the Safari debug console and reload the page several times. After a certain number of reloads (~2 reloads on iOS, ~30 reloads on macOS, it may differ on your machine), the web page crashes with an error RangeError: Out of memory
in the debug console.
This has been tested with the following configurations:
- Desktop:
- Macbook Pro Intel
- macOS Ventura 13.3.1
- Safari 16.4
- Mobile:
- iPhone 12 mini
- iOS 16.4.1
- Safari 16.4
The code has been compiled with Emscripten 1.38 and CMake 3.26.3 for both configurations.
To help debugging, emcc -v
returns:
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.38 (9eff02bc816c50ab0e3b70a3bd5b72a8dc2893a2)
clang version 17.0.0 (https://github.com/llvm/llvm-project 004bf170c6cbaa049601bcf92f86a9459aec2dc2)
Target: wasm32-unknown-emscripten
Thread model: posix
I first encountered this problem only on iOS at first. At that time, I only had the ALLOW_MEMORY_GROWTH
and pthread
options enabled (the INITIAL_MEMORY
and MAXIMUM_MEMORY
values were set by default by Emscripten). The page crashed on its first access (no need to reload the page to get the error).
After some research, I came across this ffmpeg.wasm issue and this emscripten issue which described the same memory problem on iOS. The two issues suggested reducing the MAXIMUM_MEMORY
to get rid of the out-of-memory error on iOS.
And indeed, the first access to the page works. But I found that if I reloaded the page, I still got the same error. I have the impression that the number of reloads to get the error depends on the value of MAXIMUM_MEMORY. The lower it is, the more reloads you have to do before getting the error.
I then tested on Safari macOS to identify if the problem came only from iOS. On macOS, the error also occurs, but after a larger number of reloads, probably because the memory allowed by the browser is larger on Desktop than on Mobile.
At this point, I suspected a bug coming from Safari and not from Emscripten. So I tested to run the line that crashes in the code generated by Emscripten directly in an HTML page:
var INITIAL_MEMORY = 16777216;
wasmMemory = new WebAssembly.Memory({
"initial": INITIAL_MEMORY / 65536,
"maximum": 536870912 / 65536,
"shared": true
});
But this time, I don't get the error after reloading the page. So I suspect that the problem comes from the code generated by Emscripten.
An interesting point is that once the error has occurred, reloading the page produces the error every time. However, if you open the page in another tab, the cycle starts again from the beginning.
- The problem seems to occur only on Safari (iOS and macOS).
- The problem seems to occur only if the memory is shared (
pthread
enabled) - The error seems to be related to the
MAXIMUM_MEMORY
value (or to theINITIAL_MEMORY
value ifALLOW_MEMORY_GROWTH
is disabled). - The memory seems to be never deallocated when the page is reloaded and therefore creates an out-of-memory after several reloads.