Description
I'm currently porting some code that uses std::nothrow
for allocations. However, if I build without exception support, the application aborts if the allocation fails instead of returning 0.
The issue can be reproduced with the following code:
#include <iostream>
#include <new>
int main() {
uint8_t *a = new (std::nothrow) uint8_t[(unsigned long)-518344287];
std::cout << "Result: " << (size_t)a << std::endl;
return 1;
}
Build with:
em++ main.cpp -o index.html -sALLOW_MEMORY_GROWTH
If I run this in the browser I get:
Cannot enlarge memory, asked to go up to 3776712704 bytes, but the limit is 2147483648 bytes! [index.js:1116:12](http://localhost:8000/index.js)
Cannot enlarge memory, asked to go up to 3776716800 bytes, but the limit is 2147483648 bytes! [index.js:1116:12](http://localhost:8000/index.js)
Aborted(native code called abort()) [index.js:656:6](http://localhost:8000/index.js)
Uncaught RuntimeError: Aborted(native code called abort())
abort http://localhost:8000/index.js:675
_abort http://localhost:8000/index.js:1061
createExportWrapper http://localhost:8000/index.js:705
callMain http://localhost:8000/index.js:4925
doRun http://localhost:8000/index.js:4975
run http://localhost:8000/index.js:4986
setTimeout handler*run http://localhost:8000/index.js:4982
runCaller http://localhost:8000/index.js:4910
removeRunDependency http://localhost:8000/index.js:642
receiveInstance http://localhost:8000/index.js:842
receiveInstantiationResult http://localhost:8000/index.js:860
promise callback*instantiateAsync/< http://localhost:8000/index.js:795
promise callback*instantiateAsync http://localhost:8000/index.js:787
createWasm http://localhost:8000/index.js:879
<anonymous> http://localhost:8000/index.js:4595
If I build with -fexception
the code works as expected, i.e. the "Cannot enlarge memory" errors are still there but the program doesn't abort and prints "Result: 0".
I think the problem is in system/lib/libcxx/src/new.cpp: the operator new(size_t size, const std::nothrow_t&)
just calls operator new(std::size_t size)
internally which always aborts in case of failure.
I agree with the reasons for abort
in #11042 for the current behaviour of operator new(std::size_t size)
. However, I think the std::nothrow
variant aborting is not correct. Without exceptions enabled, this is the only method to check whether an allocation failed. Also code that uses this should be aware that it might return 0.
Version of emscripten/emsdk:
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.44 (bec42dac7873903d09d713963e34020c22a8b
d2d)
clang version 17.0.0 (https://github.com/llvm/llvm-project a8cbd27d1f238e104a5d5ca345d93bc1f4d4ab1f)
Target: wasm32-unknown-emscripten
Thread model: posix