From 7e41662d394641ac976ce934d5d38203fa84e259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Sun, 29 Mar 2026 15:34:42 +0200 Subject: [PATCH 01/31] Add intrastructure for new.cc tests --- src/test/func/new/new.cc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/test/func/new/new.cc diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc new file mode 100644 index 000000000..28c99f954 --- /dev/null +++ b/src/test/func/new/new.cc @@ -0,0 +1,18 @@ +#include +#include +#include + +#include + +using namespace snmalloc; + +int main(int argc, char** argv) +{ + UNUSED(argc); + UNUSED(argv); + + setup(); + + snmalloc::debug_check_empty(); + return 0; +} From b2e88d96785571187331bbd65042e4a654c58f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Sun, 29 Mar 2026 15:36:14 +0200 Subject: [PATCH 02/31] Test deletion of null pointers --- src/test/func/new/new.cc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index 28c99f954..0410452c6 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -6,6 +6,21 @@ using namespace snmalloc; +void test_delete_null() +{ + operator delete(nullptr, 42); + operator delete(nullptr, std::nothrow); + operator delete(nullptr, 42, std::align_val_t{OS_PAGE_SIZE}); + operator delete(nullptr, std::align_val_t{OS_PAGE_SIZE}); + operator delete(nullptr, std::align_val_t{OS_PAGE_SIZE}, std::nothrow); + operator delete[](nullptr); + operator delete[](nullptr, 42); + operator delete[](nullptr, std::nothrow); + operator delete[](nullptr, std::align_val_t{OS_PAGE_SIZE}); + operator delete[](nullptr, 42, std::align_val_t{OS_PAGE_SIZE}); + operator delete[](nullptr, std::align_val_t{OS_PAGE_SIZE}, std::nothrow); +} + int main(int argc, char** argv) { UNUSED(argc); @@ -13,6 +28,9 @@ int main(int argc, char** argv) setup(); + START_TEST("Test delete / delete[] nullptr"); + test_delete_null(); + snmalloc::debug_check_empty(); return 0; } From fb8d89f19ca7437335e5c18098290d863885b40d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Sun, 29 Mar 2026 15:37:26 +0200 Subject: [PATCH 03/31] Test simple allocation and deletion --- src/test/func/new/new.cc | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index 0410452c6..635c6996f 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -21,6 +21,43 @@ void test_delete_null() operator delete[](nullptr, std::align_val_t{OS_PAGE_SIZE}, std::nothrow); } +template +void test_zero_alloc(New_Func new_func) +{ + void* non_zero = new_func(0); + EXPECT(non_zero, "allocation with size '0' did not return a valid pointer"); + operator delete(non_zero); +} + +// SNMALLOC_EXPORT void* operator new(size_t size) +// SNMALLOC_EXPORT void* operator new[](size_t size) +// SNMALLOC_EXPORT void operator delete(void* p) EXCEPTSPEC +// SNMALLOC_EXPORT void* operator delete[](void* p) EXCEPTSPEC +template +void test_new_delete_simple(size_t size) +{ + void* non_zero = new_fun(size); + bool caught_bad_alloc = false; + EXPECT(non_zero, "expected valid address, but instead got {}", non_zero); + del_fun(non_zero); + + test_zero_alloc(new_fun); + + try + { + void* impossible_alloc = new_fun(static_cast(-1)); + del_fun(impossible_alloc); + } + catch (std::bad_alloc& e) + { + caught_bad_alloc = true; + } + + EXPECT( + caught_bad_alloc, + "Impossible allocation did not throw std::bad_alloc exception"); +} + int main(int argc, char** argv) { UNUSED(argc); @@ -31,6 +68,11 @@ int main(int argc, char** argv) START_TEST("Test delete / delete[] nullptr"); test_delete_null(); + START_TEST("Test new / delete simple"); + test_new_delete_simple < operator new, operator delete>(42); + START_TEST("Test new[] / delete[] simple"); + test_new_delete_simple < operator new[], operator delete[]>(42); + snmalloc::debug_check_empty(); return 0; } From 37175ae6f436d0e6343492589a8b71f7ceaf04ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Sun, 29 Mar 2026 15:37:52 +0200 Subject: [PATCH 04/31] Test simple allocation and deletion with `std::nothrow` --- src/test/func/new/new.cc | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index 635c6996f..fa894cf1f 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -58,6 +58,29 @@ void test_new_delete_simple(size_t size) "Impossible allocation did not throw std::bad_alloc exception"); } +// SNMALLOC_EXPORT void* operator new(size_t size, const std::nothrow_t&) +// noexcept +// SNMALLOC_EXPORT void* operator new[](size_t size, const std::nothrow_t&) +// noexcept +// SNMALLOC_EXPORT void* operator delete(size_t size, const std::nothrow_t&) +// noexcept +// SNMALLOC_EXPORT void* operator delete[](size_t size, const std::nothrow_t&) +// noexcept +template< + void* (*new_fun)(size_t, const std::nothrow_t&), + void (*del_fun)(void*, const std::nothrow_t&) EXCEPTSPEC> +void test_new_delete_nothrow() +{ + void* impossible_alloc_unaligned = + new_fun(static_cast(-1), std::nothrow); + EXPECT( + impossible_alloc_unaligned == nullptr, + "Impossible allocation should not succeed"); + del_fun(impossible_alloc_unaligned, std::nothrow); + + test_zero_alloc([](size_t s) { return new_fun(s, std::nothrow); }); +} + int main(int argc, char** argv) { UNUSED(argc); @@ -73,6 +96,11 @@ int main(int argc, char** argv) START_TEST("Test new[] / delete[] simple"); test_new_delete_simple < operator new[], operator delete[]>(42); + START_TEST("Test new / delete nothrow"); + test_new_delete_nothrow < operator new, operator delete>(); + START_TEST("Test new[] / delete[] nothrow"); + test_new_delete_nothrow < operator new[], operator delete[]>(); + snmalloc::debug_check_empty(); return 0; } From 140a2b1eb61519886142e0ec81f478c0a165e681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Sun, 29 Mar 2026 15:38:37 +0200 Subject: [PATCH 05/31] Test sized deletion --- src/test/func/new/new.cc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index fa894cf1f..4ac1d9914 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -81,6 +81,16 @@ void test_new_delete_nothrow() test_zero_alloc([](size_t s) { return new_fun(s, std::nothrow); }); } +// SNMALLOC_EXPORT void operator delete(void* p, size_t size) EXCEPTSPEC +// SNMALLOC_EXPORT void operator delete[](void* p, size_t size) EXCEPTSPEC +template +void test_delete_size(size_t size) +{ + void* non_zero = new_fun(size); + EXPECT(non_zero, "expected valid address, but instead got {}", non_zero); + del_fun(non_zero, size); +} + int main(int argc, char** argv) { UNUSED(argc); @@ -101,6 +111,11 @@ int main(int argc, char** argv) START_TEST("Test new[] / delete[] nothrow"); test_new_delete_nothrow < operator new[], operator delete[]>(); + START_TEST("Test delete with size parameter"); + test_delete_size < operator new, operator delete>(42); + START_TEST("Test delete[] with size parameter"); + test_delete_size < operator new[], operator delete[]>(42); + snmalloc::debug_check_empty(); return 0; } From c36289115f7627556fde8ad91d069528c66b9bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Sun, 29 Mar 2026 15:39:06 +0200 Subject: [PATCH 06/31] Test aligned allocation and deletion --- src/test/func/new/new.cc | 31 +++++++++++++++++++++++++++++++ src/test/helpers.h | 8 ++++++++ 2 files changed, 39 insertions(+) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index 4ac1d9914..0dfc34b73 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -6,6 +6,9 @@ using namespace snmalloc; +constexpr std::array align_val_sizes = { + 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, OS_PAGE_SIZE}; + void test_delete_null() { operator delete(nullptr, 42); @@ -91,6 +94,29 @@ void test_delete_size(size_t size) del_fun(non_zero, size); } +// SNMALLOC_EXPORT void* operator new(size_t size, std::align_val_t val) +// SNMALLOC_EXPORT void* operator new[](size_t size, std::align_val_t val) +// SNMALLOC_EXPORT void operator delete(void* p, std::align_val_t) EXCEPTSPEC +// SNMALLOC_EXPORT void operator delete[](void* p, std::align_val_t) EXCEPTSPEC +template< + void* (*new_fun)(size_t, std::align_val_t), + void (*del_fun)(void*, std::align_val_t) EXCEPTSPEC> +void test_new_delete_aligned(size_t size) +{ + for (auto& align_val_size : align_val_sizes) + { + std::align_val_t align_val{align_val_size}; + void* aligned_mem = new_fun(size, align_val); + EXPECT( + is_aligned(aligned_mem, align_val_size), + "Memory was not aligned on value {}", + align_val_size); + del_fun(aligned_mem, align_val); + + test_zero_alloc([&align_val](size_t s) { return new_fun(s, align_val); }); + } +} + int main(int argc, char** argv) { UNUSED(argc); @@ -116,6 +142,11 @@ int main(int argc, char** argv) START_TEST("Test delete[] with size parameter"); test_delete_size < operator new[], operator delete[]>(42); + START_TEST("Test new / delete aligned"); + test_new_delete_aligned < operator new, operator delete>(42); + START_TEST("Test new[] / delete[] aligned"); + test_new_delete_aligned < operator new[], operator delete[]>(42); + snmalloc::debug_check_empty(); return 0; } diff --git a/src/test/helpers.h b/src/test/helpers.h index 30f6e4655..7bc71bd8b 100644 --- a/src/test/helpers.h +++ b/src/test/helpers.h @@ -1,4 +1,6 @@ #pragma once +#include +#include #ifdef _MSC_VER # define __PRETTY_FUNCTION__ __FUNCSIG__ #endif @@ -37,3 +39,9 @@ namespace snmalloc } while (0) } + +// Based on: +// https://en.cppreference.com/w/cpp/memory/is_sufficiently_aligned.html +bool is_aligned(void* ptr, std::size_t align_val_size){ + return std::bit_cast(ptr) % align_val_size == 0; +} From e1e68018360894e199125301084151a5eb436bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Sun, 29 Mar 2026 15:39:44 +0200 Subject: [PATCH 07/31] Test aligned allocation with `std::nothrow` --- src/test/func/new/new.cc | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index 0dfc34b73..1b33aa6cc 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -117,6 +117,27 @@ void test_new_delete_aligned(size_t size) } } +// SNMALLOC_EXPORT void* operator new(size_t size, std::align_val_t val, const +// std::nothrow_t&) noexcept +// SNMALLOC_EXPORT void* operator new[](size_t size, +// std::align_val_t val, const std::nothrow_t&) noexcept +template< + void* (*new_fun)(size_t, std::align_val_t, const std::nothrow_t&), + void (*del_fun)(void*, std::align_val_t, const std::nothrow_t&) EXCEPTSPEC> +void test_new_delete_aligned_nothrow() +{ + std::align_val_t page_size{OS_PAGE_SIZE}; + void* impossible_alloc_aligned = + new_fun(static_cast(-1), page_size, std::nothrow); + EXPECT( + impossible_alloc_aligned == nullptr, + "Impossible allocation should not succeed"); + del_fun(impossible_alloc_aligned, page_size, std::nothrow); + + test_zero_alloc( + [&page_size](size_t s) { return new_fun(s, page_size, std::nothrow); }); +} + int main(int argc, char** argv) { UNUSED(argc); @@ -147,6 +168,11 @@ int main(int argc, char** argv) START_TEST("Test new[] / delete[] aligned"); test_new_delete_aligned < operator new[], operator delete[]>(42); + START_TEST("Test non-throwing aligned new / delete"); + test_new_delete_aligned_nothrow < operator new, operator delete>(); + START_TEST("Test non-throwing aligned new[] / delete[]"); + test_new_delete_aligned_nothrow < operator new[], operator delete[]>(); + snmalloc::debug_check_empty(); return 0; } From cf610e23e0e55533cf59ca03451b565d59d24606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Sun, 29 Mar 2026 15:40:26 +0200 Subject: [PATCH 08/31] Test aligned sized delete with `std::nothrow` --- src/test/func/new/new.cc | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index 1b33aa6cc..8c2ce9024 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -138,6 +138,27 @@ void test_new_delete_aligned_nothrow() [&page_size](size_t s) { return new_fun(s, page_size, std::nothrow); }); } +// SNMALLOC_EXPORT void operator delete(void* p, size_t size, std::align_val_t +// val) EXCEPTSPEC +// SNMALLOC_EXPORT void operator delete[](void* p, size_t size, +// std::align_val_t val) EXCEPTSPEC +template< + void* (*new_fun)(size_t, std::align_val_t), + void (*del_fun)(void*, size_t, std::align_val_t) EXCEPTSPEC> +void test_delete_size_aligned(size_t size) +{ + for (auto& align_val_size : align_val_sizes) + { + std::align_val_t align_val{align_val_size}; + void* aligned_mem = new_fun(size, align_val); + EXPECT( + is_aligned(aligned_mem, align_val_size), + "Memory was not aligned on value {}", + align_val_size); + del_fun(aligned_mem, size, align_val); + } +} + int main(int argc, char** argv) { UNUSED(argc); @@ -173,6 +194,11 @@ int main(int argc, char** argv) START_TEST("Test non-throwing aligned new[] / delete[]"); test_new_delete_aligned_nothrow < operator new[], operator delete[]>(); + START_TEST("Test non-throwing aligned delete with explicit size"); + test_delete_size_aligned < operator new, operator delete>(42); + START_TEST("Test non-throwing aligned delete[] with explicit size"); + test_delete_size_aligned < operator new[], operator delete[]>(42); + snmalloc::debug_check_empty(); return 0; } From 7044b9a828097c1881381502e698c5e787ceaa3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Sun, 29 Mar 2026 19:26:30 +0200 Subject: [PATCH 09/31] Fix formatting --- src/test/func/new/new.cc | 3 +-- src/test/helpers.h | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index 8c2ce9024..acd140a18 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -1,9 +1,8 @@ +#include #include #include #include -#include - using namespace snmalloc; constexpr std::array align_val_sizes = { diff --git a/src/test/helpers.h b/src/test/helpers.h index 7bc71bd8b..9e1653dd2 100644 --- a/src/test/helpers.h +++ b/src/test/helpers.h @@ -42,6 +42,7 @@ namespace snmalloc // Based on: // https://en.cppreference.com/w/cpp/memory/is_sufficiently_aligned.html -bool is_aligned(void* ptr, std::size_t align_val_size){ +bool is_aligned(void* ptr, std::size_t align_val_size) +{ return std::bit_cast(ptr) % align_val_size == 0; } From d49ce05e64a236eb39329ac907509e2362eed5ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Mon, 30 Mar 2026 10:58:23 +0000 Subject: [PATCH 10/31] Don't explicilty name std::bad_alloc exception when catching it Co-authored-by: Matthew Parkinson --- src/test/func/new/new.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index acd140a18..719a17f46 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -50,7 +50,7 @@ void test_new_delete_simple(size_t size) void* impossible_alloc = new_fun(static_cast(-1)); del_fun(impossible_alloc); } - catch (std::bad_alloc& e) + catch (std::bad_alloc&) { caught_bad_alloc = true; } From 297d13618a1b36dab117b7f1b1a65f2577fda800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Mon, 30 Mar 2026 12:59:27 +0200 Subject: [PATCH 11/31] include `` explicitly Co-authored-by: Matthew Parkinson --- src/test/func/new/new.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index 719a17f46..a5175ced7 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -1,3 +1,4 @@ +#include #include #include #include From df065fce24952354cdeb36980b14a21bcebd32d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Mon, 30 Mar 2026 13:00:13 +0200 Subject: [PATCH 12/31] Replace `std::bit_cast` with `reinterpret_cast` Co-authored-by: Matthew Parkinson --- src/test/helpers.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/helpers.h b/src/test/helpers.h index 9e1653dd2..58486482f 100644 --- a/src/test/helpers.h +++ b/src/test/helpers.h @@ -1,5 +1,4 @@ #pragma once -#include #include #ifdef _MSC_VER # define __PRETTY_FUNCTION__ __FUNCSIG__ @@ -44,5 +43,5 @@ namespace snmalloc // https://en.cppreference.com/w/cpp/memory/is_sufficiently_aligned.html bool is_aligned(void* ptr, std::size_t align_val_size) { - return std::bit_cast(ptr) % align_val_size == 0; + return reinterpret_cast(ptr) % align_val_size == 0; } From 62c36358cf03ffafdaa628e39c51e364c031fe9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Mon, 30 Mar 2026 18:39:38 +0200 Subject: [PATCH 13/31] Include `` for use of `std::size_t` --- src/test/helpers.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/helpers.h b/src/test/helpers.h index 58486482f..6fa520969 100644 --- a/src/test/helpers.h +++ b/src/test/helpers.h @@ -1,4 +1,5 @@ #pragma once +#include #include #ifdef _MSC_VER # define __PRETTY_FUNCTION__ __FUNCSIG__ From a7ae5695bee4d7d66d45af3363af63dc699c31ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Tue, 7 Apr 2026 16:05:22 +0200 Subject: [PATCH 14/31] Disable tests when `SNMALLOC_USE_SELF_VENDORED_STL=ON` is set --- src/test/func/new/new.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index a5175ced7..7e2b98c4a 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -6,6 +6,12 @@ using namespace snmalloc; +#ifdef SNMALLOC_USE_SELF_VENDORED_STL +int main() +{ + return 0; +} +#else constexpr std::array align_val_sizes = { 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, OS_PAGE_SIZE}; @@ -202,3 +208,4 @@ int main(int argc, char** argv) snmalloc::debug_check_empty(); return 0; } +#endif From 69dd42869c58b8788c3073b027b3b119d1407f9b Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 17 Apr 2026 14:02:07 +0100 Subject: [PATCH 15/31] Update src/test/func/new/new.cc --- src/test/func/new/new.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index 7e2b98c4a..fe15516b6 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -6,7 +6,7 @@ using namespace snmalloc; -#ifdef SNMALLOC_USE_SELF_VENDORED_STL +#if defined(SNMALLOC_USE_SELF_VENDORED_STL) || defined(SNMALLOC_THREAD_SANITIZER_ENABLED) int main() { return 0; From 886fd37c7b3d5c42a90d83dd57effc80779c25a2 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 17 Apr 2026 14:09:34 +0100 Subject: [PATCH 16/31] Update src/test/func/new/new.cc --- src/test/func/new/new.cc | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index fe15516b6..9d6b5e2d3 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -1,17 +1,16 @@ -#include -#include -#include -#include -#include - -using namespace snmalloc; - #if defined(SNMALLOC_USE_SELF_VENDORED_STL) || defined(SNMALLOC_THREAD_SANITIZER_ENABLED) int main() { return 0; } #else +# include +# include +# include +# include +# include + +using namespace snmalloc; constexpr std::array align_val_sizes = { 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, OS_PAGE_SIZE}; From 5fd0941fc60243814e08f9ce82fd43b7b9699b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Fri, 17 Apr 2026 15:12:08 +0200 Subject: [PATCH 17/31] clang-format --- src/test/func/new/new.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index 9d6b5e2d3..a965c6b89 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -1,4 +1,5 @@ -#if defined(SNMALLOC_USE_SELF_VENDORED_STL) || defined(SNMALLOC_THREAD_SANITIZER_ENABLED) +#if defined(SNMALLOC_USE_SELF_VENDORED_STL) || \ + defined(SNMALLOC_THREAD_SANITIZER_ENABLED) int main() { return 0; From 439a42aa478493bbaf9dbd26b33682073f40978c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Mon, 27 Apr 2026 09:48:18 +0200 Subject: [PATCH 18/31] Add print statement for debugging CI failure as suggested by @devnexen in https://github.com/microsoft/snmalloc/pull/839#issuecomment-4323011821 --- src/snmalloc/override/new.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 667ca9c45..6b8179fb7 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -61,6 +61,7 @@ namespace snmalloc } // Throw std::bad_alloc on failure. + write(2, "throw\n", 6); throw std::bad_alloc(); } }; From 8a86e1ceff0babcb8c4d811cbb3ef5617aa3f5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Mon, 27 Apr 2026 22:12:48 +0200 Subject: [PATCH 19/31] Enable SNMALLOC_TRACING in CI --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 237e5a749..7947e85c9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -216,7 +216,7 @@ jobs: vm-version: '9.2' build-type: Release dependencies: "/usr/sbin/pkg_add cmake ninja-build gcc10" - cmake-flags: -DCMAKE_CXX_COMPILER=/usr/pkg/gcc10/bin/g++ + cmake-flags: "-DCMAKE_CXX_COMPILER=/usr/pkg/gcc10/bin/g++ -DSNMALLOC_TRACING=1" # ============================================================================ # QEMU cross-compilation builds From 8d1bfb7b77631978a3233b3aba4cc9bb4cdb35ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Mon, 27 Apr 2026 22:14:22 +0200 Subject: [PATCH 20/31] Add debug print "begin throw case" Co-authored-by: Matthew Parkinson --- src/snmalloc/override/new.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 6b8179fb7..9374df898 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -48,6 +48,7 @@ namespace snmalloc public: static void* failure(size_t size) { + write(2, "begin throw case\n", 18); // Throw std::bad_alloc on failure. auto new_handler = std::get_new_handler(); if (new_handler != nullptr) From 3ef38147f428c3b0789a9455c873ca74ed9a9790 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 28 Apr 2026 14:02:35 +0100 Subject: [PATCH 21/31] Apply suggestions from code review Co-authored-by: Matthew Parkinson --- src/snmalloc/override/new.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 9374df898..ffbd84f2d 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -72,6 +72,7 @@ namespace snmalloc public: static void* failure(size_t size) noexcept { + write(2, "begin nothrow case\n", 20); auto new_handler = std::get_new_handler(); if (new_handler != nullptr) { @@ -101,22 +102,26 @@ namespace snmalloc SNMALLOC_EXPORT void* operator new(size_t size) { + write(2, "begin new case\n", 16); return snmalloc::alloc(size); } SNMALLOC_EXPORT void* operator new[](size_t size) { + write(2, "begin new[] case\n", 18); return snmalloc::alloc(size); } SNMALLOC_EXPORT void* operator new(size_t size, const std::nothrow_t&) noexcept { + write(2, "begin new_nt case\n", 19); return snmalloc::alloc(size); } SNMALLOC_EXPORT void* operator new[](size_t size, const std::nothrow_t&) noexcept { + write(2, "begin new_nt[] case\n", 21); return snmalloc::alloc(size); } From 3e2b52ca9bcf0bfdf4499c39ba5288538639035c Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 28 Apr 2026 14:33:17 +0100 Subject: [PATCH 22/31] Apply suggestions from code review Co-authored-by: Matthew Parkinson --- src/snmalloc/override/new.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index ffbd84f2d..1358bbf73 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -48,7 +48,7 @@ namespace snmalloc public: static void* failure(size_t size) { - write(2, "begin throw case\n", 18); + snmalloc::message("begin throw case\n"); // Throw std::bad_alloc on failure. auto new_handler = std::get_new_handler(); if (new_handler != nullptr) @@ -62,7 +62,7 @@ namespace snmalloc } // Throw std::bad_alloc on failure. - write(2, "throw\n", 6); + snmalloc::message("throw\n"); throw std::bad_alloc(); } }; @@ -72,7 +72,7 @@ namespace snmalloc public: static void* failure(size_t size) noexcept { - write(2, "begin nothrow case\n", 20); + snmalloc::message("begin nothrow case\n"); auto new_handler = std::get_new_handler(); if (new_handler != nullptr) { @@ -102,26 +102,26 @@ namespace snmalloc SNMALLOC_EXPORT void* operator new(size_t size) { - write(2, "begin new case\n", 16); + snmalloc::message("begin new case\n"); return snmalloc::alloc(size); } SNMALLOC_EXPORT void* operator new[](size_t size) { - write(2, "begin new[] case\n", 18); + snmalloc::message("begin new[] case\n"); return snmalloc::alloc(size); } SNMALLOC_EXPORT void* operator new(size_t size, const std::nothrow_t&) noexcept { - write(2, "begin new_nt case\n", 19); + snmalloc::message("begin new_nt case\n"); return snmalloc::alloc(size); } SNMALLOC_EXPORT void* operator new[](size_t size, const std::nothrow_t&) noexcept { - write(2, "begin new_nt[] case\n", 21); + snmalloc::message("begin new_nt[] case\n"); return snmalloc::alloc(size); } From 11010fe7fe1257cb1c83568da39b646a3d26e7f4 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 29 Apr 2026 09:03:00 +0100 Subject: [PATCH 23/31] Use lambdas instead of function pointers for operator new/delete tests On NetBSD with GCC 10, taking the address of operator new/delete as function pointer template arguments can resolve to the libstdc++ versions rather than the snmalloc overrides defined in the same translation unit. This happens because the function pointer is resolved via PLT indirection on some ELF platforms. Replace the function-pointer template parameters with lambda arguments that make direct calls to operator new/delete. Direct calls are always resolved to the definitions in the translation unit, avoiding the platform-dependent symbol resolution issue. --- src/test/func/new/new.cc | 119 ++++++++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 45 deletions(-) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index a965c6b89..5de60a4e7 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -30,27 +30,27 @@ void test_delete_null() operator delete[](nullptr, std::align_val_t{OS_PAGE_SIZE}, std::nothrow); } -template -void test_zero_alloc(New_Func new_func) +template +void test_zero_alloc(New_Func new_func, Del_Func del_func) { void* non_zero = new_func(0); EXPECT(non_zero, "allocation with size '0' did not return a valid pointer"); - operator delete(non_zero); + del_func(non_zero); } // SNMALLOC_EXPORT void* operator new(size_t size) // SNMALLOC_EXPORT void* operator new[](size_t size) // SNMALLOC_EXPORT void operator delete(void* p) EXCEPTSPEC // SNMALLOC_EXPORT void* operator delete[](void* p) EXCEPTSPEC -template -void test_new_delete_simple(size_t size) +template +void test_new_delete_simple(New_Func new_fun, Del_Func del_fun, size_t size) { void* non_zero = new_fun(size); bool caught_bad_alloc = false; EXPECT(non_zero, "expected valid address, but instead got {}", non_zero); del_fun(non_zero); - test_zero_alloc(new_fun); + test_zero_alloc(new_fun, del_fun); try { @@ -75,25 +75,22 @@ void test_new_delete_simple(size_t size) // noexcept // SNMALLOC_EXPORT void* operator delete[](size_t size, const std::nothrow_t&) // noexcept -template< - void* (*new_fun)(size_t, const std::nothrow_t&), - void (*del_fun)(void*, const std::nothrow_t&) EXCEPTSPEC> -void test_new_delete_nothrow() +template +void test_new_delete_nothrow(New_Func new_fun, Del_Func del_fun) { - void* impossible_alloc_unaligned = - new_fun(static_cast(-1), std::nothrow); + void* impossible_alloc_unaligned = new_fun(static_cast(-1)); EXPECT( impossible_alloc_unaligned == nullptr, "Impossible allocation should not succeed"); - del_fun(impossible_alloc_unaligned, std::nothrow); + del_fun(impossible_alloc_unaligned); - test_zero_alloc([](size_t s) { return new_fun(s, std::nothrow); }); + test_zero_alloc(new_fun, del_fun); } // SNMALLOC_EXPORT void operator delete(void* p, size_t size) EXCEPTSPEC // SNMALLOC_EXPORT void operator delete[](void* p, size_t size) EXCEPTSPEC -template -void test_delete_size(size_t size) +template +void test_delete_size(New_Func new_fun, Del_Func del_fun, size_t size) { void* non_zero = new_fun(size); EXPECT(non_zero, "expected valid address, but instead got {}", non_zero); @@ -104,10 +101,8 @@ void test_delete_size(size_t size) // SNMALLOC_EXPORT void* operator new[](size_t size, std::align_val_t val) // SNMALLOC_EXPORT void operator delete(void* p, std::align_val_t) EXCEPTSPEC // SNMALLOC_EXPORT void operator delete[](void* p, std::align_val_t) EXCEPTSPEC -template< - void* (*new_fun)(size_t, std::align_val_t), - void (*del_fun)(void*, std::align_val_t) EXCEPTSPEC> -void test_new_delete_aligned(size_t size) +template +void test_new_delete_aligned(New_Func new_fun, Del_Func del_fun, size_t size) { for (auto& align_val_size : align_val_sizes) { @@ -119,7 +114,9 @@ void test_new_delete_aligned(size_t size) align_val_size); del_fun(aligned_mem, align_val); - test_zero_alloc([&align_val](size_t s) { return new_fun(s, align_val); }); + test_zero_alloc( + [&align_val, &new_fun](size_t s) { return new_fun(s, align_val); }, + [&align_val, &del_fun](void* p) { del_fun(p, align_val); }); } } @@ -127,31 +124,27 @@ void test_new_delete_aligned(size_t size) // std::nothrow_t&) noexcept // SNMALLOC_EXPORT void* operator new[](size_t size, // std::align_val_t val, const std::nothrow_t&) noexcept -template< - void* (*new_fun)(size_t, std::align_val_t, const std::nothrow_t&), - void (*del_fun)(void*, std::align_val_t, const std::nothrow_t&) EXCEPTSPEC> -void test_new_delete_aligned_nothrow() +template +void test_new_delete_aligned_nothrow(New_Func new_fun, Del_Func del_fun) { std::align_val_t page_size{OS_PAGE_SIZE}; - void* impossible_alloc_aligned = - new_fun(static_cast(-1), page_size, std::nothrow); + void* impossible_alloc_aligned = new_fun(static_cast(-1), page_size); EXPECT( impossible_alloc_aligned == nullptr, "Impossible allocation should not succeed"); - del_fun(impossible_alloc_aligned, page_size, std::nothrow); + del_fun(impossible_alloc_aligned, page_size); test_zero_alloc( - [&page_size](size_t s) { return new_fun(s, page_size, std::nothrow); }); + [&page_size, &new_fun](size_t s) { return new_fun(s, page_size); }, + [&page_size, &del_fun](void* p) { del_fun(p, page_size); }); } // SNMALLOC_EXPORT void operator delete(void* p, size_t size, std::align_val_t // val) EXCEPTSPEC // SNMALLOC_EXPORT void operator delete[](void* p, size_t size, // std::align_val_t val) EXCEPTSPEC -template< - void* (*new_fun)(size_t, std::align_val_t), - void (*del_fun)(void*, size_t, std::align_val_t) EXCEPTSPEC> -void test_delete_size_aligned(size_t size) +template +void test_delete_size_aligned(New_Func new_fun, Del_Func del_fun, size_t size) { for (auto& align_val_size : align_val_sizes) { @@ -176,34 +169,70 @@ int main(int argc, char** argv) test_delete_null(); START_TEST("Test new / delete simple"); - test_new_delete_simple < operator new, operator delete>(42); + test_new_delete_simple( + [](size_t s) { return operator new(s); }, + [](void* p) { operator delete(p); }, + 42); START_TEST("Test new[] / delete[] simple"); - test_new_delete_simple < operator new[], operator delete[]>(42); + test_new_delete_simple( + [](size_t s) { return operator new[](s); }, + [](void* p) { operator delete[](p); }, + 42); START_TEST("Test new / delete nothrow"); - test_new_delete_nothrow < operator new, operator delete>(); + test_new_delete_nothrow( + [](size_t s) { return operator new(s, std::nothrow); }, + [](void* p) { operator delete(p, std::nothrow); }); START_TEST("Test new[] / delete[] nothrow"); - test_new_delete_nothrow < operator new[], operator delete[]>(); + test_new_delete_nothrow( + [](size_t s) { return operator new[](s, std::nothrow); }, + [](void* p) { operator delete[](p, std::nothrow); }); START_TEST("Test delete with size parameter"); - test_delete_size < operator new, operator delete>(42); + test_delete_size( + [](size_t s) { return operator new(s); }, + [](void* p, size_t sz) { operator delete(p, sz); }, + 42); START_TEST("Test delete[] with size parameter"); - test_delete_size < operator new[], operator delete[]>(42); + test_delete_size( + [](size_t s) { return operator new[](s); }, + [](void* p, size_t sz) { operator delete[](p, sz); }, + 42); START_TEST("Test new / delete aligned"); - test_new_delete_aligned < operator new, operator delete>(42); + test_new_delete_aligned( + [](size_t s, std::align_val_t a) { return operator new(s, a); }, + [](void* p, std::align_val_t a) { operator delete(p, a); }, + 42); START_TEST("Test new[] / delete[] aligned"); - test_new_delete_aligned < operator new[], operator delete[]>(42); + test_new_delete_aligned( + [](size_t s, std::align_val_t a) { return operator new[](s, a); }, + [](void* p, std::align_val_t a) { operator delete[](p, a); }, + 42); START_TEST("Test non-throwing aligned new / delete"); - test_new_delete_aligned_nothrow < operator new, operator delete>(); + test_new_delete_aligned_nothrow( + [](size_t s, std::align_val_t a) { + return operator new(s, a, std::nothrow); + }, + [](void* p, std::align_val_t a) { operator delete(p, a, std::nothrow); }); START_TEST("Test non-throwing aligned new[] / delete[]"); - test_new_delete_aligned_nothrow < operator new[], operator delete[]>(); + test_new_delete_aligned_nothrow( + [](size_t s, std::align_val_t a) { + return operator new[](s, a, std::nothrow); + }, + [](void* p, std::align_val_t a) { operator delete[](p, a, std::nothrow); }); START_TEST("Test non-throwing aligned delete with explicit size"); - test_delete_size_aligned < operator new, operator delete>(42); + test_delete_size_aligned( + [](size_t s, std::align_val_t a) { return operator new(s, a); }, + [](void* p, size_t sz, std::align_val_t a) { operator delete(p, sz, a); }, + 42); START_TEST("Test non-throwing aligned delete[] with explicit size"); - test_delete_size_aligned < operator new[], operator delete[]>(42); + test_delete_size_aligned( + [](size_t s, std::align_val_t a) { return operator new[](s, a); }, + [](void* p, size_t sz, std::align_val_t a) { operator delete[](p, sz, a); }, + 42); snmalloc::debug_check_empty(); return 0; From b7cbae48cba440fb0fa81891c78c03c4125d4126 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 29 Apr 2026 09:33:25 +0100 Subject: [PATCH 24/31] Testing NetBSD --- src/test/func/new/new.cc | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index 5de60a4e7..9b53b1e6b 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -168,6 +168,39 @@ int main(int argc, char** argv) START_TEST("Test delete / delete[] nullptr"); test_delete_null(); + // Diagnostic: direct call to operator new outside any template/lambda + // to verify whether snmalloc's override is reached at all. + START_TEST("Test direct operator new call"); + snmalloc::message("About to call operator new(42) directly\n"); + void* diag_p = operator new(42); + snmalloc::message("operator new(42) returned {}\n", diag_p); + operator delete(diag_p); + snmalloc::message("operator delete succeeded\n"); + + // Check if operator new is the snmalloc version by attempting + // an impossible allocation and seeing if it throws. + START_TEST("Test direct impossible allocation"); + bool direct_bad_alloc = false; + try + { + snmalloc::message("About to call operator new(size_t(-1)) directly\n"); + void* diag_impossible = operator new(static_cast(-1)); + snmalloc::message( + "operator new(size_t(-1)) returned {} (should have thrown!)\n", + diag_impossible); + operator delete(diag_impossible); + } + catch (std::bad_alloc&) + { + direct_bad_alloc = true; + snmalloc::message("Caught std::bad_alloc from direct call\n"); + } + catch (...) + { + snmalloc::message("Caught unknown exception from direct call\n"); + } + snmalloc::message("direct_bad_alloc = {}\n", direct_bad_alloc); + START_TEST("Test new / delete simple"); test_new_delete_simple( [](size_t s) { return operator new(s); }, From 6af80e6a733d73f6ec8bdc922ac96f124e1907f7 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 29 Apr 2026 10:15:40 +0100 Subject: [PATCH 25/31] Compile new/delete override test at -O0 GCC recognises operator new/delete as C++ replaceable allocation functions and can transform them into malloc/free or elide paired new/delete calls at higher optimisation levels. This bypasses the snmalloc override and causes the test to fail (e.g. operator new returns NULL from malloc instead of throwing std::bad_alloc). -fno-builtin does not prevent this because GCC handles C++ replaceable allocation functions separately from C built-in functions. Compiling the test at -O0 disables all such transformations. --- CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1adaa73a0..d00516b2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -504,6 +504,14 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) target_link_libraries(${TESTNAME} snmalloc) target_compile_definitions(${TESTNAME} PRIVATE "SNMALLOC_USE_${TEST_CLEANUP}") + + # The new/delete override test must be compiled at -O0 to prevent + # GCC from recognising operator new/delete as replaceable allocation + # functions and transforming them into malloc/free or eliding + # new/delete pairs, which bypasses the custom override being tested. + if (${TEST} STREQUAL "new" AND NOT MSVC) + target_compile_options(${TESTNAME} PRIVATE -O0) + endif() if (NOT DEFINES STREQUAL " ") target_compile_definitions(${TESTNAME} PRIVATE ${DEFINES}) From 75fd5d063df7dfcb51b4211e70570fd0609864bf Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 29 Apr 2026 10:21:31 +0100 Subject: [PATCH 26/31] Upgrade NetBSD CI to 10.1 with GCC 14 GCC 10 on NetBSD 9.2 has a bug where it incorrectly optimises calls to replaceable C++ allocation functions (operator new/delete) inside templates and lambdas, replacing them with built-in malloc/free even when a user-provided replacement is visible in the same translation unit. This causes the new/delete override test to fail. Upgrade to NetBSD 10.1 (the vmactions/netbsd-vm default) and GCC 14 from pkgsrc, which does not have this issue. --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7947e85c9..705291e08 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -213,10 +213,10 @@ jobs: uses: ./.github/workflows/reusable-vm-build.yml with: vm-type: netbsd - vm-version: '9.2' + vm-version: '10.1' build-type: Release - dependencies: "/usr/sbin/pkg_add cmake ninja-build gcc10" - cmake-flags: "-DCMAKE_CXX_COMPILER=/usr/pkg/gcc10/bin/g++ -DSNMALLOC_TRACING=1" + dependencies: "/usr/sbin/pkg_add cmake ninja-build gcc14" + cmake-flags: "-DCMAKE_CXX_COMPILER=/usr/pkg/gcc14/bin/g++ -DSNMALLOC_TRACING=1" # ============================================================================ # QEMU cross-compilation builds From 1f71a21fe84482b13d5daac3ea4ffb3c451387e2 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 29 Apr 2026 10:22:19 +0100 Subject: [PATCH 27/31] Remove -O0 workaround for new/delete override test The GCC 10 compiler bug that required this workaround is addressed by upgrading the NetBSD CI to GCC 14. --- CMakeLists.txt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d00516b2c..1adaa73a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -504,14 +504,6 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) target_link_libraries(${TESTNAME} snmalloc) target_compile_definitions(${TESTNAME} PRIVATE "SNMALLOC_USE_${TEST_CLEANUP}") - - # The new/delete override test must be compiled at -O0 to prevent - # GCC from recognising operator new/delete as replaceable allocation - # functions and transforming them into malloc/free or eliding - # new/delete pairs, which bypasses the custom override being tested. - if (${TEST} STREQUAL "new" AND NOT MSVC) - target_compile_options(${TESTNAME} PRIVATE -O0) - endif() if (NOT DEFINES STREQUAL " ") target_compile_definitions(${TESTNAME} PRIVATE ${DEFINES}) From 98a87d98528743626491329fbe30fad074b3ff3e Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 29 Apr 2026 10:54:21 +0100 Subject: [PATCH 28/31] CI fixes --- .github/workflows/main.yml | 2 +- src/test/func/new/new.cc | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 705291e08..dc88f7aa3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -216,7 +216,7 @@ jobs: vm-version: '10.1' build-type: Release dependencies: "/usr/sbin/pkg_add cmake ninja-build gcc14" - cmake-flags: "-DCMAKE_CXX_COMPILER=/usr/pkg/gcc14/bin/g++ -DSNMALLOC_TRACING=1" + cmake-flags: "-DCMAKE_CXX_COMPILER=/usr/pkg/gcc14/bin/g++ -DSNMALLOC_USE_CXX11_DESTRUCTORS=ON -DSNMALLOC_TRACING=1" # ============================================================================ # QEMU cross-compilation builds diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index 9b53b1e6b..1d89a05ad 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -11,6 +11,13 @@ int main() # include # include +// snmalloc implements operator new[] / operator delete[] identically to their +// non-array counterparts, which causes GCC to produce false-positive +// -Wmismatched-new-delete warnings when it inlines through lambdas. +# if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic ignored "-Wmismatched-new-delete" +# endif + using namespace snmalloc; constexpr std::array align_val_sizes = { 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, OS_PAGE_SIZE}; From cf825872c06acc7e323597f8d4dba83c52040ee2 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 29 Apr 2026 11:07:18 +0100 Subject: [PATCH 29/31] Fix C11 desctructtors setting. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dc88f7aa3..80448b7c5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -216,7 +216,7 @@ jobs: vm-version: '10.1' build-type: Release dependencies: "/usr/sbin/pkg_add cmake ninja-build gcc14" - cmake-flags: "-DCMAKE_CXX_COMPILER=/usr/pkg/gcc14/bin/g++ -DSNMALLOC_USE_CXX11_DESTRUCTORS=ON -DSNMALLOC_TRACING=1" + cmake-flags: "-DCMAKE_CXX_COMPILER=/usr/pkg/gcc14/bin/g++ -DSNMALLOC_CLEANUP=CXX11_DESTRUCTORS -DSNMALLOC_TRACING=1" # ============================================================================ # QEMU cross-compilation builds From 80adefa17216a98c0ee89bb5e3ec09ec71d87b7f Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 29 Apr 2026 13:20:43 +0100 Subject: [PATCH 30/31] Removing CI debugging. --- .github/workflows/main.yml | 2 +- src/snmalloc/override/new.cc | 7 ------- src/test/func/new/new.cc | 33 --------------------------------- 3 files changed, 1 insertion(+), 41 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 80448b7c5..3510554c7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -216,7 +216,7 @@ jobs: vm-version: '10.1' build-type: Release dependencies: "/usr/sbin/pkg_add cmake ninja-build gcc14" - cmake-flags: "-DCMAKE_CXX_COMPILER=/usr/pkg/gcc14/bin/g++ -DSNMALLOC_CLEANUP=CXX11_DESTRUCTORS -DSNMALLOC_TRACING=1" + cmake-flags: "-DCMAKE_CXX_COMPILER=/usr/pkg/gcc14/bin/g++ -DSNMALLOC_CLEANUP=CXX11_DESTRUCTORS" # ============================================================================ # QEMU cross-compilation builds diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index 1358bbf73..667ca9c45 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -48,7 +48,6 @@ namespace snmalloc public: static void* failure(size_t size) { - snmalloc::message("begin throw case\n"); // Throw std::bad_alloc on failure. auto new_handler = std::get_new_handler(); if (new_handler != nullptr) @@ -62,7 +61,6 @@ namespace snmalloc } // Throw std::bad_alloc on failure. - snmalloc::message("throw\n"); throw std::bad_alloc(); } }; @@ -72,7 +70,6 @@ namespace snmalloc public: static void* failure(size_t size) noexcept { - snmalloc::message("begin nothrow case\n"); auto new_handler = std::get_new_handler(); if (new_handler != nullptr) { @@ -102,26 +99,22 @@ namespace snmalloc SNMALLOC_EXPORT void* operator new(size_t size) { - snmalloc::message("begin new case\n"); return snmalloc::alloc(size); } SNMALLOC_EXPORT void* operator new[](size_t size) { - snmalloc::message("begin new[] case\n"); return snmalloc::alloc(size); } SNMALLOC_EXPORT void* operator new(size_t size, const std::nothrow_t&) noexcept { - snmalloc::message("begin new_nt case\n"); return snmalloc::alloc(size); } SNMALLOC_EXPORT void* operator new[](size_t size, const std::nothrow_t&) noexcept { - snmalloc::message("begin new_nt[] case\n"); return snmalloc::alloc(size); } diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index 1d89a05ad..daff77abd 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -175,39 +175,6 @@ int main(int argc, char** argv) START_TEST("Test delete / delete[] nullptr"); test_delete_null(); - // Diagnostic: direct call to operator new outside any template/lambda - // to verify whether snmalloc's override is reached at all. - START_TEST("Test direct operator new call"); - snmalloc::message("About to call operator new(42) directly\n"); - void* diag_p = operator new(42); - snmalloc::message("operator new(42) returned {}\n", diag_p); - operator delete(diag_p); - snmalloc::message("operator delete succeeded\n"); - - // Check if operator new is the snmalloc version by attempting - // an impossible allocation and seeing if it throws. - START_TEST("Test direct impossible allocation"); - bool direct_bad_alloc = false; - try - { - snmalloc::message("About to call operator new(size_t(-1)) directly\n"); - void* diag_impossible = operator new(static_cast(-1)); - snmalloc::message( - "operator new(size_t(-1)) returned {} (should have thrown!)\n", - diag_impossible); - operator delete(diag_impossible); - } - catch (std::bad_alloc&) - { - direct_bad_alloc = true; - snmalloc::message("Caught std::bad_alloc from direct call\n"); - } - catch (...) - { - snmalloc::message("Caught unknown exception from direct call\n"); - } - snmalloc::message("direct_bad_alloc = {}\n", direct_bad_alloc); - START_TEST("Test new / delete simple"); test_new_delete_simple( [](size_t s) { return operator new(s); }, From 9dd722df821a93240f31740ee9ea1259b2669968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Heidekr=C3=BCger?= Date: Wed, 29 Apr 2026 15:11:23 +0200 Subject: [PATCH 31/31] Revert back to function pointers instead of lambdas Avoid having to hard-code a suppression of certain GCC warnings --- src/test/func/new/new.cc | 126 ++++++++++++++------------------------- 1 file changed, 45 insertions(+), 81 deletions(-) diff --git a/src/test/func/new/new.cc b/src/test/func/new/new.cc index daff77abd..a965c6b89 100644 --- a/src/test/func/new/new.cc +++ b/src/test/func/new/new.cc @@ -11,13 +11,6 @@ int main() # include # include -// snmalloc implements operator new[] / operator delete[] identically to their -// non-array counterparts, which causes GCC to produce false-positive -// -Wmismatched-new-delete warnings when it inlines through lambdas. -# if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic ignored "-Wmismatched-new-delete" -# endif - using namespace snmalloc; constexpr std::array align_val_sizes = { 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, OS_PAGE_SIZE}; @@ -37,27 +30,27 @@ void test_delete_null() operator delete[](nullptr, std::align_val_t{OS_PAGE_SIZE}, std::nothrow); } -template -void test_zero_alloc(New_Func new_func, Del_Func del_func) +template +void test_zero_alloc(New_Func new_func) { void* non_zero = new_func(0); EXPECT(non_zero, "allocation with size '0' did not return a valid pointer"); - del_func(non_zero); + operator delete(non_zero); } // SNMALLOC_EXPORT void* operator new(size_t size) // SNMALLOC_EXPORT void* operator new[](size_t size) // SNMALLOC_EXPORT void operator delete(void* p) EXCEPTSPEC // SNMALLOC_EXPORT void* operator delete[](void* p) EXCEPTSPEC -template -void test_new_delete_simple(New_Func new_fun, Del_Func del_fun, size_t size) +template +void test_new_delete_simple(size_t size) { void* non_zero = new_fun(size); bool caught_bad_alloc = false; EXPECT(non_zero, "expected valid address, but instead got {}", non_zero); del_fun(non_zero); - test_zero_alloc(new_fun, del_fun); + test_zero_alloc(new_fun); try { @@ -82,22 +75,25 @@ void test_new_delete_simple(New_Func new_fun, Del_Func del_fun, size_t size) // noexcept // SNMALLOC_EXPORT void* operator delete[](size_t size, const std::nothrow_t&) // noexcept -template -void test_new_delete_nothrow(New_Func new_fun, Del_Func del_fun) +template< + void* (*new_fun)(size_t, const std::nothrow_t&), + void (*del_fun)(void*, const std::nothrow_t&) EXCEPTSPEC> +void test_new_delete_nothrow() { - void* impossible_alloc_unaligned = new_fun(static_cast(-1)); + void* impossible_alloc_unaligned = + new_fun(static_cast(-1), std::nothrow); EXPECT( impossible_alloc_unaligned == nullptr, "Impossible allocation should not succeed"); - del_fun(impossible_alloc_unaligned); + del_fun(impossible_alloc_unaligned, std::nothrow); - test_zero_alloc(new_fun, del_fun); + test_zero_alloc([](size_t s) { return new_fun(s, std::nothrow); }); } // SNMALLOC_EXPORT void operator delete(void* p, size_t size) EXCEPTSPEC // SNMALLOC_EXPORT void operator delete[](void* p, size_t size) EXCEPTSPEC -template -void test_delete_size(New_Func new_fun, Del_Func del_fun, size_t size) +template +void test_delete_size(size_t size) { void* non_zero = new_fun(size); EXPECT(non_zero, "expected valid address, but instead got {}", non_zero); @@ -108,8 +104,10 @@ void test_delete_size(New_Func new_fun, Del_Func del_fun, size_t size) // SNMALLOC_EXPORT void* operator new[](size_t size, std::align_val_t val) // SNMALLOC_EXPORT void operator delete(void* p, std::align_val_t) EXCEPTSPEC // SNMALLOC_EXPORT void operator delete[](void* p, std::align_val_t) EXCEPTSPEC -template -void test_new_delete_aligned(New_Func new_fun, Del_Func del_fun, size_t size) +template< + void* (*new_fun)(size_t, std::align_val_t), + void (*del_fun)(void*, std::align_val_t) EXCEPTSPEC> +void test_new_delete_aligned(size_t size) { for (auto& align_val_size : align_val_sizes) { @@ -121,9 +119,7 @@ void test_new_delete_aligned(New_Func new_fun, Del_Func del_fun, size_t size) align_val_size); del_fun(aligned_mem, align_val); - test_zero_alloc( - [&align_val, &new_fun](size_t s) { return new_fun(s, align_val); }, - [&align_val, &del_fun](void* p) { del_fun(p, align_val); }); + test_zero_alloc([&align_val](size_t s) { return new_fun(s, align_val); }); } } @@ -131,27 +127,31 @@ void test_new_delete_aligned(New_Func new_fun, Del_Func del_fun, size_t size) // std::nothrow_t&) noexcept // SNMALLOC_EXPORT void* operator new[](size_t size, // std::align_val_t val, const std::nothrow_t&) noexcept -template -void test_new_delete_aligned_nothrow(New_Func new_fun, Del_Func del_fun) +template< + void* (*new_fun)(size_t, std::align_val_t, const std::nothrow_t&), + void (*del_fun)(void*, std::align_val_t, const std::nothrow_t&) EXCEPTSPEC> +void test_new_delete_aligned_nothrow() { std::align_val_t page_size{OS_PAGE_SIZE}; - void* impossible_alloc_aligned = new_fun(static_cast(-1), page_size); + void* impossible_alloc_aligned = + new_fun(static_cast(-1), page_size, std::nothrow); EXPECT( impossible_alloc_aligned == nullptr, "Impossible allocation should not succeed"); - del_fun(impossible_alloc_aligned, page_size); + del_fun(impossible_alloc_aligned, page_size, std::nothrow); test_zero_alloc( - [&page_size, &new_fun](size_t s) { return new_fun(s, page_size); }, - [&page_size, &del_fun](void* p) { del_fun(p, page_size); }); + [&page_size](size_t s) { return new_fun(s, page_size, std::nothrow); }); } // SNMALLOC_EXPORT void operator delete(void* p, size_t size, std::align_val_t // val) EXCEPTSPEC // SNMALLOC_EXPORT void operator delete[](void* p, size_t size, // std::align_val_t val) EXCEPTSPEC -template -void test_delete_size_aligned(New_Func new_fun, Del_Func del_fun, size_t size) +template< + void* (*new_fun)(size_t, std::align_val_t), + void (*del_fun)(void*, size_t, std::align_val_t) EXCEPTSPEC> +void test_delete_size_aligned(size_t size) { for (auto& align_val_size : align_val_sizes) { @@ -176,70 +176,34 @@ int main(int argc, char** argv) test_delete_null(); START_TEST("Test new / delete simple"); - test_new_delete_simple( - [](size_t s) { return operator new(s); }, - [](void* p) { operator delete(p); }, - 42); + test_new_delete_simple < operator new, operator delete>(42); START_TEST("Test new[] / delete[] simple"); - test_new_delete_simple( - [](size_t s) { return operator new[](s); }, - [](void* p) { operator delete[](p); }, - 42); + test_new_delete_simple < operator new[], operator delete[]>(42); START_TEST("Test new / delete nothrow"); - test_new_delete_nothrow( - [](size_t s) { return operator new(s, std::nothrow); }, - [](void* p) { operator delete(p, std::nothrow); }); + test_new_delete_nothrow < operator new, operator delete>(); START_TEST("Test new[] / delete[] nothrow"); - test_new_delete_nothrow( - [](size_t s) { return operator new[](s, std::nothrow); }, - [](void* p) { operator delete[](p, std::nothrow); }); + test_new_delete_nothrow < operator new[], operator delete[]>(); START_TEST("Test delete with size parameter"); - test_delete_size( - [](size_t s) { return operator new(s); }, - [](void* p, size_t sz) { operator delete(p, sz); }, - 42); + test_delete_size < operator new, operator delete>(42); START_TEST("Test delete[] with size parameter"); - test_delete_size( - [](size_t s) { return operator new[](s); }, - [](void* p, size_t sz) { operator delete[](p, sz); }, - 42); + test_delete_size < operator new[], operator delete[]>(42); START_TEST("Test new / delete aligned"); - test_new_delete_aligned( - [](size_t s, std::align_val_t a) { return operator new(s, a); }, - [](void* p, std::align_val_t a) { operator delete(p, a); }, - 42); + test_new_delete_aligned < operator new, operator delete>(42); START_TEST("Test new[] / delete[] aligned"); - test_new_delete_aligned( - [](size_t s, std::align_val_t a) { return operator new[](s, a); }, - [](void* p, std::align_val_t a) { operator delete[](p, a); }, - 42); + test_new_delete_aligned < operator new[], operator delete[]>(42); START_TEST("Test non-throwing aligned new / delete"); - test_new_delete_aligned_nothrow( - [](size_t s, std::align_val_t a) { - return operator new(s, a, std::nothrow); - }, - [](void* p, std::align_val_t a) { operator delete(p, a, std::nothrow); }); + test_new_delete_aligned_nothrow < operator new, operator delete>(); START_TEST("Test non-throwing aligned new[] / delete[]"); - test_new_delete_aligned_nothrow( - [](size_t s, std::align_val_t a) { - return operator new[](s, a, std::nothrow); - }, - [](void* p, std::align_val_t a) { operator delete[](p, a, std::nothrow); }); + test_new_delete_aligned_nothrow < operator new[], operator delete[]>(); START_TEST("Test non-throwing aligned delete with explicit size"); - test_delete_size_aligned( - [](size_t s, std::align_val_t a) { return operator new(s, a); }, - [](void* p, size_t sz, std::align_val_t a) { operator delete(p, sz, a); }, - 42); + test_delete_size_aligned < operator new, operator delete>(42); START_TEST("Test non-throwing aligned delete[] with explicit size"); - test_delete_size_aligned( - [](size_t s, std::align_val_t a) { return operator new[](s, a); }, - [](void* p, size_t sz, std::align_val_t a) { operator delete[](p, sz, a); }, - 42); + test_delete_size_aligned < operator new[], operator delete[]>(42); snmalloc::debug_check_empty(); return 0;