Skip to content

Commit ffe4765

Browse files
committed
Add exception handling for new operator
The new operator in the snmalloc did not throw exceptions in the case of allocation failure. Moreover, it was not possible to override the behaviour of the failure of the new operator using the std::set_new_handler function. This commit adds the necessary code to the snmalloc new operator to throw std::bad_alloc when the allocation fails. It also allows the std::set_new_handler function to be used to set a custom handler for allocation failures.
1 parent d4c7e01 commit ffe4765

File tree

3 files changed

+102
-18
lines changed

3 files changed

+102
-18
lines changed

CMakeLists.txt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ endif()
379379
function(add_warning_flags name)
380380
target_compile_options(${name} PRIVATE
381381
$<$<CXX_COMPILER_ID:MSVC>:/Zi /W4 /WX /wd4127 /wd4324 /wd4201>
382-
$<$<NOT:$<OR:$<CXX_COMPILER_ID:MSVC>,$<STREQUAL:${CMAKE_CXX_SIMULATE_ID},MSVC>>>:-fno-exceptions -fno-rtti -Wall -Wextra -Werror -Wundef>
382+
$<$<NOT:$<OR:$<CXX_COMPILER_ID:MSVC>,$<STREQUAL:${CMAKE_CXX_SIMULATE_ID},MSVC>>>:-fno-rtti -Wall -Wextra -Werror -Wundef>
383383
$<$<CXX_COMPILER_ID:Clang>:-Wsign-conversion -Wconversion>)
384384
target_link_options(${name} PRIVATE
385385
$<$<BOOL:${SNMALLOC_LINKER_SUPPORT_NO_ALLOW_SHLIB_UNDEF}>:-Wl,--no-undefined>
@@ -540,13 +540,13 @@ endif()
540540
# If the compiler supports excluding the C++ stdlib implementation, use
541541
# it. Otherwise, fall back to linking the library as if it were C, which
542542
# has roughly the same effect.
543-
if (NOT ${SNMALLOC_CLEANUP} STREQUAL CXX11_DESTRUCTORS)
544-
if (SNMALLOC_LINKER_SUPPORT_NOSTDLIBXX)
545-
target_link_options(${name} PRIVATE -nostdlib++)
546-
else()
547-
set_target_properties(${name} PROPERTIES LINKER_LANGUAGE C)
548-
endif()
549-
endif()
543+
# if (NOT ${SNMALLOC_CLEANUP} STREQUAL CXX11_DESTRUCTORS)
544+
# if (SNMALLOC_LINKER_SUPPORT_NOSTDLIBXX)
545+
# target_link_options(${name} PRIVATE -nostdlib++)
546+
# else()
547+
# set_target_properties(${name} PROPERTIES LINKER_LANGUAGE C)
548+
# endif()
549+
# endif()
550550
# Remove all the duplicate new/malloc and free/delete definitions
551551
target_link_options(${name} PRIVATE $<$<BOOL:${LLD_WORKS}>:$<$<BOOL:${SNMALLOC_LINK_ICF}>:-Wl,--icf=all> -fuse-ld=lld>)
552552
endif()

src/snmalloc/override/new.cc

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,90 @@
2020

2121
// only define these if we are not using the vendored STL
2222
#ifndef SNMALLOC_USE_SELF_VENDORED_STL
23+
24+
namespace snmalloc
25+
{
26+
template<bool ShouldThrow = true>
27+
class SetHandlerContinuations
28+
{
29+
public:
30+
static void* success(void* p, size_t size, bool secondary_allocator = false)
31+
{
32+
UNUSED(secondary_allocator, size);
33+
SNMALLOC_ASSERT(p != nullptr);
34+
35+
SNMALLOC_ASSERT(
36+
secondary_allocator ||
37+
is_start_of_object(size_to_sizeclass_full(size), address_cast(p)));
38+
39+
return p;
40+
}
41+
42+
static void* failure(size_t size)
43+
{
44+
UNUSED(size);
45+
46+
auto new_handler = std::get_new_handler();
47+
if (new_handler != nullptr)
48+
{
49+
if constexpr (ShouldThrow)
50+
{
51+
try
52+
{
53+
// Call the new handler, which may throw an exception.
54+
new_handler();
55+
}
56+
catch (...)
57+
{
58+
// If the new handler throws, we just return nullptr.
59+
return nullptr;
60+
}
61+
}
62+
// Retry now new_handler has been called.
63+
// I dislike the unbounded retrying here, but that seems to be what
64+
// other implementations do.
65+
return snmalloc::alloc<SetHandlerContinuations<ShouldThrow>>(size);
66+
}
67+
68+
if constexpr (ShouldThrow)
69+
{
70+
// Throw std::bad_alloc on failure.
71+
throw std::bad_alloc();
72+
}
73+
else
74+
{
75+
// If we are here, then the allocation failed.
76+
// Set errno to ENOMEM, as per the C standard.
77+
errno = ENOMEM;
78+
79+
// Return nullptr on failure.
80+
return nullptr;
81+
}
82+
}
83+
};
84+
85+
using NoThrow = SetHandlerContinuations<false>;
86+
using Throw = SetHandlerContinuations<true>;
87+
} // namespace snmalloc
88+
2389
void* operator new(size_t size)
2490
{
25-
return snmalloc::libc::malloc(size);
91+
return snmalloc::alloc<snmalloc::Throw>(size);
2692
}
2793

2894
void* operator new[](size_t size)
2995
{
30-
return snmalloc::libc::malloc(size);
96+
return snmalloc::alloc<snmalloc::Throw>(size);
3197
}
3298

3399
void* operator new(size_t size, std::nothrow_t&)
34100
{
35-
return snmalloc::libc::malloc(size);
101+
return snmalloc::alloc<snmalloc::NoThrow>(size);
36102
}
37103

38104
void* operator new[](size_t size, std::nothrow_t&)
39105
{
40-
return snmalloc::libc::malloc(size);
106+
return snmalloc::alloc<snmalloc::NoThrow>(size);
41107
}
42108

43109
void operator delete(void* p) EXCEPTSPEC
@@ -73,25 +139,25 @@ void operator delete[](void* p, std::nothrow_t&)
73139
void* operator new(size_t size, std::align_val_t val)
74140
{
75141
size = snmalloc::aligned_size(size_t(val), size);
76-
return snmalloc::libc::malloc(size);
142+
return snmalloc::alloc<snmalloc::Throw>(size);
77143
}
78144

79145
void* operator new[](size_t size, std::align_val_t val)
80146
{
81147
size = snmalloc::aligned_size(size_t(val), size);
82-
return snmalloc::libc::malloc(size);
148+
return snmalloc::alloc<snmalloc::Throw>(size);
83149
}
84150

85151
void* operator new(size_t size, std::align_val_t val, std::nothrow_t&)
86152
{
87153
size = snmalloc::aligned_size(size_t(val), size);
88-
return snmalloc::libc::malloc(size);
154+
return snmalloc::alloc<snmalloc::NoThrow>(size);
89155
}
90156

91157
void* operator new[](size_t size, std::align_val_t val, std::nothrow_t&)
92158
{
93159
size = snmalloc::aligned_size(size_t(val), size);
94-
return snmalloc::libc::malloc(size);
160+
return snmalloc::alloc<snmalloc::NoThrow>(size);
95161
}
96162

97163
void operator delete(void* p, std::align_val_t) EXCEPTSPEC

src/test/func/miracle_ptr/miracle_ptr.cc

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,27 @@ int main()
1818
# include <atomic>
1919
# include <iostream>
2020
# include <memory>
21+
# include <new>
2122
# include <snmalloc/backend/globalconfig.h>
2223
# include <snmalloc/snmalloc_core.h>
2324

25+
// This code makes the delete overloads build with the correct noexcept spec.
26+
# ifdef _WIN32
27+
# ifdef __clang__
28+
# define EXCEPTSPEC noexcept
29+
# else
30+
# define EXCEPTSPEC
31+
# endif
32+
# else
33+
# ifdef _GLIBCXX_USE_NOEXCEPT
34+
# define EXCEPTSPEC _GLIBCXX_USE_NOEXCEPT
35+
# elif defined(_NOEXCEPT)
36+
# define EXCEPTSPEC _NOEXCEPT
37+
# else
38+
# define EXCEPTSPEC
39+
# endif
40+
# endif
41+
2442
namespace snmalloc
2543
{
2644
// Instantiate the allocator with a client meta data provider that uses an
@@ -170,12 +188,12 @@ void* operator new(size_t size)
170188
return snmalloc::miracle::malloc(size);
171189
}
172190

173-
void operator delete(void* p)
191+
void operator delete(void* p) EXCEPTSPEC
174192
{
175193
snmalloc::miracle::free(p);
176194
}
177195

178-
void operator delete(void* p, size_t)
196+
void operator delete(void* p, size_t) EXCEPTSPEC
179197
{
180198
snmalloc::miracle::free(p);
181199
}

0 commit comments

Comments
 (0)