Skip to content

Commit 452fcc4

Browse files
authored
Add exception handling for new operator (#791)
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 PR 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 b49a42d commit 452fcc4

File tree

5 files changed

+190
-79
lines changed

5 files changed

+190
-79
lines changed

CMakeLists.txt

Lines changed: 48 additions & 39 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>
@@ -503,29 +503,29 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY)
503503
message(WARNING "Compiler does not support `-march=native` required by SNMALLOC_OPTIMISE_FOR_CURRENT_MACHINE")
504504
set(SNMALLOC_OPTIMISE_FOR_CURRENT_MACHINE FALSE)
505505
endif()
506-
endif()
506+
endif()
507507

508508

509-
function(add_shim name type)
510-
add_library(${name} ${type} ${ARGN})
509+
function(compile name TYPE ${ARGN})
510+
add_library(${name} ${TYPE} ${ARGN})
511511
target_link_libraries(${name} snmalloc)
512-
set_target_properties(${name} PROPERTIES CXX_VISIBILITY_PRESET hidden INTERPROCEDURAL_OPTIMIZATION ${SNMALLOC_COMPILER_SUPPORT_IPO})
513512
target_compile_definitions(${name} PRIVATE "SNMALLOC_USE_${SNMALLOC_CLEANUP}")
514513

515-
if(MSVC)
516-
target_compile_definitions(${name} PRIVATE -D_HAS_EXCEPTIONS=0)
517-
endif()
518-
519514
add_warning_flags(${name})
520515
if(NOT MSVC)
521516
target_compile_definitions(${name} PRIVATE "SNMALLOC_EXPORT=__attribute__((visibility(\"default\")))")
522-
target_compile_options(${name} PRIVATE
523-
-fomit-frame-pointer -ffunction-sections)
517+
set_target_properties(${name} PROPERTIES CXX_VISIBILITY_PRESET hidden INTERPROCEDURAL_OPTIMIZATION ${SNMALLOC_COMPILER_SUPPORT_IPO})
518+
# Make debugging harder, and code faster.
519+
target_compile_options(${name} PRIVATE -fomit-frame-pointer)
520+
521+
set_property(TARGET ${name} PROPERTY POSITION_INDEPENDENT_CODE ON)
524522

523+
# Check for prefetch write support.
525524
check_cxx_compiler_flag("-Werror -Wextra -Wall -mprfchw" SUPPORT_PREFETCH_WRITE)
526525
if (SUPPORT_PREFETCH_WRITE)
527526
target_compile_options(${name} PRIVATE -mprfchw)
528527
endif()
528+
529529
# Static TLS model is unsupported on Haiku.
530530
if (SNMALLOC_STATIC_MODE_TLS)
531531
target_compile_options(${name} PRIVATE -ftls-model=initial-exec)
@@ -536,18 +536,8 @@ endif()
536536
target_compile_options(${name} PRIVATE -march=native)
537537
endif()
538538

539-
# Ensure that we do not link against C++ stdlib when compiling shims.
540-
# If the compiler supports excluding the C++ stdlib implementation, use
541-
# it. Otherwise, fall back to linking the library as if it were C, which
542-
# 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()
550539
# Remove all the duplicate new/malloc and free/delete definitions
540+
target_compile_options(${name} PRIVATE -ffunction-sections)
551541
target_link_options(${name} PRIVATE $<$<BOOL:${LLD_WORKS}>:$<$<BOOL:${SNMALLOC_LINK_ICF}>:-Wl,--icf=all> -fuse-ld=lld>)
552542
endif()
553543

@@ -556,40 +546,59 @@ endif()
556546
target_compile_definitions(${name} PRIVATE
557547
SNMALLOC_PAGEID=$<IF:$<BOOL:${SNMALLOC_PAGEID}>,true,false>)
558548

559-
install(TARGETS ${name} EXPORT snmallocConfig)
549+
if(MSVC)
550+
else()
551+
# We can't do --nostdlib++ as we are using exceptions, but we
552+
# can make it a static dependency, which we aren't really using
553+
# as the interesting stuff for exceptions is in libgcc_s
554+
target_link_options(${name} PRIVATE -static-libstdc++)
555+
endif()
560556

557+
if (SNMALLOC_RUST_LIBC_API)
558+
target_compile_definitions(${name} PRIVATE SNMALLOC_RUST_LIBC_API)
559+
endif()
560+
install(TARGETS ${name} EXPORT snmallocConfig)
561561
endfunction()
562562

563-
set(SHIM_FILES src/snmalloc/override/malloc.cc src/snmalloc/override/new.cc)
564-
set(SHIM_FILES_MEMCPY src/snmalloc/override/memcpy.cc)
563+
# Various files for overriding libc/rust behaviours.
564+
set(MALLOC src/snmalloc/override/malloc.cc)
565+
set(NEW src/snmalloc/override/new.cc)
566+
set(MEMCPY src/snmalloc/override/memcpy.cc)
567+
set(RUST src/snmalloc/override/rust.cc)
565568

566-
add_shim(snmalloc-new-override STATIC src/snmalloc/override/new.cc)
569+
set(ALLOC ${MALLOC} ${NEW})
570+
set(ALL ${ALLOC} ${MEMCPY})
567571

568572
if (SNMALLOC_STATIC_LIBRARY)
569-
add_shim(snmallocshim-static STATIC ${SHIM_FILES})
573+
compile(snmallocshim-static STATIC ${ALLOC})
570574
target_compile_definitions(snmallocshim-static PRIVATE
571575
SNMALLOC_STATIC_LIBRARY_PREFIX=${SNMALLOC_STATIC_LIBRARY_PREFIX})
572576
endif ()
573577

578+
compile(snmalloc-new-override STATIC ${NEW})
579+
574580
if(NOT WIN32)
575-
add_shim(snmallocshim SHARED ${SHIM_FILES})
581+
compile(snmallocshim SHARED ${ALLOC})
582+
576583
if (SNMALLOC_MEMCPY_OVERRIDE)
577-
add_shim(snmallocshim-checks-memcpy-only SHARED ${SHIM_FILES} ${SHIM_FILES_MEMCPY})
578-
add_shim(snmallocshim-checks SHARED ${SHIM_FILES} ${SHIM_FILES_MEMCPY})
584+
compile(snmallocshim-checks-memcpy-only SHARED ${ALL})
585+
compile(snmallocshim-checks SHARED ${ALL})
579586
else()
580-
add_shim(snmallocshim-checks SHARED ${SHIM_FILES})
587+
compile(snmallocshim-checks SHARED ${ALLOC})
581588
endif()
582589
target_compile_definitions(snmallocshim-checks PRIVATE SNMALLOC_CHECK_CLIENT)
590+
591+
compile(snmalloc-minimal SHARED ${MALLOC})
592+
target_compile_options(snmalloc-minimal PRIVATE -fno-exceptions)
593+
if (SNMALLOC_LINKER_SUPPORT_NOSTDLIBXX AND NOT ${SNMALLOC_CLEANUP} STREQUAL CXX11_DESTRUCTORS)
594+
target_compile_options(snmalloc-minimal PRIVATE -fno-exceptions -nostdlib++)
595+
endif ()
583596
endif()
584597

585598
if(SNMALLOC_RUST_SUPPORT)
586-
add_shim(snmallocshim-rust STATIC src/snmalloc/override/rust.cc)
587-
add_shim(snmallocshim-checks-rust STATIC src/snmalloc/override/rust.cc)
599+
compile(snmallocshim-rust STATIC ${RUST})
600+
compile(snmallocshim-checks-rust STATIC ${RUST})
588601
target_compile_definitions(snmallocshim-checks-rust PRIVATE SNMALLOC_CHECK_CLIENT)
589-
if (SNMALLOC_RUST_LIBC_API)
590-
target_compile_definitions(snmallocshim-rust PRIVATE SNMALLOC_RUST_LIBC_API)
591-
target_compile_definitions(snmallocshim-checks-rust PRIVATE SNMALLOC_RUST_LIBC_API)
592-
endif()
593602
endif()
594603

595604
if (SNMALLOC_BUILD_TESTING)
@@ -625,7 +634,7 @@ endif()
625634

626635
foreach (MITIGATION ${MITIGATIONS})
627636
set(DEFINES "SNMALLOC_CHECK_CLIENT_MITIGATIONS=${MITIGATION}")
628-
add_shim(snmallocshim-${MITIGATION} SHARED ${SHIM_FILES})
637+
compile(snmallocshim-${MITIGATION} SHARED ${ALLOC})
629638
target_compile_definitions(snmallocshim-${MITIGATION} PRIVATE ${DEFINES})
630639
if (SNMALLOC_BUILD_TESTING)
631640
make_tests(${MITIGATION} ${DEFINES})
@@ -640,7 +649,7 @@ endif()
640649
set(MITIGATIONSET "${MITIGATIONSET}+${MITIGATION}")
641650
message(STATUS "MITIGATIONSET: ${COUNT} -> ${MITIGATIONSET}")
642651
set(DEFINES "-DSNMALLOC_CHECK_CLIENT_MITIGATIONS=${MITIGATIONSET}")
643-
add_shim(snmallocshim-${MITIGATIONNAME} SHARED ${SHIM_FILES})
652+
compile(snmallocshim-${MITIGATIONNAME} SHARED ${ALLOC})
644653
target_compile_definitions(snmallocshim-${MITIGATIONNAME} PRIVATE ${DEFINES})
645654
if (SNMALLOC_BUILD_TESTING)
646655
make_tests(${MITIGATIONNAME} ${DEFINES})

src/snmalloc/mem/corealloc.h

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ namespace snmalloc
5050
return p;
5151
}
5252

53-
static void* failure(size_t size)
53+
static void* failure(size_t size) noexcept
5454
{
5555
UNUSED(size);
5656
// If we are here, then the allocation failed.
@@ -401,25 +401,25 @@ namespace snmalloc
401401
* the stack as often closing over the arguments would cause less good
402402
* codegen.
403403
*/
404-
template<typename Action, typename... Args>
404+
template<bool noexc, typename Action, typename... Args>
405405
SNMALLOC_FAST_PATH decltype(auto)
406-
handle_message_queue(Action action, Args... args)
406+
handle_message_queue(Action action, Args... args) noexcept(noexc)
407407
{
408408
// Inline the empty check, but not necessarily the full queue handling.
409409
if (SNMALLOC_LIKELY(!has_messages()))
410410
{
411411
return action(args...);
412412
}
413413

414-
return handle_message_queue_slow(action, args...);
414+
return handle_message_queue_slow<noexc>(action, args...);
415415
}
416416

417417
/**
418418
* Process remote frees into this allocator.
419419
*/
420-
template<typename Action, typename... Args>
420+
template<bool noexc, typename Action, typename... Args>
421421
SNMALLOC_SLOW_PATH decltype(auto)
422-
handle_message_queue_slow(Action action, Args... args)
422+
handle_message_queue_slow(Action action, Args... args) noexcept(noexc)
423423
{
424424
bool need_post = false;
425425
size_t bytes_freed = 0;
@@ -602,7 +602,8 @@ namespace snmalloc
602602
* required. It is defaulted to do nothing.
603603
*/
604604
template<typename Conts = Uninit, typename CheckInit = CheckInitNoOp>
605-
SNMALLOC_FAST_PATH ALLOCATOR void* alloc(size_t size)
605+
SNMALLOC_FAST_PATH ALLOCATOR void*
606+
alloc(size_t size) noexcept(noexcept(Conts::failure(0)))
606607
{
607608
// Perform the - 1 on size, so that zero wraps around and ends up on
608609
// slow path.
@@ -621,7 +622,8 @@ namespace snmalloc
621622
* Fast allocation for small objects.
622623
*/
623624
template<typename Conts, typename CheckInit>
624-
SNMALLOC_FAST_PATH void* small_alloc(size_t size)
625+
SNMALLOC_FAST_PATH void*
626+
small_alloc(size_t size) noexcept(noexcept(Conts::failure(0)))
625627
{
626628
auto domesticate =
627629
[this](freelist::QueuePtr p) SNMALLOC_FAST_PATH_LAMBDA {
@@ -637,7 +639,7 @@ namespace snmalloc
637639
return finish_alloc<Conts>(p, size);
638640
}
639641

640-
return handle_message_queue(
642+
return handle_message_queue<noexcept(Conts::failure(0))>(
641643
[](
642644
Allocator* alloc,
643645
smallsizeclass_t sizeclass,
@@ -661,8 +663,8 @@ namespace snmalloc
661663
* register.
662664
*/
663665
template<typename Conts, typename CheckInit>
664-
static SNMALLOC_SLOW_PATH void*
665-
alloc_not_small(size_t size, Allocator* self)
666+
static SNMALLOC_SLOW_PATH void* alloc_not_small(
667+
size_t size, Allocator* self) noexcept(noexcept(Conts::failure(0)))
666668
{
667669
if (size == 0)
668670
{
@@ -672,7 +674,7 @@ namespace snmalloc
672674
return self->small_alloc<Conts, CheckInit>(1);
673675
}
674676

675-
return self->handle_message_queue(
677+
return self->template handle_message_queue<noexcept(Conts::failure(0))>(
676678
[](Allocator* self, size_t size) SNMALLOC_FAST_PATH_LAMBDA {
677679
return CheckInit::check_init(
678680
[self, size]() SNMALLOC_FAST_PATH_LAMBDA {
@@ -739,7 +741,9 @@ namespace snmalloc
739741

740742
template<typename Conts, typename CheckInit>
741743
SNMALLOC_FAST_PATH void* small_refill(
742-
smallsizeclass_t sizeclass, freelist::Iter<>& fast_free_list, size_t size)
744+
smallsizeclass_t sizeclass,
745+
freelist::Iter<>& fast_free_list,
746+
size_t size) noexcept(noexcept(Conts::failure(0)))
743747
{
744748
void* result = Config::SecondaryAllocator::allocate(
745749
[size]() -> stl::Pair<size_t, size_t> {
@@ -809,7 +813,9 @@ namespace snmalloc
809813

810814
template<typename Conts, typename CheckInit>
811815
SNMALLOC_SLOW_PATH void* small_refill_slow(
812-
smallsizeclass_t sizeclass, freelist::Iter<>& fast_free_list, size_t size)
816+
smallsizeclass_t sizeclass,
817+
freelist::Iter<>& fast_free_list,
818+
size_t size) noexcept(noexcept(Conts::failure(0)))
813819
{
814820
return CheckInit::check_init(
815821
[this, size, sizeclass, &fast_free_list]() SNMALLOC_FAST_PATH_LAMBDA {
@@ -994,7 +1000,7 @@ namespace snmalloc
9941000
* deallocation to the other allocators.
9951001
***************************************************************************/
9961002
template<typename CheckInit = CheckInitNoOp>
997-
SNMALLOC_FAST_PATH void dealloc(void* p_raw)
1003+
SNMALLOC_FAST_PATH void dealloc(void* p_raw) noexcept
9981004
{
9991005
#ifdef __CHERI_PURE_CAPABILITY__
10001006
/*
@@ -1043,7 +1049,7 @@ namespace snmalloc
10431049

10441050
SNMALLOC_FAST_PATH void dealloc_local_object(
10451051
CapPtr<void, capptr::bounds::Alloc> p,
1046-
const typename Config::PagemapEntry& entry)
1052+
const typename Config::PagemapEntry& entry) noexcept
10471053
{
10481054
auto meta = entry.get_slab_metadata();
10491055

@@ -1078,7 +1084,7 @@ namespace snmalloc
10781084
SNMALLOC_SLOW_PATH void dealloc_local_object_slow(
10791085
capptr::Alloc<void> p,
10801086
const PagemapEntry& entry,
1081-
BackendSlabMetadata* meta)
1087+
BackendSlabMetadata* meta) noexcept
10821088
{
10831089
// TODO: Handle message queue on this path?
10841090

@@ -1273,8 +1279,8 @@ namespace snmalloc
12731279
}
12741280

12751281
template<typename CheckInit>
1276-
SNMALLOC_FAST_PATH void
1277-
dealloc_remote(const PagemapEntry& entry, capptr::Alloc<void> p_tame)
1282+
SNMALLOC_FAST_PATH void dealloc_remote(
1283+
const PagemapEntry& entry, capptr::Alloc<void> p_tame) noexcept
12781284
{
12791285
if (SNMALLOC_LIKELY(entry.is_owned()))
12801286
{
@@ -1327,8 +1333,8 @@ namespace snmalloc
13271333
* as we might acquire the originating allocator.
13281334
*/
13291335
template<typename CheckInit>
1330-
SNMALLOC_SLOW_PATH void
1331-
dealloc_remote_slow(const PagemapEntry& entry, capptr::Alloc<void> p)
1336+
SNMALLOC_SLOW_PATH void dealloc_remote_slow(
1337+
const PagemapEntry& entry, capptr::Alloc<void> p) noexcept
13321338
{
13331339
CheckInit::check_init(
13341340
[this, &entry, p]() SNMALLOC_FAST_PATH_LAMBDA {
@@ -1344,7 +1350,7 @@ namespace snmalloc
13441350

13451351
post();
13461352
},
1347-
[](Allocator* a, void* p) {
1353+
[](Allocator* a, void* p) SNMALLOC_FAST_PATH_LAMBDA {
13481354
// Recheck what kind of dealloc we should do in case the allocator
13491355
// we get from lazy_init is the originating allocator.
13501356
a->dealloc(p); // TODO don't double count statistics
@@ -1385,7 +1391,7 @@ namespace snmalloc
13851391
// Process incoming message queue
13861392
// Loop as normally only processes a batch
13871393
while (has_messages())
1388-
handle_message_queue([]() {});
1394+
handle_message_queue<true>([]() {});
13891395
}
13901396

13911397
auto& key = freelist::Object::key_root;

src/snmalloc/mem/ticker.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ namespace snmalloc
4141
* how many calls for the next time we hit the slow path.
4242
*/
4343
template<typename T = void*>
44-
SNMALLOC_SLOW_PATH T check_tick_slow(T p = nullptr)
44+
SNMALLOC_SLOW_PATH T check_tick_slow(T p = nullptr) noexcept
4545
{
4646
uint64_t now_ms = PAL::time_in_ms();
4747

0 commit comments

Comments
 (0)