From a3f758a3d1c846fef3d22097a3335aa1db444c3f Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 4 Jul 2025 13:35:46 +0100 Subject: [PATCH 01/22] 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. --- CMakeLists.txt | 16 ++--- src/snmalloc/override/new.cc | 82 +++++++++++++++++++++--- src/test/func/miracle_ptr/miracle_ptr.cc | 22 ++++++- 3 files changed, 102 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 392b53e80..0c37a0b28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -379,7 +379,7 @@ endif() function(add_warning_flags name) target_compile_options(${name} PRIVATE $<$:/Zi /W4 /WX /wd4127 /wd4324 /wd4201> - $<$,$>>:-fno-exceptions -fno-rtti -Wall -Wextra -Werror -Wundef> + $<$,$>>:-fno-rtti -Wall -Wextra -Werror -Wundef> $<$:-Wsign-conversion -Wconversion>) target_link_options(${name} PRIVATE $<$:-Wl,--no-undefined> @@ -540,13 +540,13 @@ endif() # If the compiler supports excluding the C++ stdlib implementation, use # it. Otherwise, fall back to linking the library as if it were C, which # has roughly the same effect. - if (NOT ${SNMALLOC_CLEANUP} STREQUAL CXX11_DESTRUCTORS) - if (SNMALLOC_LINKER_SUPPORT_NOSTDLIBXX) - target_link_options(${name} PRIVATE -nostdlib++) - else() - set_target_properties(${name} PROPERTIES LINKER_LANGUAGE C) - endif() - endif() + # if (NOT ${SNMALLOC_CLEANUP} STREQUAL CXX11_DESTRUCTORS) + # if (SNMALLOC_LINKER_SUPPORT_NOSTDLIBXX) + # target_link_options(${name} PRIVATE -nostdlib++) + # else() + # set_target_properties(${name} PROPERTIES LINKER_LANGUAGE C) + # endif() + # endif() # Remove all the duplicate new/malloc and free/delete definitions target_link_options(${name} PRIVATE $<$:$<$:-Wl,--icf=all> -fuse-ld=lld>) endif() diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 3f0efc183..faaa808bc 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -20,24 +20,90 @@ // only define these if we are not using the vendored STL #ifndef SNMALLOC_USE_SELF_VENDORED_STL + +namespace snmalloc +{ + template + class SetHandlerContinuations + { + public: + static void* success(void* p, size_t size, bool secondary_allocator = false) + { + UNUSED(secondary_allocator, size); + SNMALLOC_ASSERT(p != nullptr); + + SNMALLOC_ASSERT( + secondary_allocator || + is_start_of_object(size_to_sizeclass_full(size), address_cast(p))); + + return p; + } + + static void* failure(size_t size) + { + UNUSED(size); + + auto new_handler = std::get_new_handler(); + if (new_handler != nullptr) + { + if constexpr (ShouldThrow) + { + try + { + // Call the new handler, which may throw an exception. + new_handler(); + } + catch (...) + { + // If the new handler throws, we just return nullptr. + return nullptr; + } + } + // Retry now new_handler has been called. + // I dislike the unbounded retrying here, but that seems to be what + // other implementations do. + return snmalloc::alloc>(size); + } + + if constexpr (ShouldThrow) + { + // Throw std::bad_alloc on failure. + throw std::bad_alloc(); + } + else + { + // If we are here, then the allocation failed. + // Set errno to ENOMEM, as per the C standard. + errno = ENOMEM; + + // Return nullptr on failure. + return nullptr; + } + } + }; + + using NoThrow = SetHandlerContinuations; + using Throw = SetHandlerContinuations; +} // namespace snmalloc + void* operator new(size_t size) { - return snmalloc::libc::malloc(size); + return snmalloc::alloc(size); } void* operator new[](size_t size) { - return snmalloc::libc::malloc(size); + return snmalloc::alloc(size); } void* operator new(size_t size, std::nothrow_t&) { - return snmalloc::libc::malloc(size); + return snmalloc::alloc(size); } void* operator new[](size_t size, std::nothrow_t&) { - return snmalloc::libc::malloc(size); + return snmalloc::alloc(size); } void operator delete(void* p) EXCEPTSPEC @@ -73,25 +139,25 @@ void operator delete[](void* p, std::nothrow_t&) void* operator new(size_t size, std::align_val_t val) { size = snmalloc::aligned_size(size_t(val), size); - return snmalloc::libc::malloc(size); + return snmalloc::alloc(size); } void* operator new[](size_t size, std::align_val_t val) { size = snmalloc::aligned_size(size_t(val), size); - return snmalloc::libc::malloc(size); + return snmalloc::alloc(size); } void* operator new(size_t size, std::align_val_t val, std::nothrow_t&) { size = snmalloc::aligned_size(size_t(val), size); - return snmalloc::libc::malloc(size); + return snmalloc::alloc(size); } void* operator new[](size_t size, std::align_val_t val, std::nothrow_t&) { size = snmalloc::aligned_size(size_t(val), size); - return snmalloc::libc::malloc(size); + return snmalloc::alloc(size); } void operator delete(void* p, std::align_val_t) EXCEPTSPEC diff --git a/src/test/func/miracle_ptr/miracle_ptr.cc b/src/test/func/miracle_ptr/miracle_ptr.cc index f0504597b..b1f212408 100644 --- a/src/test/func/miracle_ptr/miracle_ptr.cc +++ b/src/test/func/miracle_ptr/miracle_ptr.cc @@ -18,9 +18,27 @@ int main() # include # include # include +# include # include # include +// This code makes the delete overloads build with the correct noexcept spec. +# ifdef _WIN32 +# ifdef __clang__ +# define EXCEPTSPEC noexcept +# else +# define EXCEPTSPEC +# endif +# else +# ifdef _GLIBCXX_USE_NOEXCEPT +# define EXCEPTSPEC _GLIBCXX_USE_NOEXCEPT +# elif defined(_NOEXCEPT) +# define EXCEPTSPEC _NOEXCEPT +# else +# define EXCEPTSPEC +# endif +# endif + namespace snmalloc { // Instantiate the allocator with a client meta data provider that uses an @@ -170,12 +188,12 @@ void* operator new(size_t size) return snmalloc::miracle::malloc(size); } -void operator delete(void* p) +void operator delete(void* p) EXCEPTSPEC { snmalloc::miracle::free(p); } -void operator delete(void* p, size_t) +void operator delete(void* p, size_t) EXCEPTSPEC { snmalloc::miracle::free(p); } From c0b5bf64ab53c5bcbd395dfa1e4f0f78a9056b3f Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 9 Jul 2025 15:48:34 +0100 Subject: [PATCH 02/22] Refactoring building This makes most components compilable without needing exceptions. The tails calls in the main code should mean we land on the exception path without any other snmalloc frames in the way. --- CMakeLists.txt | 101 ++++++++++++++++++++----------- src/snmalloc/override/failure.cc | 56 +++++++++++++++++ src/snmalloc/override/new.cc | 45 +++++--------- 3 files changed, 137 insertions(+), 65 deletions(-) create mode 100644 src/snmalloc/override/failure.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c37a0b28..f45f42e99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -503,29 +503,30 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) message(WARNING "Compiler does not support `-march=native` required by SNMALLOC_OPTIMISE_FOR_CURRENT_MACHINE") set(SNMALLOC_OPTIMISE_FOR_CURRENT_MACHINE FALSE) endif() -endif() + endif() - function(add_shim name type) - add_library(${name} ${type} ${ARGN}) + function(compile_object name ${ARGN}) + add_library(${name} OBJECT ${ARGN}) target_link_libraries(${name} snmalloc) set_target_properties(${name} PROPERTIES CXX_VISIBILITY_PRESET hidden INTERPROCEDURAL_OPTIMIZATION ${SNMALLOC_COMPILER_SUPPORT_IPO}) target_compile_definitions(${name} PRIVATE "SNMALLOC_USE_${SNMALLOC_CLEANUP}") - if(MSVC) - target_compile_definitions(${name} PRIVATE -D_HAS_EXCEPTIONS=0) - endif() - add_warning_flags(${name}) if(NOT MSVC) target_compile_definitions(${name} PRIVATE "SNMALLOC_EXPORT=__attribute__((visibility(\"default\")))") - target_compile_options(${name} PRIVATE - -fomit-frame-pointer -ffunction-sections) + set_target_properties(${name} PROPERTIES CXX_VISIBILITY_PRESET hidden INTERPROCEDURAL_OPTIMIZATION ${SNMALLOC_COMPILER_SUPPORT_IPO}) + # Make debugging harder, and code faster. + target_compile_options(${name} PRIVATE -fomit-frame-pointer) + set_property(TARGET ${name} PROPERTY POSITION_INDEPENDENT_CODE ON) + + # Check for prefetch write support. check_cxx_compiler_flag("-Werror -Wextra -Wall -mprfchw" SUPPORT_PREFETCH_WRITE) if (SUPPORT_PREFETCH_WRITE) target_compile_options(${name} PRIVATE -mprfchw) endif() + # Static TLS model is unsupported on Haiku. if (SNMALLOC_STATIC_MODE_TLS) target_compile_options(${name} PRIVATE -ftls-model=initial-exec) @@ -536,18 +537,8 @@ endif() target_compile_options(${name} PRIVATE -march=native) endif() - # Ensure that we do not link against C++ stdlib when compiling shims. - # If the compiler supports excluding the C++ stdlib implementation, use - # it. Otherwise, fall back to linking the library as if it were C, which - # has roughly the same effect. - # if (NOT ${SNMALLOC_CLEANUP} STREQUAL CXX11_DESTRUCTORS) - # if (SNMALLOC_LINKER_SUPPORT_NOSTDLIBXX) - # target_link_options(${name} PRIVATE -nostdlib++) - # else() - # set_target_properties(${name} PROPERTIES LINKER_LANGUAGE C) - # endif() - # endif() # Remove all the duplicate new/malloc and free/delete definitions + target_compile_options(${name} PRIVATE -ffunction-sections) target_link_options(${name} PRIVATE $<$:$<$:-Wl,--icf=all> -fuse-ld=lld>) endif() @@ -556,40 +547,80 @@ endif() target_compile_definitions(${name} PRIVATE SNMALLOC_PAGEID=$,true,false>) + if (SNMALLOC_RUST_LIBC_API) + target_compile_definitions(${name} PRIVATE SNMALLOC_RUST_LIBC_API) + endif() install(TARGETS ${name} EXPORT snmallocConfig) + endfunction() + + function(compile_object_noexc name ${ARGN}) + compile_object(${name} ${ARGN}) + if(MSVC) + target_compile_definitions(${name} PRIVATE -D_HAS_EXCEPTIONS=0) + else() + # Ensure that we do not link against C++ stdlib when compiling shims. + # If the compiler supports excluding the C++ stdlib implementation, use + # it. Otherwise, fall back to linking the library as if it were C, which + # has roughly the same effect. + if (NOT ${SNMALLOC_CLEANUP} STREQUAL CXX11_DESTRUCTORS) + if (SNMALLOC_LINKER_SUPPORT_NOSTDLIBXX) + target_link_options(${name} PRIVATE -nostdlib++) + else() + set_target_properties(${name} PROPERTIES LINKER_LANGUAGE C) + endif() + endif() + endif() endfunction() - set(SHIM_FILES src/snmalloc/override/malloc.cc src/snmalloc/override/new.cc) - set(SHIM_FILES_MEMCPY src/snmalloc/override/memcpy.cc) + function(add_shim name type) + add_library(${name} ${type}) + target_link_libraries(${name} ${ARGN}) + install(TARGETS ${name} EXPORT snmallocConfig) + endfunction() - add_shim(snmalloc-new-override STATIC src/snmalloc/override/new.cc) + compile_object(snmalloc_exception_handler src/snmalloc/override/failure.cc) + compile_object_noexc(snmalloc_core src/snmalloc/override/malloc.cc) + compile_object_noexc(snmalloc_new src/snmalloc/override/new.cc) + compile_object_noexc(snmalloc_memcpy src/snmalloc/override/memcpy.cc) + compile_object_noexc(snmalloc_rust src/snmalloc/override/rust.cc) + + compile_object(snmalloc_exception_handler_checks src/snmalloc/override/failure.cc) + target_compile_definitions(snmalloc_exception_handler_checks PRIVATE SNMALLOC_CHECK_CLIENT) + compile_object_noexc(snmalloc_core_checks src/snmalloc/override/malloc.cc) + target_compile_definitions(snmalloc_core_checks PRIVATE SNMALLOC_CHECK_CLIENT) + compile_object_noexc(snmalloc_new_checks src/snmalloc/override/new.cc) + target_compile_definitions(snmalloc_new_checks PRIVATE SNMALLOC_CHECK_CLIENT) + compile_object_noexc(snmalloc_memcpy_checks src/snmalloc/override/memcpy.cc) + target_compile_definitions(snmalloc_memcpy_checks PRIVATE SNMALLOC_CHECK_CLIENT) + compile_object_noexc(snmalloc_rust_checks src/snmalloc/override/rust.cc) + target_compile_definitions(snmalloc_rust_checks PRIVATE SNMALLOC_CHECK_CLIENT) + + add_shim(snmalloc-new-override STATIC snmalloc_new snmalloc_exception_handler) if (SNMALLOC_STATIC_LIBRARY) - add_shim(snmallocshim-static STATIC ${SHIM_FILES}) + add_shim(snmallocshim-static STATIC snmalloc_core snmalloc_exception_handler snmalloc_new snmalloc_memcpy) target_compile_definitions(snmallocshim-static PRIVATE SNMALLOC_STATIC_LIBRARY_PREFIX=${SNMALLOC_STATIC_LIBRARY_PREFIX}) endif () + set(SNMALLOC_UNCHECK_OBJECTS snmalloc_core snmalloc_exception_handler snmalloc_new) + set(SNMALLOC_CHECK_OBJECTS snmalloc_core_checks snmalloc_exception_handler_checks snmalloc_new_checks) + if(NOT WIN32) - add_shim(snmallocshim SHARED ${SHIM_FILES}) + add_shim(snmallocshim SHARED ${SNMALLOC_UNCHECK_OBJECTS}) if (SNMALLOC_MEMCPY_OVERRIDE) - add_shim(snmallocshim-checks-memcpy-only SHARED ${SHIM_FILES} ${SHIM_FILES_MEMCPY}) - add_shim(snmallocshim-checks SHARED ${SHIM_FILES} ${SHIM_FILES_MEMCPY}) + add_shim(snmallocshim-checks-memcpy-only SHARED ${SNMALLOC_UNCHECK_OBJECTS} snmalloc_memcpy) + add_shim(snmallocshim-checks SHARED ${SNMALLOC_CHECK_OBJECTS} snmalloc_new_checks snmalloc_memcpy_checks) else() - add_shim(snmallocshim-checks SHARED ${SHIM_FILES}) + add_shim(snmallocshim-checks SHARED ${SNMALLOC_CHECK_OBJECTS}) endif() - target_compile_definitions(snmallocshim-checks PRIVATE SNMALLOC_CHECK_CLIENT) endif() if(SNMALLOC_RUST_SUPPORT) - add_shim(snmallocshim-rust STATIC src/snmalloc/override/rust.cc) - add_shim(snmallocshim-checks-rust STATIC src/snmalloc/override/rust.cc) + add_shim(snmallocshim-rust STATIC snmalloc_rust) + add_shim(snmallocshim-checks-rust STATIC snmalloc_rust_checks) target_compile_definitions(snmallocshim-checks-rust PRIVATE SNMALLOC_CHECK_CLIENT) - if (SNMALLOC_RUST_LIBC_API) - target_compile_definitions(snmallocshim-rust PRIVATE SNMALLOC_RUST_LIBC_API) - target_compile_definitions(snmallocshim-checks-rust PRIVATE SNMALLOC_RUST_LIBC_API) - endif() endif() if (SNMALLOC_BUILD_TESTING) diff --git a/src/snmalloc/override/failure.cc b/src/snmalloc/override/failure.cc new file mode 100644 index 000000000..ded69a9f2 --- /dev/null +++ b/src/snmalloc/override/failure.cc @@ -0,0 +1,56 @@ +#include +#include +#include + +namespace snmalloc +{ + void* alloc_nothrow(size_t size); + void* alloc_throw(size_t size); + + template + class SetHandlerContinuations; + + void* failure_throw(std::size_t size) + { + auto new_handler = std::get_new_handler(); + if (new_handler != nullptr) + { + // Call the new handler, which may throw an exception. + new_handler(); + // Retry now new_handler has been called. + // I dislike the unbounded retrying here, but that seems to be what + // other implementations do. + return alloc_nothrow(size); + } + + // Throw std::bad_alloc on failure. + throw std::bad_alloc(); + } + + void* failure_nothrow(std::size_t size) + { + auto new_handler = std::get_new_handler(); + if (new_handler != nullptr) + { + try + { + // Call the new handler, which may throw an exception. + new_handler(); + } + catch (...) + { + // If the new handler throws, we just return nullptr. + return nullptr; + } + // Retry now new_handler has been called. + return alloc_nothrow(size); + } + + // If we are here, then the allocation failed. + // Set errno to ENOMEM, as per the C standard. + errno = ENOMEM; + + // Return nullptr on failure. + return nullptr; + } +} diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index faaa808bc..2edee84fb 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -23,6 +23,9 @@ namespace snmalloc { + void* failure_throw(size_t size); + void* failure_nothrow(size_t size); + template class SetHandlerContinuations { @@ -41,49 +44,31 @@ namespace snmalloc static void* failure(size_t size) { - UNUSED(size); - - auto new_handler = std::get_new_handler(); - if (new_handler != nullptr) - { - if constexpr (ShouldThrow) - { - try - { - // Call the new handler, which may throw an exception. - new_handler(); - } - catch (...) - { - // If the new handler throws, we just return nullptr. - return nullptr; - } - } - // Retry now new_handler has been called. - // I dislike the unbounded retrying here, but that seems to be what - // other implementations do. - return snmalloc::alloc>(size); - } - if constexpr (ShouldThrow) { // Throw std::bad_alloc on failure. - throw std::bad_alloc(); + return failure_throw(size); } else { - // If we are here, then the allocation failed. - // Set errno to ENOMEM, as per the C standard. - errno = ENOMEM; - // Return nullptr on failure. - return nullptr; + return failure_nothrow(size); } } }; using NoThrow = SetHandlerContinuations; using Throw = SetHandlerContinuations; + + void* alloc_nothrow(size_t size) + { + return alloc(size); + } + + void* alloc_throw(size_t size) + { + return alloc(size); + } } // namespace snmalloc void* operator new(size_t size) From dad91104f5f7bbcbd81bdf60721c1d2ad905effb Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 10 Jul 2025 14:38:38 +0100 Subject: [PATCH 03/22] Updated packaging of exception handling --- CMakeLists.txt | 74 ++++++++++++++++---------------- src/snmalloc/override/failure.cc | 28 ++++++++++-- 2 files changed, 61 insertions(+), 41 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f45f42e99..6d00359a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -506,8 +506,8 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) endif() - function(compile_object name ${ARGN}) - add_library(${name} OBJECT ${ARGN}) + function(compile name TYPE ${ARGN}) + add_library(${name} ${TYPE} ${ARGN}) target_link_libraries(${name} snmalloc) set_target_properties(${name} PROPERTIES CXX_VISIBILITY_PRESET hidden INTERPROCEDURAL_OPTIMIZATION ${SNMALLOC_COMPILER_SUPPORT_IPO}) target_compile_definitions(${name} PRIVATE "SNMALLOC_USE_${SNMALLOC_CLEANUP}") @@ -553,12 +553,14 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) install(TARGETS ${name} EXPORT snmallocConfig) endfunction() - function(compile_object_noexc name ${ARGN}) - compile_object(${name} ${ARGN}) + function(compile_noexc name TYPE ${ARGN}) + compile(${name} ${TYPE} ${ARGN}) + target_compile_definitionS(${name} PRIVATE -DSNMALLOC_NOEXC) if(MSVC) target_compile_definitions(${name} PRIVATE -D_HAS_EXCEPTIONS=0) else() + target_compile_options(${name} PRIVATE -fno-exceptions) # Ensure that we do not link against C++ stdlib when compiling shims. # If the compiler supports excluding the C++ stdlib implementation, use # it. Otherwise, fall back to linking the library as if it were C, which @@ -573,53 +575,51 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) endif() endfunction() - function(add_shim name type) - add_library(${name} ${type}) - target_link_libraries(${name} ${ARGN}) - install(TARGETS ${name} EXPORT snmallocConfig) - endfunction() + # Various files for overriding libc/rust behaviours. + set(MALLOC src/snmalloc/override/malloc.cc) + set(NEW src/snmalloc/override/new.cc) + set(MEMCPY src/snmalloc/override/memcpy.cc) + set(RUST src/snmalloc/override/rust.cc) + + set(ALLOC ${MALLOC} ${NEW}) + set(ALL ${ALLOC} ${MEMCPY}) + + # Exception handler code needs to be compiled with exceptions enabled. + compile(snmalloc_exception_handler OBJECT src/snmalloc/override/failure.cc) - compile_object(snmalloc_exception_handler src/snmalloc/override/failure.cc) - compile_object_noexc(snmalloc_core src/snmalloc/override/malloc.cc) - compile_object_noexc(snmalloc_new src/snmalloc/override/new.cc) - compile_object_noexc(snmalloc_memcpy src/snmalloc/override/memcpy.cc) - compile_object_noexc(snmalloc_rust src/snmalloc/override/rust.cc) - - compile_object(snmalloc_exception_handler_checks src/snmalloc/override/failure.cc) - target_compile_definitions(snmalloc_exception_handler_checks PRIVATE SNMALLOC_CHECK_CLIENT) - compile_object_noexc(snmalloc_core_checks src/snmalloc/override/malloc.cc) - target_compile_definitions(snmalloc_core_checks PRIVATE SNMALLOC_CHECK_CLIENT) - compile_object_noexc(snmalloc_new_checks src/snmalloc/override/new.cc) - target_compile_definitions(snmalloc_new_checks PRIVATE SNMALLOC_CHECK_CLIENT) - compile_object_noexc(snmalloc_memcpy_checks src/snmalloc/override/memcpy.cc) - target_compile_definitions(snmalloc_memcpy_checks PRIVATE SNMALLOC_CHECK_CLIENT) - compile_object_noexc(snmalloc_rust_checks src/snmalloc/override/rust.cc) - target_compile_definitions(snmalloc_rust_checks PRIVATE SNMALLOC_CHECK_CLIENT) - - add_shim(snmalloc-new-override STATIC snmalloc_new snmalloc_exception_handler) + # Compile a versions without exceptions as that is what snmalloc previously did. + compile_noexc(snmalloc_exception_handler_noexc OBJECT src/snmalloc/override/failure.cc) + + compile_noexc(snmalloc-new-override STATIC ${NEW}) + target_link_libraries(snmalloc-new-override snmalloc_exception_handler) if (SNMALLOC_STATIC_LIBRARY) - add_shim(snmallocshim-static STATIC snmalloc_core snmalloc_exception_handler snmalloc_new snmalloc_memcpy) + compile_noexc(snmallocshim-static STATIC src/snmalloc/override/malloc.cc src/snmalloc/override/new.cc src/snmalloc/override/memcpy.cc) target_compile_definitions(snmallocshim-static PRIVATE SNMALLOC_STATIC_LIBRARY_PREFIX=${SNMALLOC_STATIC_LIBRARY_PREFIX}) endif () - set(SNMALLOC_UNCHECK_OBJECTS snmalloc_core snmalloc_exception_handler snmalloc_new) - set(SNMALLOC_CHECK_OBJECTS snmalloc_core_checks snmalloc_exception_handler_checks snmalloc_new_checks) - if(NOT WIN32) - add_shim(snmallocshim SHARED ${SNMALLOC_UNCHECK_OBJECTS}) + compile_noexc(snmallocshim SHARED ${ALLOC}) + target_link_libraries(snmallocshim snmalloc_exception_handler_noexc) + + compile(snmallocnew SHARED ${ALLOC}) + target_link_libraries(snmallocnew snmalloc_exception_handler) + if (SNMALLOC_MEMCPY_OVERRIDE) - add_shim(snmallocshim-checks-memcpy-only SHARED ${SNMALLOC_UNCHECK_OBJECTS} snmalloc_memcpy) - add_shim(snmallocshim-checks SHARED ${SNMALLOC_CHECK_OBJECTS} snmalloc_new_checks snmalloc_memcpy_checks) + compile(snmallocshim-checks-memcpy-only SHARED ${ALL}) + target_link_libraries(snmallocshim-checks-memcpy-only snmalloc_exception_handler) + compile(snmallocshim-checks SHARED ${ALL}) else() - add_shim(snmallocshim-checks SHARED ${SNMALLOC_CHECK_OBJECTS}) + compile(snmallocshim-checks SHARED ${MALLOC}) endif() + target_compile_definitions(snmallocshim-checks PRIVATE SNMALLOC_CHECK_CLIENT) + target_link_libraries(snmallocshim-checks snmalloc_exception_handler) endif() if(SNMALLOC_RUST_SUPPORT) - add_shim(snmallocshim-rust STATIC snmalloc_rust) - add_shim(snmallocshim-checks-rust STATIC snmalloc_rust_checks) + compile_noexc(snmallocshim-rust STATIC ${RUST}) + compile_noexc(snmallocshim-checks-rust STATIC ${RUST}) target_compile_definitions(snmallocshim-checks-rust PRIVATE SNMALLOC_CHECK_CLIENT) endif() diff --git a/src/snmalloc/override/failure.cc b/src/snmalloc/override/failure.cc index ded69a9f2..456291312 100644 --- a/src/snmalloc/override/failure.cc +++ b/src/snmalloc/override/failure.cc @@ -7,9 +7,28 @@ namespace snmalloc void* alloc_nothrow(size_t size); void* alloc_throw(size_t size); - template - class SetHandlerContinuations; +#ifdef SNMALLOC_NOEXC + // If we are compiling without exceptions, we cannot usefully use + // new_handlers to retry allocations, so we just ignore them and + // return nullptr on failure. + void* failure_throw(std::size_t) + { + // If we are here, then the allocation failed. + // Set errno to ENOMEM, as per the C standard. + errno = ENOMEM; + // Return nullptr on failure. + return nullptr; + } + void* failure_nothrow(std::size_t) + { + // If we are here, then the allocation failed. + // Set errno to ENOMEM, as per the C standard. + errno = ENOMEM; + // Return nullptr on failure. + return nullptr; + } +#else void* failure_throw(std::size_t size) { auto new_handler = std::get_new_handler(); @@ -20,7 +39,7 @@ namespace snmalloc // Retry now new_handler has been called. // I dislike the unbounded retrying here, but that seems to be what // other implementations do. - return alloc_nothrow(size); + return alloc_throw(size); } // Throw std::bad_alloc on failure. @@ -45,7 +64,6 @@ namespace snmalloc // Retry now new_handler has been called. return alloc_nothrow(size); } - // If we are here, then the allocation failed. // Set errno to ENOMEM, as per the C standard. errno = ENOMEM; @@ -53,4 +71,6 @@ namespace snmalloc // Return nullptr on failure. return nullptr; } +#endif } + From 6967bc6acddc148861907ae4f2a0444710e5185c Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 10 Jul 2025 16:37:52 +0100 Subject: [PATCH 04/22] Refactoring based on CI --- CMakeLists.txt | 13 +----- src/snmalloc/override/failure.cc | 76 -------------------------------- src/snmalloc/override/new.cc | 66 +++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 88 deletions(-) delete mode 100644 src/snmalloc/override/failure.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d00359a8..862cffddb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -584,37 +584,26 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) set(ALLOC ${MALLOC} ${NEW}) set(ALL ${ALLOC} ${MEMCPY}) - # Exception handler code needs to be compiled with exceptions enabled. - compile(snmalloc_exception_handler OBJECT src/snmalloc/override/failure.cc) - - # Compile a versions without exceptions as that is what snmalloc previously did. - compile_noexc(snmalloc_exception_handler_noexc OBJECT src/snmalloc/override/failure.cc) - compile_noexc(snmalloc-new-override STATIC ${NEW}) target_link_libraries(snmalloc-new-override snmalloc_exception_handler) if (SNMALLOC_STATIC_LIBRARY) - compile_noexc(snmallocshim-static STATIC src/snmalloc/override/malloc.cc src/snmalloc/override/new.cc src/snmalloc/override/memcpy.cc) + compile_noexc(snmallocshim-static STATIC ${ALL}) target_compile_definitions(snmallocshim-static PRIVATE SNMALLOC_STATIC_LIBRARY_PREFIX=${SNMALLOC_STATIC_LIBRARY_PREFIX}) endif () if(NOT WIN32) compile_noexc(snmallocshim SHARED ${ALLOC}) - target_link_libraries(snmallocshim snmalloc_exception_handler_noexc) - compile(snmallocnew SHARED ${ALLOC}) - target_link_libraries(snmallocnew snmalloc_exception_handler) if (SNMALLOC_MEMCPY_OVERRIDE) compile(snmallocshim-checks-memcpy-only SHARED ${ALL}) - target_link_libraries(snmallocshim-checks-memcpy-only snmalloc_exception_handler) compile(snmallocshim-checks SHARED ${ALL}) else() compile(snmallocshim-checks SHARED ${MALLOC}) endif() target_compile_definitions(snmallocshim-checks PRIVATE SNMALLOC_CHECK_CLIENT) - target_link_libraries(snmallocshim-checks snmalloc_exception_handler) endif() if(SNMALLOC_RUST_SUPPORT) diff --git a/src/snmalloc/override/failure.cc b/src/snmalloc/override/failure.cc deleted file mode 100644 index 456291312..000000000 --- a/src/snmalloc/override/failure.cc +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include -#include - -namespace snmalloc -{ - void* alloc_nothrow(size_t size); - void* alloc_throw(size_t size); - -#ifdef SNMALLOC_NOEXC - // If we are compiling without exceptions, we cannot usefully use - // new_handlers to retry allocations, so we just ignore them and - // return nullptr on failure. - void* failure_throw(std::size_t) - { - // If we are here, then the allocation failed. - // Set errno to ENOMEM, as per the C standard. - errno = ENOMEM; - // Return nullptr on failure. - return nullptr; - } - - void* failure_nothrow(std::size_t) - { - // If we are here, then the allocation failed. - // Set errno to ENOMEM, as per the C standard. - errno = ENOMEM; - // Return nullptr on failure. - return nullptr; - } -#else - void* failure_throw(std::size_t size) - { - auto new_handler = std::get_new_handler(); - if (new_handler != nullptr) - { - // Call the new handler, which may throw an exception. - new_handler(); - // Retry now new_handler has been called. - // I dislike the unbounded retrying here, but that seems to be what - // other implementations do. - return alloc_throw(size); - } - - // Throw std::bad_alloc on failure. - throw std::bad_alloc(); - } - - void* failure_nothrow(std::size_t size) - { - auto new_handler = std::get_new_handler(); - if (new_handler != nullptr) - { - try - { - // Call the new handler, which may throw an exception. - new_handler(); - } - catch (...) - { - // If the new handler throws, we just return nullptr. - return nullptr; - } - // Retry now new_handler has been called. - return alloc_nothrow(size); - } - // If we are here, then the allocation failed. - // Set errno to ENOMEM, as per the C standard. - errno = ENOMEM; - - // Return nullptr on failure. - return nullptr; - } -#endif -} - diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 2edee84fb..e7b51a5fc 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -69,6 +69,72 @@ namespace snmalloc { return alloc(size); } + +#ifdef SNMALLOC_NOEXC + // If we are compiling without exceptions, we cannot usefully use + // new_handlers to retry allocations, so we just ignore them and + // return nullptr on failure. + void* failure_throw(std::size_t) + { + // If we are here, then the allocation failed. + // Set errno to ENOMEM, as per the C standard. + errno = ENOMEM; + // Return nullptr on failure. + return nullptr; + } + + void* failure_nothrow(std::size_t) + { + // If we are here, then the allocation failed. + // Set errno to ENOMEM, as per the C standard. + errno = ENOMEM; + // Return nullptr on failure. + return nullptr; + } +#else + void* failure_throw(std::size_t size) + { + auto new_handler = std::get_new_handler(); + if (new_handler != nullptr) + { + // Call the new handler, which may throw an exception. + new_handler(); + // Retry now new_handler has been called. + // I dislike the unbounded retrying here, but that seems to be what + // other implementations do. + return alloc_throw(size); + } + + // Throw std::bad_alloc on failure. + throw std::bad_alloc(); + } + + void* failure_nothrow(std::size_t size) + { + auto new_handler = std::get_new_handler(); + if (new_handler != nullptr) + { + try + { + // Call the new handler, which may throw an exception. + new_handler(); + } + catch (...) + { + // If the new handler throws, we just return nullptr. + return nullptr; + } + // Retry now new_handler has been called. + return alloc_nothrow(size); + } + // If we are here, then the allocation failed. + // Set errno to ENOMEM, as per the C standard. + errno = ENOMEM; + + // Return nullptr on failure. + return nullptr; + } +#endif } // namespace snmalloc void* operator new(size_t size) From 1a7be3a8c89987af812585f12971fa534d3fa292 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 10 Jul 2025 16:39:27 +0100 Subject: [PATCH 05/22] Clangformat --- src/snmalloc/override/new.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index e7b51a5fc..ba1d0d58c 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -70,7 +70,7 @@ namespace snmalloc return alloc(size); } -#ifdef SNMALLOC_NOEXC +# ifdef SNMALLOC_NOEXC // If we are compiling without exceptions, we cannot usefully use // new_handlers to retry allocations, so we just ignore them and // return nullptr on failure. @@ -91,7 +91,7 @@ namespace snmalloc // Return nullptr on failure. return nullptr; } -#else +# else void* failure_throw(std::size_t size) { auto new_handler = std::get_new_handler(); @@ -134,7 +134,7 @@ namespace snmalloc // Return nullptr on failure. return nullptr; } -#endif +# endif } // namespace snmalloc void* operator new(size_t size) From 8b5d52757f7d3ca2153fcc90e933cb5b67af75c5 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 10 Jul 2025 17:01:40 +0100 Subject: [PATCH 06/22] Make C++ library statically linked --- CMakeLists.txt | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 862cffddb..7bc4fa7f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -506,7 +506,7 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) endif() - function(compile name TYPE ${ARGN}) + function(compile_core name TYPE ${ARGN}) add_library(${name} ${TYPE} ${ARGN}) target_link_libraries(${name} snmalloc) set_target_properties(${name} PROPERTIES CXX_VISIBILITY_PRESET hidden INTERPROCEDURAL_OPTIMIZATION ${SNMALLOC_COMPILER_SUPPORT_IPO}) @@ -554,7 +554,7 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) endfunction() function(compile_noexc name TYPE ${ARGN}) - compile(${name} ${TYPE} ${ARGN}) + compile_core(${name} ${TYPE} ${ARGN}) target_compile_definitionS(${name} PRIVATE -DSNMALLOC_NOEXC) if(MSVC) @@ -575,6 +575,20 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) endif() endfunction() + function(compile name TYPE ${ARGN}) + compile_core(${name} ${TYPE} ${ARGN}) + + if(MSVC) + else() + # We can't dp --notstdlib++ as we are using exceptions, but we + # can make it a static dependency, which we aren't really using + # as the interesting stuff for exceptions is in libgcc_s + if (NOT ${SNMALLOC_CLEANUP} STREQUAL CXX11_DESTRUCTORS) + target_link_options(${name} PRIVATE -static-libstdc++) + endif() + endif() + endfunction() + # Various files for overriding libc/rust behaviours. set(MALLOC src/snmalloc/override/malloc.cc) set(NEW src/snmalloc/override/new.cc) @@ -645,7 +659,7 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) foreach (MITIGATION ${MITIGATIONS}) set(DEFINES "SNMALLOC_CHECK_CLIENT_MITIGATIONS=${MITIGATION}") - add_shim(snmallocshim-${MITIGATION} SHARED ${SHIM_FILES}) + compile(snmallocshim-${MITIGATION} SHARED ${ALLOC}) target_compile_definitions(snmallocshim-${MITIGATION} PRIVATE ${DEFINES}) if (SNMALLOC_BUILD_TESTING) make_tests(${MITIGATION} ${DEFINES}) @@ -660,7 +674,7 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) set(MITIGATIONSET "${MITIGATIONSET}+${MITIGATION}") message(STATUS "MITIGATIONSET: ${COUNT} -> ${MITIGATIONSET}") set(DEFINES "-DSNMALLOC_CHECK_CLIENT_MITIGATIONS=${MITIGATIONSET}") - add_shim(snmallocshim-${MITIGATIONNAME} SHARED ${SHIM_FILES}) + compile(snmallocshim-${MITIGATIONNAME} SHARED ${ALLOC}) target_compile_definitions(snmallocshim-${MITIGATIONNAME} PRIVATE ${DEFINES}) if (SNMALLOC_BUILD_TESTING) make_tests(${MITIGATIONNAME} ${DEFINES}) From 25dd1dbc22d9cb17d601cc9a6a6bb5861c4e9451 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 10 Jul 2025 17:25:15 +0100 Subject: [PATCH 07/22] Simplify using static linkage. --- CMakeLists.txt | 52 ++++------------ src/snmalloc/override/new.cc | 112 +++++++++++------------------------ 2 files changed, 45 insertions(+), 119 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bc4fa7f6..e9e9c2acb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -506,7 +506,7 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) endif() - function(compile_core name TYPE ${ARGN}) + function(compile name TYPE ${ARGN}) add_library(${name} ${TYPE} ${ARGN}) target_link_libraries(${name} snmalloc) set_target_properties(${name} PROPERTIES CXX_VISIBILITY_PRESET hidden INTERPROCEDURAL_OPTIMIZATION ${SNMALLOC_COMPILER_SUPPORT_IPO}) @@ -547,46 +547,20 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) target_compile_definitions(${name} PRIVATE SNMALLOC_PAGEID=$,true,false>) - if (SNMALLOC_RUST_LIBC_API) - target_compile_definitions(${name} PRIVATE SNMALLOC_RUST_LIBC_API) - endif() - install(TARGETS ${name} EXPORT snmallocConfig) - endfunction() - - function(compile_noexc name TYPE ${ARGN}) - compile_core(${name} ${TYPE} ${ARGN}) - - target_compile_definitionS(${name} PRIVATE -DSNMALLOC_NOEXC) if(MSVC) - target_compile_definitions(${name} PRIVATE -D_HAS_EXCEPTIONS=0) else() - target_compile_options(${name} PRIVATE -fno-exceptions) - # Ensure that we do not link against C++ stdlib when compiling shims. - # If the compiler supports excluding the C++ stdlib implementation, use - # it. Otherwise, fall back to linking the library as if it were C, which - # has roughly the same effect. - if (NOT ${SNMALLOC_CLEANUP} STREQUAL CXX11_DESTRUCTORS) - if (SNMALLOC_LINKER_SUPPORT_NOSTDLIBXX) - target_link_options(${name} PRIVATE -nostdlib++) - else() - set_target_properties(${name} PROPERTIES LINKER_LANGUAGE C) - endif() - endif() - endif() - endfunction() - - function(compile name TYPE ${ARGN}) - compile_core(${name} ${TYPE} ${ARGN}) - - if(MSVC) - else() - # We can't dp --notstdlib++ as we are using exceptions, but we + # We can't do --notstdlib++ as we are using exceptions, but we # can make it a static dependency, which we aren't really using # as the interesting stuff for exceptions is in libgcc_s if (NOT ${SNMALLOC_CLEANUP} STREQUAL CXX11_DESTRUCTORS) target_link_options(${name} PRIVATE -static-libstdc++) endif() endif() + + if (SNMALLOC_RUST_LIBC_API) + target_compile_definitions(${name} PRIVATE SNMALLOC_RUST_LIBC_API) + endif() + install(TARGETS ${name} EXPORT snmallocConfig) endfunction() # Various files for overriding libc/rust behaviours. @@ -598,18 +572,14 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) set(ALLOC ${MALLOC} ${NEW}) set(ALL ${ALLOC} ${MEMCPY}) - compile_noexc(snmalloc-new-override STATIC ${NEW}) - target_link_libraries(snmalloc-new-override snmalloc_exception_handler) - if (SNMALLOC_STATIC_LIBRARY) - compile_noexc(snmallocshim-static STATIC ${ALL}) + compile(snmallocshim-static STATIC ${ALL}) target_compile_definitions(snmallocshim-static PRIVATE SNMALLOC_STATIC_LIBRARY_PREFIX=${SNMALLOC_STATIC_LIBRARY_PREFIX}) endif () if(NOT WIN32) - compile_noexc(snmallocshim SHARED ${ALLOC}) - compile(snmallocnew SHARED ${ALLOC}) + compile(snmallocshim SHARED ${ALLOC}) if (SNMALLOC_MEMCPY_OVERRIDE) compile(snmallocshim-checks-memcpy-only SHARED ${ALL}) @@ -621,8 +591,8 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) endif() if(SNMALLOC_RUST_SUPPORT) - compile_noexc(snmallocshim-rust STATIC ${RUST}) - compile_noexc(snmallocshim-checks-rust STATIC ${RUST}) + compile(snmallocshim-rust STATIC ${RUST}) + compile(snmallocshim-checks-rust STATIC ${RUST}) target_compile_definitions(snmallocshim-checks-rust PRIVATE SNMALLOC_CHECK_CLIENT) endif() diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index ba1d0d58c..6e53c3fe8 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -47,94 +47,50 @@ namespace snmalloc if constexpr (ShouldThrow) { // Throw std::bad_alloc on failure. - return failure_throw(size); + auto new_handler = std::get_new_handler(); + if (new_handler != nullptr) + { + // Call the new handler, which may throw an exception. + new_handler(); + // Retry now new_handler has been called. + // I dislike the unbounded retrying here, but that seems to be what + // other implementations do. + return alloc(size); + } + + // Throw std::bad_alloc on failure. + throw std::bad_alloc(); } else { + auto new_handler = std::get_new_handler(); + if (new_handler != nullptr) + { + try + { + // Call the new handler, which may throw an exception. + new_handler(); + } + catch (...) + { + // If the new handler throws, we just return nullptr. + return nullptr; + } + // Retry now new_handler has been called. + return alloc(size); + } + // If we are here, then the allocation failed. + // Set errno to ENOMEM, as per the C standard. + errno = ENOMEM; + // Return nullptr on failure. - return failure_nothrow(size); + return nullptr; } } }; using NoThrow = SetHandlerContinuations; using Throw = SetHandlerContinuations; - - void* alloc_nothrow(size_t size) - { - return alloc(size); - } - - void* alloc_throw(size_t size) - { - return alloc(size); - } - -# ifdef SNMALLOC_NOEXC - // If we are compiling without exceptions, we cannot usefully use - // new_handlers to retry allocations, so we just ignore them and - // return nullptr on failure. - void* failure_throw(std::size_t) - { - // If we are here, then the allocation failed. - // Set errno to ENOMEM, as per the C standard. - errno = ENOMEM; - // Return nullptr on failure. - return nullptr; - } - - void* failure_nothrow(std::size_t) - { - // If we are here, then the allocation failed. - // Set errno to ENOMEM, as per the C standard. - errno = ENOMEM; - // Return nullptr on failure. - return nullptr; - } -# else - void* failure_throw(std::size_t size) - { - auto new_handler = std::get_new_handler(); - if (new_handler != nullptr) - { - // Call the new handler, which may throw an exception. - new_handler(); - // Retry now new_handler has been called. - // I dislike the unbounded retrying here, but that seems to be what - // other implementations do. - return alloc_throw(size); - } - - // Throw std::bad_alloc on failure. - throw std::bad_alloc(); - } - - void* failure_nothrow(std::size_t size) - { - auto new_handler = std::get_new_handler(); - if (new_handler != nullptr) - { - try - { - // Call the new handler, which may throw an exception. - new_handler(); - } - catch (...) - { - // If the new handler throws, we just return nullptr. - return nullptr; - } - // Retry now new_handler has been called. - return alloc_nothrow(size); - } - // If we are here, then the allocation failed. - // Set errno to ENOMEM, as per the C standard. - errno = ENOMEM; - - // Return nullptr on failure. - return nullptr; - } -# endif } // namespace snmalloc void* operator new(size_t size) From 26ebdc9b190a1716ef26748c60f1cc18231b0e75 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 10 Jul 2025 17:30:37 +0100 Subject: [PATCH 08/22] Fixes from self review --- CMakeLists.txt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e9e9c2acb..7061d0f51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -552,9 +552,7 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) # We can't do --notstdlib++ as we are using exceptions, but we # can make it a static dependency, which we aren't really using # as the interesting stuff for exceptions is in libgcc_s - if (NOT ${SNMALLOC_CLEANUP} STREQUAL CXX11_DESTRUCTORS) - target_link_options(${name} PRIVATE -static-libstdc++) - endif() + target_link_options(${name} PRIVATE -static-libstdc++) endif() if (SNMALLOC_RUST_LIBC_API) @@ -573,7 +571,7 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) set(ALL ${ALLOC} ${MEMCPY}) if (SNMALLOC_STATIC_LIBRARY) - compile(snmallocshim-static STATIC ${ALL}) + compile(snmallocshim-static STATIC ${ALLOC}) target_compile_definitions(snmallocshim-static PRIVATE SNMALLOC_STATIC_LIBRARY_PREFIX=${SNMALLOC_STATIC_LIBRARY_PREFIX}) endif () @@ -585,7 +583,7 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) compile(snmallocshim-checks-memcpy-only SHARED ${ALL}) compile(snmallocshim-checks SHARED ${ALL}) else() - compile(snmallocshim-checks SHARED ${MALLOC}) + compile(snmallocshim-checks SHARED ${ALLOC}) endif() target_compile_definitions(snmallocshim-checks PRIVATE SNMALLOC_CHECK_CLIENT) endif() From 3146644414152de27e65716308238636b6a43823 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 10 Jul 2025 19:38:22 +0100 Subject: [PATCH 09/22] Add back missing things, typos, and add a new minimal shared library --- CMakeLists.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7061d0f51..fab3b54d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -549,7 +549,7 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) if(MSVC) else() - # We can't do --notstdlib++ as we are using exceptions, but we + # We can't do --nostdlib++ as we are using exceptions, but we # can make it a static dependency, which we aren't really using # as the interesting stuff for exceptions is in libgcc_s target_link_options(${name} PRIVATE -static-libstdc++) @@ -576,6 +576,8 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) SNMALLOC_STATIC_LIBRARY_PREFIX=${SNMALLOC_STATIC_LIBRARY_PREFIX}) endif () + compile(snmalloc-new-override STATIC ${NEW}) + if(NOT WIN32) compile(snmallocshim SHARED ${ALLOC}) @@ -586,6 +588,11 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) compile(snmallocshim-checks SHARED ${ALLOC}) endif() target_compile_definitions(snmallocshim-checks PRIVATE SNMALLOC_CHECK_CLIENT) + + compile(snmallocshim-minimal SHARED ${MALLOC}) + if (SNMALLOC_LINKER_SUPPORT_NOSTDLIBXX) + target_link_options(snmallocshim-minimal PRIVATE -nostdlib++) + endif () endif() if(SNMALLOC_RUST_SUPPORT) From 8424e422d8226d4f272185cda1c4c7e7688c7bc9 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 10 Jul 2025 19:54:06 +0100 Subject: [PATCH 10/22] Fix minimal build --- CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fab3b54d7..f22df77cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -589,9 +589,10 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) endif() target_compile_definitions(snmallocshim-checks PRIVATE SNMALLOC_CHECK_CLIENT) - compile(snmallocshim-minimal SHARED ${MALLOC}) - if (SNMALLOC_LINKER_SUPPORT_NOSTDLIBXX) - target_link_options(snmallocshim-minimal PRIVATE -nostdlib++) + compile(snmalloc-minimal SHARED ${MALLOC}) + target_compile_options(snmalloc-minimal PRIVATE -fno-exceptions) + if (SNMALLOC_LINKER_SUPPORT_NOSTDLIBXX AND NOT ${SNMALLOC_CLEANUP} STREQUAL CXX11_DESTRUCTORS) + target_compile_options(snmalloc-minimal PRIVATE -fno-exceptions -nostdlib++) endif () endif() From 14bba78b13aaf77d60c975b99fc665889e19d2df Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 11 Jul 2025 06:14:08 +0100 Subject: [PATCH 11/22] Missing const --- src/snmalloc/override/new.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 6e53c3fe8..1775d5b95 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -103,12 +103,12 @@ void* operator new[](size_t size) return snmalloc::alloc(size); } -void* operator new(size_t size, std::nothrow_t&) +void* operator new(size_t size, const std::nothrow_t&) { return snmalloc::alloc(size); } -void* operator new[](size_t size, std::nothrow_t&) +void* operator new[](size_t size, const std::nothrow_t&) { return snmalloc::alloc(size); } @@ -123,7 +123,7 @@ void operator delete(void* p, size_t size) EXCEPTSPEC snmalloc::libc::free_sized(p, size); } -void operator delete(void* p, std::nothrow_t&) +void operator delete(void* p, const std::nothrow_t&) { snmalloc::libc::free(p); } @@ -138,7 +138,7 @@ void operator delete[](void* p, size_t size) EXCEPTSPEC snmalloc::libc::free_sized(p, size); } -void operator delete[](void* p, std::nothrow_t&) +void operator delete[](void* p, const std::nothrow_t&) { snmalloc::libc::free(p); } @@ -155,13 +155,13 @@ void* operator new[](size_t size, std::align_val_t val) return snmalloc::alloc(size); } -void* operator new(size_t size, std::align_val_t val, std::nothrow_t&) +void* operator new(size_t size, std::align_val_t val, const std::nothrow_t&) { size = snmalloc::aligned_size(size_t(val), size); return snmalloc::alloc(size); } -void* operator new[](size_t size, std::align_val_t val, std::nothrow_t&) +void* operator new[](size_t size, std::align_val_t val, const std::nothrow_t&) { size = snmalloc::aligned_size(size_t(val), size); return snmalloc::alloc(size); From 8b73139540014b314c838d5edc6de33db0706ac4 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 11 Jul 2025 06:18:12 +0100 Subject: [PATCH 12/22] Except spec --- src/snmalloc/override/new.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 1775d5b95..4f980703b 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -103,12 +103,12 @@ void* operator new[](size_t size) return snmalloc::alloc(size); } -void* operator new(size_t size, const std::nothrow_t&) +void* operator new(size_t size, const std::nothrow_t&) EXCEPTSPEC { return snmalloc::alloc(size); } -void* operator new[](size_t size, const std::nothrow_t&) +void* operator new[](size_t size, const std::nothrow_t&) EXCEPTSPEC { return snmalloc::alloc(size); } @@ -123,7 +123,7 @@ void operator delete(void* p, size_t size) EXCEPTSPEC snmalloc::libc::free_sized(p, size); } -void operator delete(void* p, const std::nothrow_t&) +void operator delete(void* p, const std::nothrow_t&) EXCEPTSPEC { snmalloc::libc::free(p); } @@ -138,7 +138,7 @@ void operator delete[](void* p, size_t size) EXCEPTSPEC snmalloc::libc::free_sized(p, size); } -void operator delete[](void* p, const std::nothrow_t&) +void operator delete[](void* p, const std::nothrow_t&) EXCEPTSPEC { snmalloc::libc::free(p); } @@ -155,13 +155,13 @@ void* operator new[](size_t size, std::align_val_t val) return snmalloc::alloc(size); } -void* operator new(size_t size, std::align_val_t val, const std::nothrow_t&) +void* operator new(size_t size, std::align_val_t val, const std::nothrow_t&) EXCEPTSPEC { size = snmalloc::aligned_size(size_t(val), size); return snmalloc::alloc(size); } -void* operator new[](size_t size, std::align_val_t val, const std::nothrow_t&) +void* operator new[](size_t size, std::align_val_t val, const std::nothrow_t&) EXCEPTSPEC { size = snmalloc::aligned_size(size_t(val), size); return snmalloc::alloc(size); From 32c7b39a9d41a39fd4aaef19d76e487dda09fec1 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 11 Jul 2025 06:21:30 +0100 Subject: [PATCH 13/22] clang format --- src/snmalloc/override/new.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 4f980703b..5d61e800e 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -155,13 +155,15 @@ void* operator new[](size_t size, std::align_val_t val) return snmalloc::alloc(size); } -void* operator new(size_t size, std::align_val_t val, const std::nothrow_t&) EXCEPTSPEC +void* operator new(size_t size, std::align_val_t val, const std::nothrow_t&) + EXCEPTSPEC { size = snmalloc::aligned_size(size_t(val), size); return snmalloc::alloc(size); } -void* operator new[](size_t size, std::align_val_t val, const std::nothrow_t&) EXCEPTSPEC +void* operator new[](size_t size, std::align_val_t val, const std::nothrow_t&) + EXCEPTSPEC { size = snmalloc::aligned_size(size_t(val), size); return snmalloc::alloc(size); From 9dea802d6ebc84456c82b32f33b6e50c58c4931b Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 11 Jul 2025 06:31:07 +0100 Subject: [PATCH 14/22] exceptspec --- src/snmalloc/override/new.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 5d61e800e..528b93d80 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -103,12 +103,12 @@ void* operator new[](size_t size) return snmalloc::alloc(size); } -void* operator new(size_t size, const std::nothrow_t&) EXCEPTSPEC +void* operator new(size_t size, const std::nothrow_t&) noexcept { return snmalloc::alloc(size); } -void* operator new[](size_t size, const std::nothrow_t&) EXCEPTSPEC +void* operator new[](size_t size, const std::nothrow_t&) noexcept { return snmalloc::alloc(size); } @@ -123,7 +123,7 @@ void operator delete(void* p, size_t size) EXCEPTSPEC snmalloc::libc::free_sized(p, size); } -void operator delete(void* p, const std::nothrow_t&) EXCEPTSPEC +void operator delete(void* p, const std::nothrow_t&) noexcept { snmalloc::libc::free(p); } @@ -138,7 +138,7 @@ void operator delete[](void* p, size_t size) EXCEPTSPEC snmalloc::libc::free_sized(p, size); } -void operator delete[](void* p, const std::nothrow_t&) EXCEPTSPEC +void operator delete[](void* p, const std::nothrow_t&) noexcept { snmalloc::libc::free(p); } @@ -155,14 +155,14 @@ void* operator new[](size_t size, std::align_val_t val) return snmalloc::alloc(size); } -void* operator new(size_t size, std::align_val_t val, const std::nothrow_t&) +void* operator new(size_t size, std::align_val_t val, const std::nothrow_t&) noexcept EXCEPTSPEC { size = snmalloc::aligned_size(size_t(val), size); return snmalloc::alloc(size); } -void* operator new[](size_t size, std::align_val_t val, const std::nothrow_t&) +void* operator new[](size_t size, std::align_val_t val, const std::nothrow_t&) noexcept EXCEPTSPEC { size = snmalloc::aligned_size(size_t(val), size); From 93c2a40795f34152959cb367df1c46fc877a5b06 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 11 Jul 2025 09:07:04 +0100 Subject: [PATCH 15/22] noexcept again --- src/snmalloc/override/new.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 528b93d80..266910419 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -155,15 +155,15 @@ void* operator new[](size_t size, std::align_val_t val) return snmalloc::alloc(size); } -void* operator new(size_t size, std::align_val_t val, const std::nothrow_t&) noexcept - EXCEPTSPEC +void* operator new( + size_t size, std::align_val_t val, const std::nothrow_t&) noexcept { size = snmalloc::aligned_size(size_t(val), size); return snmalloc::alloc(size); } -void* operator new[](size_t size, std::align_val_t val, const std::nothrow_t&) noexcept - EXCEPTSPEC +void* operator new[]( + size_t size, std::align_val_t val, const std::nothrow_t&) noexcept { size = snmalloc::aligned_size(size_t(val), size); return snmalloc::alloc(size); From 5377c684f2cde334669fa3ed20b844ea0ed4ec15 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 11 Jul 2025 09:32:57 +0100 Subject: [PATCH 16/22] Remove duplicate --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f22df77cf..69864469c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -509,7 +509,6 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) function(compile name TYPE ${ARGN}) add_library(${name} ${TYPE} ${ARGN}) target_link_libraries(${name} snmalloc) - set_target_properties(${name} PROPERTIES CXX_VISIBILITY_PRESET hidden INTERPROCEDURAL_OPTIMIZATION ${SNMALLOC_COMPILER_SUPPORT_IPO}) target_compile_definitions(${name} PRIVATE "SNMALLOC_USE_${SNMALLOC_CLEANUP}") add_warning_flags(${name}) From 792a00b8d3744a4f0c266e6236177ee8bdd2ae13 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 11 Jul 2025 10:43:23 +0100 Subject: [PATCH 17/22] Make internals noexcept to improve codegen --- src/snmalloc/mem/corealloc.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/snmalloc/mem/corealloc.h b/src/snmalloc/mem/corealloc.h index 1a8ce98aa..a260a4fb8 100644 --- a/src/snmalloc/mem/corealloc.h +++ b/src/snmalloc/mem/corealloc.h @@ -994,7 +994,7 @@ namespace snmalloc * deallocation to the other allocators. ***************************************************************************/ template - SNMALLOC_FAST_PATH void dealloc(void* p_raw) + SNMALLOC_FAST_PATH void dealloc(void* p_raw) noexcept { #ifdef __CHERI_PURE_CAPABILITY__ /* @@ -1043,7 +1043,7 @@ namespace snmalloc SNMALLOC_FAST_PATH void dealloc_local_object( CapPtr p, - const typename Config::PagemapEntry& entry) + const typename Config::PagemapEntry& entry) noexcept { auto meta = entry.get_slab_metadata(); @@ -1078,7 +1078,7 @@ namespace snmalloc SNMALLOC_SLOW_PATH void dealloc_local_object_slow( capptr::Alloc p, const PagemapEntry& entry, - BackendSlabMetadata* meta) + BackendSlabMetadata* meta) noexcept { // TODO: Handle message queue on this path? @@ -1274,7 +1274,7 @@ namespace snmalloc template SNMALLOC_FAST_PATH void - dealloc_remote(const PagemapEntry& entry, capptr::Alloc p_tame) + dealloc_remote(const PagemapEntry& entry, capptr::Alloc p_tame) noexcept { if (SNMALLOC_LIKELY(entry.is_owned())) { @@ -1328,7 +1328,7 @@ namespace snmalloc */ template SNMALLOC_SLOW_PATH void - dealloc_remote_slow(const PagemapEntry& entry, capptr::Alloc p) + dealloc_remote_slow(const PagemapEntry& entry, capptr::Alloc p) noexcept { CheckInit::check_init( [this, &entry, p]() SNMALLOC_FAST_PATH_LAMBDA { @@ -1344,7 +1344,7 @@ namespace snmalloc post(); }, - [](Allocator* a, void* p) { + [](Allocator* a, void* p) SNMALLOC_FAST_PATH_LAMBDA { // Recheck what kind of dealloc we should do in case the allocator // we get from lazy_init is the originating allocator. a->dealloc(p); // TODO don't double count statistics From 577834c3037afd1755e67fa4001186b62e7a78b1 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 11 Jul 2025 11:13:11 +0100 Subject: [PATCH 18/22] clangformat --- src/snmalloc/mem/corealloc.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/snmalloc/mem/corealloc.h b/src/snmalloc/mem/corealloc.h index a260a4fb8..5c9965fad 100644 --- a/src/snmalloc/mem/corealloc.h +++ b/src/snmalloc/mem/corealloc.h @@ -1273,8 +1273,8 @@ namespace snmalloc } template - SNMALLOC_FAST_PATH void - dealloc_remote(const PagemapEntry& entry, capptr::Alloc p_tame) noexcept + SNMALLOC_FAST_PATH void dealloc_remote( + const PagemapEntry& entry, capptr::Alloc p_tame) noexcept { if (SNMALLOC_LIKELY(entry.is_owned())) { @@ -1327,8 +1327,8 @@ namespace snmalloc * as we might acquire the originating allocator. */ template - SNMALLOC_SLOW_PATH void - dealloc_remote_slow(const PagemapEntry& entry, capptr::Alloc p) noexcept + SNMALLOC_SLOW_PATH void dealloc_remote_slow( + const PagemapEntry& entry, capptr::Alloc p) noexcept { CheckInit::check_init( [this, &entry, p]() SNMALLOC_FAST_PATH_LAMBDA { From c99f872ee4dfed025335b64bd92742e106ae80f4 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 11 Jul 2025 11:55:52 +0100 Subject: [PATCH 19/22] Make handlers subclasses --- src/snmalloc/override/new.cc | 66 +++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 266910419..7c39e65ea 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -23,28 +23,28 @@ namespace snmalloc { - void* failure_throw(size_t size); - void* failure_nothrow(size_t size); - - template - class SetHandlerContinuations + namespace handler { - public: - static void* success(void* p, size_t size, bool secondary_allocator = false) + class Base { - UNUSED(secondary_allocator, size); - SNMALLOC_ASSERT(p != nullptr); + public: + static void* success(void* p, size_t size, bool secondary_allocator = false) + { + UNUSED(secondary_allocator, size); + SNMALLOC_ASSERT(p != nullptr); - SNMALLOC_ASSERT( - secondary_allocator || - is_start_of_object(size_to_sizeclass_full(size), address_cast(p))); + SNMALLOC_ASSERT( + secondary_allocator || + is_start_of_object(size_to_sizeclass_full(size), address_cast(p))); - return p; - } + return p; + } + }; - static void* failure(size_t size) + class Throw : public Base { - if constexpr (ShouldThrow) + public: + static void* failure(size_t size) { // Throw std::bad_alloc on failure. auto new_handler = std::get_new_handler(); @@ -55,13 +55,18 @@ namespace snmalloc // Retry now new_handler has been called. // I dislike the unbounded retrying here, but that seems to be what // other implementations do. - return alloc(size); + return alloc(size); } // Throw std::bad_alloc on failure. throw std::bad_alloc(); } - else + }; + + class NoThrow : public Base + { + public: + static void* failure(size_t size) { auto new_handler = std::get_new_handler(); if (new_handler != nullptr) @@ -77,7 +82,7 @@ namespace snmalloc return nullptr; } // Retry now new_handler has been called. - return alloc(size); + return alloc(size); } // If we are here, then the allocation failed. // Set errno to ENOMEM, as per the C standard. @@ -86,31 +91,28 @@ namespace snmalloc // Return nullptr on failure. return nullptr; } - } - }; - - using NoThrow = SetHandlerContinuations; - using Throw = SetHandlerContinuations; + }; + } } // namespace snmalloc void* operator new(size_t size) { - return snmalloc::alloc(size); + return snmalloc::alloc(size); } void* operator new[](size_t size) { - return snmalloc::alloc(size); + return snmalloc::alloc(size); } void* operator new(size_t size, const std::nothrow_t&) noexcept { - return snmalloc::alloc(size); + return snmalloc::alloc(size); } void* operator new[](size_t size, const std::nothrow_t&) noexcept { - return snmalloc::alloc(size); + return snmalloc::alloc(size); } void operator delete(void* p) EXCEPTSPEC @@ -146,27 +148,27 @@ void operator delete[](void* p, const std::nothrow_t&) noexcept void* operator new(size_t size, std::align_val_t val) { size = snmalloc::aligned_size(size_t(val), size); - return snmalloc::alloc(size); + return snmalloc::alloc(size); } void* operator new[](size_t size, std::align_val_t val) { size = snmalloc::aligned_size(size_t(val), size); - return snmalloc::alloc(size); + return snmalloc::alloc(size); } void* operator new( size_t size, std::align_val_t val, const std::nothrow_t&) noexcept { size = snmalloc::aligned_size(size_t(val), size); - return snmalloc::alloc(size); + return snmalloc::alloc(size); } void* operator new[]( size_t size, std::align_val_t val, const std::nothrow_t&) noexcept { size = snmalloc::aligned_size(size_t(val), size); - return snmalloc::alloc(size); + return snmalloc::alloc(size); } void operator delete(void* p, std::align_val_t) EXCEPTSPEC From d30d9f4ff7ea1f3aa0e783267064c9d5f66c7d8a Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 11 Jul 2025 13:10:56 +0100 Subject: [PATCH 20/22] Format --- src/snmalloc/override/new.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 7c39e65ea..03d321de2 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -28,7 +28,8 @@ namespace snmalloc class Base { public: - static void* success(void* p, size_t size, bool secondary_allocator = false) + static void* + success(void* p, size_t size, bool secondary_allocator = false) { UNUSED(secondary_allocator, size); SNMALLOC_ASSERT(p != nullptr); From 04b6edc8377f592a2968d718162314b5c50303d1 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 11 Jul 2025 13:45:01 +0100 Subject: [PATCH 21/22] Propogate noexcept as template to remove a few non-tailcalls --- src/snmalloc/mem/corealloc.h | 33 +++++++++++++++++++-------------- src/snmalloc/override/new.cc | 2 +- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/snmalloc/mem/corealloc.h b/src/snmalloc/mem/corealloc.h index 5c9965fad..29eefebe9 100644 --- a/src/snmalloc/mem/corealloc.h +++ b/src/snmalloc/mem/corealloc.h @@ -50,7 +50,7 @@ namespace snmalloc return p; } - static void* failure(size_t size) + static void* failure(size_t size) noexcept { UNUSED(size); // If we are here, then the allocation failed. @@ -401,9 +401,9 @@ namespace snmalloc * the stack as often closing over the arguments would cause less good * codegen. */ - template + template SNMALLOC_FAST_PATH decltype(auto) - handle_message_queue(Action action, Args... args) + handle_message_queue(Action action, Args... args) noexcept(noexc) { // Inline the empty check, but not necessarily the full queue handling. if (SNMALLOC_LIKELY(!has_messages())) @@ -411,15 +411,15 @@ namespace snmalloc return action(args...); } - return handle_message_queue_slow(action, args...); + return handle_message_queue_slow(action, args...); } /** * Process remote frees into this allocator. */ - template + template SNMALLOC_SLOW_PATH decltype(auto) - handle_message_queue_slow(Action action, Args... args) + handle_message_queue_slow(Action action, Args... args) noexcept(noexc) { bool need_post = false; size_t bytes_freed = 0; @@ -621,7 +621,8 @@ namespace snmalloc * Fast allocation for small objects. */ template - SNMALLOC_FAST_PATH void* small_alloc(size_t size) + SNMALLOC_FAST_PATH void* + small_alloc(size_t size) noexcept(noexcept(Conts::failure)) { auto domesticate = [this](freelist::QueuePtr p) SNMALLOC_FAST_PATH_LAMBDA { @@ -637,7 +638,7 @@ namespace snmalloc return finish_alloc(p, size); } - return handle_message_queue( + return handle_message_queue( []( Allocator* alloc, smallsizeclass_t sizeclass, @@ -661,8 +662,8 @@ namespace snmalloc * register. */ template - static SNMALLOC_SLOW_PATH void* - alloc_not_small(size_t size, Allocator* self) + static SNMALLOC_SLOW_PATH void* alloc_not_small( + size_t size, Allocator* self) noexcept(noexcept(Conts::failure)) { if (size == 0) { @@ -672,7 +673,7 @@ namespace snmalloc return self->small_alloc(1); } - return self->handle_message_queue( + return self->template handle_message_queue( [](Allocator* self, size_t size) SNMALLOC_FAST_PATH_LAMBDA { return CheckInit::check_init( [self, size]() SNMALLOC_FAST_PATH_LAMBDA { @@ -739,7 +740,9 @@ namespace snmalloc template SNMALLOC_FAST_PATH void* small_refill( - smallsizeclass_t sizeclass, freelist::Iter<>& fast_free_list, size_t size) + smallsizeclass_t sizeclass, + freelist::Iter<>& fast_free_list, + size_t size) noexcept(noexcept(Conts::failure)) { void* result = Config::SecondaryAllocator::allocate( [size]() -> stl::Pair { @@ -809,7 +812,9 @@ namespace snmalloc template SNMALLOC_SLOW_PATH void* small_refill_slow( - smallsizeclass_t sizeclass, freelist::Iter<>& fast_free_list, size_t size) + smallsizeclass_t sizeclass, + freelist::Iter<>& fast_free_list, + size_t size) noexcept(noexcept(Conts::failure)) { return CheckInit::check_init( [this, size, sizeclass, &fast_free_list]() SNMALLOC_FAST_PATH_LAMBDA { @@ -1385,7 +1390,7 @@ namespace snmalloc // Process incoming message queue // Loop as normally only processes a batch while (has_messages()) - handle_message_queue([]() {}); + handle_message_queue([]() {}); } auto& key = freelist::Object::key_root; diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 03d321de2..7a97c6651 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -67,7 +67,7 @@ namespace snmalloc class NoThrow : public Base { public: - static void* failure(size_t size) + static void* failure(size_t size) noexcept { auto new_handler = std::get_new_handler(); if (new_handler != nullptr) From e8615011213d047375fa9b472ae57cfeefc95310 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 11 Jul 2025 14:14:51 +0100 Subject: [PATCH 22/22] Fix msvc --- src/snmalloc/mem/corealloc.h | 15 ++++++++------- src/snmalloc/mem/ticker.h | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/snmalloc/mem/corealloc.h b/src/snmalloc/mem/corealloc.h index 29eefebe9..d83ab9dc7 100644 --- a/src/snmalloc/mem/corealloc.h +++ b/src/snmalloc/mem/corealloc.h @@ -602,7 +602,8 @@ namespace snmalloc * required. It is defaulted to do nothing. */ template - SNMALLOC_FAST_PATH ALLOCATOR void* alloc(size_t size) + SNMALLOC_FAST_PATH ALLOCATOR void* + alloc(size_t size) noexcept(noexcept(Conts::failure(0))) { // Perform the - 1 on size, so that zero wraps around and ends up on // slow path. @@ -622,7 +623,7 @@ namespace snmalloc */ template SNMALLOC_FAST_PATH void* - small_alloc(size_t size) noexcept(noexcept(Conts::failure)) + small_alloc(size_t size) noexcept(noexcept(Conts::failure(0))) { auto domesticate = [this](freelist::QueuePtr p) SNMALLOC_FAST_PATH_LAMBDA { @@ -638,7 +639,7 @@ namespace snmalloc return finish_alloc(p, size); } - return handle_message_queue( + return handle_message_queue( []( Allocator* alloc, smallsizeclass_t sizeclass, @@ -663,7 +664,7 @@ namespace snmalloc */ template static SNMALLOC_SLOW_PATH void* alloc_not_small( - size_t size, Allocator* self) noexcept(noexcept(Conts::failure)) + size_t size, Allocator* self) noexcept(noexcept(Conts::failure(0))) { if (size == 0) { @@ -673,7 +674,7 @@ namespace snmalloc return self->small_alloc(1); } - return self->template handle_message_queue( + return self->template handle_message_queue( [](Allocator* self, size_t size) SNMALLOC_FAST_PATH_LAMBDA { return CheckInit::check_init( [self, size]() SNMALLOC_FAST_PATH_LAMBDA { @@ -742,7 +743,7 @@ namespace snmalloc SNMALLOC_FAST_PATH void* small_refill( smallsizeclass_t sizeclass, freelist::Iter<>& fast_free_list, - size_t size) noexcept(noexcept(Conts::failure)) + size_t size) noexcept(noexcept(Conts::failure(0))) { void* result = Config::SecondaryAllocator::allocate( [size]() -> stl::Pair { @@ -814,7 +815,7 @@ namespace snmalloc SNMALLOC_SLOW_PATH void* small_refill_slow( smallsizeclass_t sizeclass, freelist::Iter<>& fast_free_list, - size_t size) noexcept(noexcept(Conts::failure)) + size_t size) noexcept(noexcept(Conts::failure(0))) { return CheckInit::check_init( [this, size, sizeclass, &fast_free_list]() SNMALLOC_FAST_PATH_LAMBDA { diff --git a/src/snmalloc/mem/ticker.h b/src/snmalloc/mem/ticker.h index c48ff193e..33e3819fb 100644 --- a/src/snmalloc/mem/ticker.h +++ b/src/snmalloc/mem/ticker.h @@ -41,7 +41,7 @@ namespace snmalloc * how many calls for the next time we hit the slow path. */ template - SNMALLOC_SLOW_PATH T check_tick_slow(T p = nullptr) + SNMALLOC_SLOW_PATH T check_tick_slow(T p = nullptr) noexcept { uint64_t now_ms = PAL::time_in_ms();