Skip to content

Commit bf71db3

Browse files
authored
Fix std::nothrow aborting when exceptions are disabled (#20154)
1 parent 054bf30 commit bf71db3

File tree

6 files changed

+69
-0
lines changed

6 files changed

+69
-0
lines changed

ChangeLog.md

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ See docs/process.md for more on how version tagging works.
2424
developers and helps take a care of post-checkout tasks such as `npm install`.
2525
If this script needs to be run (e.g. becuase package.json was changed, emcc
2626
will exit with an error. (#19736)
27+
- If exceptions are disabled, using `new` together with `std::nothrow` no
28+
longer aborts if the allocation fails. Instead `nullptr` is returned now.
29+
This does not change the behavior of regular usage of `new`.
2730

2831
3.1.47 - 10/09/23
2932
-----------------

src/settings.js

+4
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ var MALLOC = "dlmalloc";
140140
// which it did not previously. If you don't want that, just stop passing
141141
// it in at link time.
142142
//
143+
// Note that this setting does not affect the behavior of operator new in C++.
144+
// This function will always abort on allocation failure if exceptions are disabled.
145+
// If you want new to return 0 on failure, use it with std::nothrow.
146+
//
143147
// [link]
144148
var ABORTING_MALLOC = true;
145149

system/lib/libcxx/src/new.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,34 @@ operator new(std::size_t size) _THROW_BAD_ALLOC
9090
return p;
9191
}
9292

93+
#if defined(__EMSCRIPTEN__) && defined(_LIBCPP_NO_EXCEPTIONS)
94+
void* _new_nothrow(size_t size) noexcept
95+
{
96+
/// We cannot call ::operator new(size) here because it would abort
97+
/// when malloc returns 0 and exceptions are disabled.
98+
/// Expected behaviour of std::nothrow is to return 0 in that case.
99+
void* p = nullptr;
100+
if (size == 0)
101+
size = 1;
102+
while ((p = ::malloc(size)) == nullptr)
103+
{
104+
std::new_handler nh = std::get_new_handler();
105+
if (nh)
106+
nh();
107+
else
108+
break;
109+
}
110+
return p;
111+
}
112+
#endif
113+
93114
_LIBCPP_WEAK
94115
void*
95116
operator new(size_t size, const std::nothrow_t&) noexcept
96117
{
118+
#if defined(__EMSCRIPTEN__) && defined(_LIBCPP_NO_EXCEPTIONS)
119+
return _new_nothrow(size);
120+
#else
97121
void* p = nullptr;
98122
#ifndef _LIBCPP_NO_EXCEPTIONS
99123
try
@@ -107,6 +131,7 @@ operator new(size_t size, const std::nothrow_t&) noexcept
107131
}
108132
#endif // _LIBCPP_NO_EXCEPTIONS
109133
return p;
134+
#endif // __EMSCRIPTEN__ && _LIBCPP_NO_EXCEPTIONS
110135
}
111136

112137
_LIBCPP_WEAK
@@ -120,6 +145,9 @@ _LIBCPP_WEAK
120145
void*
121146
operator new[](size_t size, const std::nothrow_t&) noexcept
122147
{
148+
#if defined(__EMSCRIPTEN__) && defined(_LIBCPP_NO_EXCEPTIONS)
149+
return _new_nothrow(size);
150+
#else
123151
void* p = nullptr;
124152
#ifndef _LIBCPP_NO_EXCEPTIONS
125153
try
@@ -133,6 +161,7 @@ operator new[](size_t size, const std::nothrow_t&) noexcept
133161
}
134162
#endif // _LIBCPP_NO_EXCEPTIONS
135163
return p;
164+
#endif // __EMSCRIPTEN__ && _LIBCPP_NO_EXCEPTIONS
136165
}
137166

138167
_LIBCPP_WEAK

test/core/test_nothrow_new.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2023 The Emscripten Authors. All rights reserved.
2+
// Emscripten is available under two separate licenses, the MIT license and the
3+
// University of Illinois/NCSA Open Source License. Both these licenses can be
4+
// found in the LICENSE file.
5+
6+
#include <iostream>
7+
#include <new>
8+
9+
char* data;
10+
11+
int main() {
12+
data = new (std::nothrow) char[20 * 1024 * 1024];
13+
if (data == nullptr) {
14+
std::cout << "success" << std::endl;
15+
return 0;
16+
} else {
17+
std::cout << "failure" << std::endl;
18+
return 1;
19+
}
20+
}

test/core/test_nothrow_new.out

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
success

test/test_core.py

+12
Original file line numberDiff line numberDiff line change
@@ -2440,6 +2440,18 @@ def test_aborting_new(self, args):
24402440
self.emcc_args += args
24412441
self.do_core_test('test_aborting_new.cpp')
24422442

2443+
@parameterized({
2444+
'nogrow': (['-sABORTING_MALLOC=0'],),
2445+
'grow': (['-sABORTING_MALLOC=0', '-sALLOW_MEMORY_GROWTH', '-sMAXIMUM_MEMORY=18MB'],)
2446+
})
2447+
@no_asan('requires more memory when growing')
2448+
@no_lsan('requires more memory when growing')
2449+
@no_4gb('depends on MAXIMUM_MEMORY')
2450+
@no_2gb('depends on MAXIMUM_MEMORY')
2451+
def test_nothrow_new(self, args):
2452+
self.emcc_args += args
2453+
self.do_core_test('test_nothrow_new.cpp')
2454+
24432455
@no_wasm2js('no WebAssembly.Memory()')
24442456
@no_asan('ASan alters the memory size')
24452457
@no_lsan('LSan alters the memory size')

0 commit comments

Comments
 (0)