From a769b818080c2a11c4e12112129d8a389aec3829 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 28 Sep 2023 12:22:47 +0100 Subject: [PATCH 1/5] Add `get_alloc` This adds a separate function to get the allocator. This slightly simplifies the external thread local storage story, and is aimed at enabling multiple thread local allocators. --- src/snmalloc/global/bounds_checks.h | 4 +-- src/snmalloc/global/threadalloc.h | 31 +++++++------------ src/snmalloc/override/jemalloc_compat.cc | 24 +++++++------- src/snmalloc/override/libc.h | 16 +++++----- src/snmalloc/override/rust.cc | 10 +++--- .../func/first_operation/first_operation.cc | 29 +++++++++-------- src/test/func/memory/memory.cc | 30 +++++++++--------- src/test/func/sandbox/sandbox.cc | 4 +-- src/test/func/statistics/stats.cc | 4 +-- src/test/func/teardown/teardown.cc | 31 +++++++++---------- .../thread_alloc_external.cc | 4 +-- src/test/perf/contention/contention.cc | 4 +-- .../perf/external_pointer/externalpointer.cc | 2 +- src/test/perf/low_memory/low-memory.cc | 4 +-- src/test/perf/memcpy/memcpy.cc | 8 ++--- src/test/perf/singlethread/singlethread.cc | 2 +- 16 files changed, 99 insertions(+), 108 deletions(-) diff --git a/src/snmalloc/global/bounds_checks.h b/src/snmalloc/global/bounds_checks.h index 378f5439a..9752125fa 100644 --- a/src/snmalloc/global/bounds_checks.h +++ b/src/snmalloc/global/bounds_checks.h @@ -61,7 +61,7 @@ namespace snmalloc } else { - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); void* p = const_cast(ptr); auto range_end = pointer_offset(p, len); @@ -91,7 +91,7 @@ namespace snmalloc { if constexpr (PerformCheck) { - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); return alloc.check_bounds(ptr, len); } else diff --git a/src/snmalloc/global/threadalloc.h b/src/snmalloc/global/threadalloc.h index d900fb272..8675b5209 100644 --- a/src/snmalloc/global/threadalloc.h +++ b/src/snmalloc/global/threadalloc.h @@ -31,25 +31,6 @@ extern "C" void _malloc_thread_cleanup(); namespace snmalloc { #ifdef SNMALLOC_EXTERNAL_THREAD_ALLOC - /** - * Version of the `ThreadAlloc` interface that does no management of thread - * local state. - * - * It assumes that Alloc has been defined, and `ThreadAllocExternal` class - * has access to snmalloc_core.h. - */ - class ThreadAlloc - { - protected: - static void register_cleanup() {} - - public: - static SNMALLOC_FAST_PATH Alloc& get() - { - return ThreadAllocExternal::get(); - } - }; - /** * Function passed as a template parameter to `Allocator` to allow lazy * replacement. There is nothing to initialise in this case, so we expect @@ -183,3 +164,15 @@ namespace snmalloc inline void register_clean_up() {} } #endif + +namespace snmalloc +{ + SNMALLOC_FAST_PATH_INLINE Alloc& get_alloc() + { +#ifdef SNMALLOC_EXTERNAL_THREAD_ALLOC + return ThreadAllocExternal::get(); +#else + return ThreadAlloc::get(); +#endif + } +} // namespace snmalloc \ No newline at end of file diff --git a/src/snmalloc/override/jemalloc_compat.cc b/src/snmalloc/override/jemalloc_compat.cc index 7fe11da57..1099578ec 100644 --- a/src/snmalloc/override/jemalloc_compat.cc +++ b/src/snmalloc/override/jemalloc_compat.cc @@ -139,11 +139,11 @@ extern "C" } if (f.should_zero()) { - *ptr = ThreadAlloc::get().alloc(size); + *ptr = get_alloc().alloc(size); } else { - *ptr = ThreadAlloc::get().alloc(size); + *ptr = get_alloc().alloc(size); } return (*ptr != nullptr) ? allocm_success : allocm_err_oom; } @@ -162,7 +162,7 @@ extern "C" auto f = JEMallocFlags(flags); auto alloc_size = f.aligned_size(size); - auto& a = ThreadAlloc::get(); + auto& a = get_alloc(); size_t sz = a.alloc_size(*ptr); // Keep the current allocation if the given size is in the same sizeclass. if (sz == round_size(alloc_size)) @@ -213,7 +213,7 @@ extern "C" */ int SNMALLOC_NAME_MANGLE(sallocm)(const void* ptr, size_t* rsize, int) { - *rsize = ThreadAlloc::get().alloc_size(ptr); + *rsize = get_alloc().alloc_size(ptr); return allocm_success; } @@ -224,7 +224,7 @@ extern "C" */ int SNMALLOC_NAME_MANGLE(dallocm)(void* ptr, int) { - ThreadAlloc::get().dealloc(ptr); + get_alloc().dealloc(ptr); return allocm_success; } @@ -253,9 +253,9 @@ extern "C" size = f.aligned_size(size); if (f.should_zero()) { - return ThreadAlloc::get().alloc(size); + return get_alloc().alloc(size); } - return ThreadAlloc::get().alloc(size); + return get_alloc().alloc(size); } /** @@ -270,7 +270,7 @@ extern "C" auto f = JEMallocFlags(flags); size = f.aligned_size(size); - auto& a = ThreadAlloc::get(); + auto& a = get_alloc(); size_t sz = round_size(a.alloc_size(ptr)); // Keep the current allocation if the given size is in the same sizeclass. if (sz == size) @@ -309,7 +309,7 @@ extern "C" */ size_t SNMALLOC_NAME_MANGLE(xallocx)(void* ptr, size_t, size_t, int) { - auto& a = ThreadAlloc::get(); + auto& a = get_alloc(); return a.alloc_size(ptr); } @@ -319,7 +319,7 @@ extern "C" */ size_t SNMALLOC_NAME_MANGLE(sallocx)(const void* ptr, int) { - auto& a = ThreadAlloc::get(); + auto& a = get_alloc(); return a.alloc_size(ptr); } @@ -330,7 +330,7 @@ extern "C" */ void SNMALLOC_NAME_MANGLE(dallocx)(void* ptr, int) { - ThreadAlloc::get().dealloc(ptr); + get_alloc().dealloc(ptr); } /** @@ -343,7 +343,7 @@ extern "C" */ void SNMALLOC_NAME_MANGLE(sdallocx)(void* ptr, size_t, int) { - ThreadAlloc::get().dealloc(ptr); + get_alloc().dealloc(ptr); } /** diff --git a/src/snmalloc/override/libc.h b/src/snmalloc/override/libc.h index 05a82eca0..624da4eb1 100644 --- a/src/snmalloc/override/libc.h +++ b/src/snmalloc/override/libc.h @@ -21,22 +21,22 @@ namespace snmalloc::libc inline void* __malloc_end_pointer(void* ptr) { - return ThreadAlloc::get().external_pointer(ptr); + return get_alloc().external_pointer(ptr); } SNMALLOC_FAST_PATH_INLINE void* malloc(size_t size) { - return ThreadAlloc::get().alloc(size); + return get_alloc().alloc(size); } SNMALLOC_FAST_PATH_INLINE void free(void* ptr) { - ThreadAlloc::get().dealloc(ptr); + get_alloc().dealloc(ptr); } SNMALLOC_FAST_PATH_INLINE void free_sized(void* ptr, size_t size) { - ThreadAlloc::get().dealloc(ptr, size); + get_alloc().dealloc(ptr, size); } SNMALLOC_FAST_PATH_INLINE void* calloc(size_t nmemb, size_t size) @@ -47,12 +47,12 @@ namespace snmalloc::libc { return set_error(); } - return ThreadAlloc::get().alloc(sz); + return get_alloc().alloc(sz); } SNMALLOC_FAST_PATH_INLINE void* realloc(void* ptr, size_t size) { - auto& a = ThreadAlloc::get(); + auto& a = get_alloc(); size_t sz = a.alloc_size(ptr); // Keep the current allocation if the given size is in the same sizeclass. if (sz == round_size(size)) @@ -90,7 +90,7 @@ namespace snmalloc::libc inline size_t malloc_usable_size(const void* ptr) { - return ThreadAlloc::get().alloc_size(ptr); + return get_alloc().alloc_size(ptr); } inline void* reallocarray(void* ptr, size_t nmemb, size_t size) @@ -107,7 +107,7 @@ namespace snmalloc::libc inline int reallocarr(void* ptr_, size_t nmemb, size_t size) { int err = errno; - auto& a = ThreadAlloc::get(); + auto& a = get_alloc(); bool overflow = false; size_t sz = bits::umul(size, nmemb, overflow); if (SNMALLOC_UNLIKELY(sz == 0)) diff --git a/src/snmalloc/override/rust.cc b/src/snmalloc/override/rust.cc index f7825cda1..2d32feb5b 100644 --- a/src/snmalloc/override/rust.cc +++ b/src/snmalloc/override/rust.cc @@ -12,19 +12,19 @@ using namespace snmalloc; extern "C" SNMALLOC_EXPORT void* SNMALLOC_NAME_MANGLE(rust_alloc)(size_t alignment, size_t size) { - return ThreadAlloc::get().alloc(aligned_size(alignment, size)); + return get_alloc().alloc(aligned_size(alignment, size)); } extern "C" SNMALLOC_EXPORT void* SNMALLOC_NAME_MANGLE(rust_alloc_zeroed)(size_t alignment, size_t size) { - return ThreadAlloc::get().alloc(aligned_size(alignment, size)); + return get_alloc().alloc(aligned_size(alignment, size)); } extern "C" SNMALLOC_EXPORT void SNMALLOC_NAME_MANGLE(rust_dealloc)(void* ptr, size_t alignment, size_t size) { - ThreadAlloc::get().dealloc(ptr, aligned_size(alignment, size)); + get_alloc().dealloc(ptr, aligned_size(alignment, size)); } extern "C" SNMALLOC_EXPORT void* SNMALLOC_NAME_MANGLE(rust_realloc)( @@ -36,11 +36,11 @@ extern "C" SNMALLOC_EXPORT void* SNMALLOC_NAME_MANGLE(rust_realloc)( size_to_sizeclass_full(aligned_old_size).raw() == size_to_sizeclass_full(aligned_new_size).raw()) return ptr; - void* p = ThreadAlloc::get().alloc(aligned_new_size); + void* p = get_alloc().alloc(aligned_new_size); if (p) { std::memcpy(p, ptr, old_size < new_size ? old_size : new_size); - ThreadAlloc::get().dealloc(ptr, aligned_old_size); + get_alloc().dealloc(ptr, aligned_old_size); } return p; } diff --git a/src/test/func/first_operation/first_operation.cc b/src/test/func/first_operation/first_operation.cc index 629027fc9..75121a0bb 100644 --- a/src/test/func/first_operation/first_operation.cc +++ b/src/test/func/first_operation/first_operation.cc @@ -13,27 +13,27 @@ void alloc1(size_t size) { - void* r = snmalloc::ThreadAlloc::get().alloc(size); - snmalloc::ThreadAlloc::get().dealloc(r); + void* r = snmalloc::get_alloc().alloc(size); + snmalloc::get_alloc().dealloc(r); } void alloc2(size_t size) { - auto& a = snmalloc::ThreadAlloc::get(); + auto& a = snmalloc::get_alloc(); void* r = a.alloc(size); a.dealloc(r); } void alloc3(size_t size) { - auto& a = snmalloc::ThreadAlloc::get(); + auto& a = snmalloc::get_alloc(); void* r = a.alloc(size); a.dealloc(r, size); } void alloc4(size_t size) { - auto& a = snmalloc::ThreadAlloc::get(); + auto& a = snmalloc::get_alloc(); void* r = a.alloc(size); a.dealloc(r); } @@ -62,15 +62,14 @@ void check_calloc(void* p, size_t size) void calloc1(size_t size) { - void* r = - snmalloc::ThreadAlloc::get().alloc(size); + void* r = snmalloc::get_alloc().alloc(size); check_calloc(r, size); - snmalloc::ThreadAlloc::get().dealloc(r); + snmalloc::get_alloc().dealloc(r); } void calloc2(size_t size) { - auto& a = snmalloc::ThreadAlloc::get(); + auto& a = snmalloc::get_alloc(); void* r = a.alloc(size); check_calloc(r, size); a.dealloc(r); @@ -78,7 +77,7 @@ void calloc2(size_t size) void calloc3(size_t size) { - auto& a = snmalloc::ThreadAlloc::get(); + auto& a = snmalloc::get_alloc(); void* r = a.alloc(size); check_calloc(r, size); a.dealloc(r, size); @@ -86,7 +85,7 @@ void calloc3(size_t size) void calloc4(size_t size) { - auto& a = snmalloc::ThreadAlloc::get(); + auto& a = snmalloc::get_alloc(); void* r = a.alloc(size); check_calloc(r, size); a.dealloc(r); @@ -94,22 +93,22 @@ void calloc4(size_t size) void dealloc1(void* p, size_t) { - snmalloc::ThreadAlloc::get().dealloc(p); + snmalloc::get_alloc().dealloc(p); } void dealloc2(void* p, size_t size) { - snmalloc::ThreadAlloc::get().dealloc(p, size); + snmalloc::get_alloc().dealloc(p, size); } void dealloc3(void* p, size_t) { - snmalloc::ThreadAlloc::get().dealloc(p); + snmalloc::get_alloc().dealloc(p); } void dealloc4(void* p, size_t size) { - snmalloc::ThreadAlloc::get().dealloc(p, size); + snmalloc::get_alloc().dealloc(p, size); } void f(size_t size) diff --git a/src/test/func/memory/memory.cc b/src/test/func/memory/memory.cc index 2a2ada2ee..a72e3e410 100644 --- a/src/test/func/memory/memory.cc +++ b/src/test/func/memory/memory.cc @@ -59,7 +59,7 @@ void test_limited(rlim64_t as_limit, size_t& count) upper_bound = std::min( upper_bound, static_cast(info.freeram >> 3u)); std::cout << "trying to alloc " << upper_bound / KiB << " KiB" << std::endl; - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); std::cout << "allocator initialised" << std::endl; auto chunk = alloc.alloc(upper_bound); alloc.dealloc(chunk); @@ -81,7 +81,7 @@ void test_limited(rlim64_t as_limit, size_t& count) void test_alloc_dealloc_64k() { - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); constexpr size_t count = 1 << 12; constexpr size_t outer_count = 12; @@ -113,7 +113,7 @@ void test_alloc_dealloc_64k() void test_random_allocation() { - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); std::unordered_set allocated; constexpr size_t count = 10000; @@ -165,7 +165,7 @@ void test_random_allocation() void test_calloc() { - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); for (size_t size = 16; size <= (1 << 24); size <<= 1) { @@ -235,7 +235,7 @@ void test_double_alloc() void test_external_pointer() { // Malloc does not have an external pointer querying mechanism. - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); for (uint8_t sc = 0; sc < NUM_SMALL_SIZECLASSES; sc++) { @@ -278,7 +278,7 @@ void test_external_pointer() void check_offset(void* base, void* interior) { - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); void* calced_base = alloc.external_pointer((void*)interior); if (calced_base != (void*)base) { @@ -303,7 +303,7 @@ void test_external_pointer_large() { xoroshiro::p128r64 r; - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); constexpr size_t count_log = DefaultPal::address_bits > 32 ? 5 : 3; constexpr size_t count = 1 << count_log; @@ -346,7 +346,7 @@ void test_external_pointer_large() void test_external_pointer_dealloc_bug() { std::cout << "Testing external pointer dealloc bug" << std::endl; - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); constexpr size_t count = MIN_CHUNK_SIZE; void* allocs[count]; @@ -375,7 +375,7 @@ void test_external_pointer_stack() std::array stack; - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); for (size_t i = 0; i < stack.size(); i++) { @@ -393,7 +393,7 @@ void test_external_pointer_stack() void test_alloc_16M() { - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); // sizes >= 16M use large_alloc const size_t size = 16'000'000; @@ -404,7 +404,7 @@ void test_alloc_16M() void test_calloc_16M() { - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); // sizes >= 16M use large_alloc const size_t size = 16'000'000; @@ -415,7 +415,7 @@ void test_calloc_16M() void test_calloc_large_bug() { - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); // Perform large calloc, to check for correct zeroing from PAL. // Some PALS have special paths for PAGE aligned zeroing of large // allocations. This is a large allocation that is intentionally @@ -430,7 +430,7 @@ void test_calloc_large_bug() template void test_static_sized_alloc() { - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); auto p = alloc.alloc(); static_assert((dealloc >= 0) && (dealloc <= 2), "bad dealloc flavor"); @@ -469,7 +469,7 @@ void test_static_sized_allocs() void test_remaining_bytes() { - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); for (size_t sc = 0; sc < NUM_SMALL_SIZECLASSES; sc++) { auto size = sizeclass_to_size(sc); @@ -499,7 +499,7 @@ void test_consolidaton_bug() * Check for consolidation of various sizes, but allocating and deallocating, * then requesting larger sizes. See issue #506 */ - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); for (size_t i = 0; i < 27; i++) { diff --git a/src/test/func/sandbox/sandbox.cc b/src/test/func/sandbox/sandbox.cc index f3327afc5..a8d13e4cc 100644 --- a/src/test/func/sandbox/sandbox.cc +++ b/src/test/func/sandbox/sandbox.cc @@ -243,7 +243,7 @@ namespace // Use the outside-sandbox snmalloc to allocate memory, rather than using // the PAL directly, so that our out-of-sandbox can amplify sandbox // pointers - return ThreadAlloc::get().alloc(sb_size); + return get_alloc().alloc(sb_size); } }; } @@ -259,7 +259,7 @@ int main() auto check = [](Sandbox& sb, auto& alloc, size_t sz) { void* ptr = alloc.alloc(sz); SNMALLOC_CHECK(sb.is_in_sandbox_heap(ptr, sz)); - ThreadAlloc::get().dealloc(ptr); + get_alloc().dealloc(ptr); }; auto check_with_sb = [&](Sandbox& sb) { // Check with a range of sizes diff --git a/src/test/func/statistics/stats.cc b/src/test/func/statistics/stats.cc index c8db1cad7..6886f5063 100644 --- a/src/test/func/statistics/stats.cc +++ b/src/test/func/statistics/stats.cc @@ -12,7 +12,7 @@ template void debug_check_empty_1() { std::cout << "debug_check_empty_1 " << size << std::endl; - snmalloc::Alloc& a = snmalloc::ThreadAlloc::get(); + snmalloc::Alloc& a = snmalloc::get_alloc(); bool result; auto r = a.alloc(size); @@ -58,7 +58,7 @@ template void debug_check_empty_2() { std::cout << "debug_check_empty_2 " << size << std::endl; - snmalloc::Alloc& a = snmalloc::ThreadAlloc::get(); + snmalloc::Alloc& a = snmalloc::get_alloc(); bool result; std::vector allocs; // 1GB of allocations diff --git a/src/test/func/teardown/teardown.cc b/src/test/func/teardown/teardown.cc index f68ed4d03..c768a8eca 100644 --- a/src/test/func/teardown/teardown.cc +++ b/src/test/func/teardown/teardown.cc @@ -13,7 +13,7 @@ void trigger_teardown() { - auto& a = snmalloc::ThreadAlloc::get(); + auto& a = snmalloc::get_alloc(); // Trigger init void* r = a.alloc(16); a.dealloc(r); @@ -24,14 +24,14 @@ void trigger_teardown() void alloc1(size_t size) { trigger_teardown(); - void* r = snmalloc::ThreadAlloc::get().alloc(size); - snmalloc::ThreadAlloc::get().dealloc(r); + void* r = snmalloc::get_alloc().alloc(size); + snmalloc::get_alloc().dealloc(r); } void alloc2(size_t size) { trigger_teardown(); - auto& a = snmalloc::ThreadAlloc::get(); + auto& a = snmalloc::get_alloc(); void* r = a.alloc(size); a.dealloc(r); } @@ -39,7 +39,7 @@ void alloc2(size_t size) void alloc3(size_t size) { trigger_teardown(); - auto& a = snmalloc::ThreadAlloc::get(); + auto& a = snmalloc::get_alloc(); void* r = a.alloc(size); a.dealloc(r, size); } @@ -47,7 +47,7 @@ void alloc3(size_t size) void alloc4(size_t size) { trigger_teardown(); - auto& a = snmalloc::ThreadAlloc::get(); + auto& a = snmalloc::get_alloc(); void* r = a.alloc(size); a.dealloc(r); } @@ -77,16 +77,15 @@ void check_calloc(void* p, size_t size) void calloc1(size_t size) { trigger_teardown(); - void* r = - snmalloc::ThreadAlloc::get().alloc(size); + void* r = snmalloc::get_alloc().alloc(size); check_calloc(r, size); - snmalloc::ThreadAlloc::get().dealloc(r); + snmalloc::get_alloc().dealloc(r); } void calloc2(size_t size) { trigger_teardown(); - auto& a = snmalloc::ThreadAlloc::get(); + auto& a = snmalloc::get_alloc(); void* r = a.alloc(size); check_calloc(r, size); a.dealloc(r); @@ -95,7 +94,7 @@ void calloc2(size_t size) void calloc3(size_t size) { trigger_teardown(); - auto& a = snmalloc::ThreadAlloc::get(); + auto& a = snmalloc::get_alloc(); void* r = a.alloc(size); check_calloc(r, size); a.dealloc(r, size); @@ -104,7 +103,7 @@ void calloc3(size_t size) void calloc4(size_t size) { trigger_teardown(); - auto& a = snmalloc::ThreadAlloc::get(); + auto& a = snmalloc::get_alloc(); void* r = a.alloc(size); check_calloc(r, size); a.dealloc(r); @@ -113,25 +112,25 @@ void calloc4(size_t size) void dealloc1(void* p, size_t) { trigger_teardown(); - snmalloc::ThreadAlloc::get().dealloc(p); + snmalloc::get_alloc().dealloc(p); } void dealloc2(void* p, size_t size) { trigger_teardown(); - snmalloc::ThreadAlloc::get().dealloc(p, size); + snmalloc::get_alloc().dealloc(p, size); } void dealloc3(void* p, size_t) { trigger_teardown(); - snmalloc::ThreadAlloc::get().dealloc(p); + snmalloc::get_alloc().dealloc(p); } void dealloc4(void* p, size_t size) { trigger_teardown(); - snmalloc::ThreadAlloc::get().dealloc(p, size); + snmalloc::get_alloc().dealloc(p, size); } void f(size_t size) diff --git a/src/test/func/thread_alloc_external/thread_alloc_external.cc b/src/test/func/thread_alloc_external/thread_alloc_external.cc index 2b10ed8cb..8d7d2cd55 100644 --- a/src/test/func/thread_alloc_external/thread_alloc_external.cc +++ b/src/test/func/thread_alloc_external/thread_alloc_external.cc @@ -63,7 +63,7 @@ int main() setup(); allocator_thread_init(); - auto& a = ThreadAlloc::get(); + auto& a = get_alloc(); for (size_t i = 0; i < 1000; i++) { @@ -72,7 +72,7 @@ int main() a.dealloc(r1); } - ThreadAlloc::get().teardown(); + get_alloc().teardown(); // This checks that the scoped allocator does not call // register clean up, as this configuration will fault diff --git a/src/test/perf/contention/contention.cc b/src/test/perf/contention/contention.cc index e266f0491..cbad5806b 100644 --- a/src/test/perf/contention/contention.cc +++ b/src/test/perf/contention/contention.cc @@ -75,7 +75,7 @@ size_t swapcount; void test_tasks_f(size_t id) { - auto& a = ThreadAlloc::get(); + auto& a = get_alloc(); xoroshiro::p128r32 r(id + 5000); for (size_t n = 0; n < swapcount; n++) @@ -111,7 +111,7 @@ void test_tasks(size_t num_tasks, size_t count, size_t size) { std::cout << "Sequential setup" << std::endl; - auto& a = ThreadAlloc::get(); + auto& a = get_alloc(); contention = new std::atomic[size]; xoroshiro::p128r32 r; diff --git a/src/test/perf/external_pointer/externalpointer.cc b/src/test/perf/external_pointer/externalpointer.cc index be3306cba..80f9a4d61 100644 --- a/src/test/perf/external_pointer/externalpointer.cc +++ b/src/test/perf/external_pointer/externalpointer.cc @@ -52,7 +52,7 @@ namespace test void test_external_pointer(xoroshiro::p128r64& r) { - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); // This is very slow on Windows at the moment. Until this is fixed, help // CI terminate. #if defined(NDEBUG) && !defined(_MSC_VER) diff --git a/src/test/perf/low_memory/low-memory.cc b/src/test/perf/low_memory/low-memory.cc index fa2997fdf..5ae290c07 100644 --- a/src/test/perf/low_memory/low-memory.cc +++ b/src/test/perf/low_memory/low-memory.cc @@ -19,7 +19,7 @@ class Queue Node* new_node(size_t size) { - auto result = (Node*)ThreadAlloc::get().alloc(size); + auto result = (Node*)get_alloc().alloc(size); result->next = nullptr; return result; } @@ -43,7 +43,7 @@ class Queue return false; Node* next = head->next; - ThreadAlloc::get().dealloc(head); + get_alloc().dealloc(head); head = next; return true; } diff --git a/src/test/perf/memcpy/memcpy.cc b/src/test/perf/memcpy/memcpy.cc index e3bee7d2c..a05cddcec 100644 --- a/src/test/perf/memcpy/memcpy.cc +++ b/src/test/perf/memcpy/memcpy.cc @@ -29,7 +29,7 @@ void shape(size_t size) // the memcpys. constexpr size_t alignment = 16; offset = (my_random() % // size / alignment) * alignment; Shape s; - s.object = ThreadAlloc::get().alloc(rsize); + s.object = get_alloc().alloc(rsize); s.dst = static_cast(s.object) + offset; // Bring into cache the destination of the copy. memset(s.dst, 0xFF, size); @@ -41,7 +41,7 @@ void unshape() { for (auto& s : allocs) { - ThreadAlloc::get().dealloc(s.object); + get_alloc().dealloc(s.object); } allocs.clear(); } @@ -62,7 +62,7 @@ void test( Memcpy mc, std::vector>& stats) { - auto src = ThreadAlloc::get().alloc(size); + auto src = get_alloc().alloc(size); shape(size); for (size_t i = 0; i < 10; i++) { @@ -71,7 +71,7 @@ void test( auto time = m.get_time(); stats.push_back({size, time}); } - ThreadAlloc::get().dealloc(src); + get_alloc().dealloc(src); unshape(); } diff --git a/src/test/perf/singlethread/singlethread.cc b/src/test/perf/singlethread/singlethread.cc index b93dcd428..b5fe9bc14 100644 --- a/src/test/perf/singlethread/singlethread.cc +++ b/src/test/perf/singlethread/singlethread.cc @@ -8,7 +8,7 @@ using namespace snmalloc; template void test_alloc_dealloc(size_t count, size_t size, bool write) { - auto& alloc = ThreadAlloc::get(); + auto& alloc = get_alloc(); { MeasureTime m; From 4ff01f88e6d02f370de12008d7861f67cf59ad26 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 28 Sep 2023 14:44:24 +0100 Subject: [PATCH 2/5] Create ThreadLocal This abstracts the ThreadAlloc concept into an parameterised ThreadLocal class. The class enables registering the teardown operation off the fast path. --- src/snmalloc/backend/fixedglobalconfig.h | 9 - src/snmalloc/backend/globalconfig.h | 12 -- src/snmalloc/backend_helpers/commonconfig.h | 3 - src/snmalloc/ds/ds.h | 1 + src/snmalloc/ds/threadlocal.h | 162 ++++++++++++++++++ src/snmalloc/global/threadalloc.h | 166 +------------------ src/snmalloc/mem/localalloc.h | 4 +- src/test/func/domestication/domestication.cc | 5 - 8 files changed, 168 insertions(+), 194 deletions(-) create mode 100644 src/snmalloc/ds/threadlocal.h diff --git a/src/snmalloc/backend/fixedglobalconfig.h b/src/snmalloc/backend/fixedglobalconfig.h index 37a78c287..2243b2cad 100644 --- a/src/snmalloc/backend/fixedglobalconfig.h +++ b/src/snmalloc/backend/fixedglobalconfig.h @@ -69,15 +69,6 @@ namespace snmalloc return opts; }(); - // This needs to be a forward reference as the - // thread local state will need to know about this. - // This may allocate, so must be called once a thread - // local allocator exists. - static void register_clean_up() - { - snmalloc::register_clean_up(); - } - static void init(LocalState* local_state, void* base, size_t length) { UNUSED(local_state); diff --git a/src/snmalloc/backend/globalconfig.h b/src/snmalloc/backend/globalconfig.h index 525c77275..03b7b496e 100644 --- a/src/snmalloc/backend/globalconfig.h +++ b/src/snmalloc/backend/globalconfig.h @@ -11,9 +11,6 @@ namespace snmalloc { - // Forward reference to thread local cleanup. - void register_clean_up(); - /** * The default configuration for a global snmalloc. It contains all the * datastructures to manage the memory from the OS. It had several internal @@ -152,15 +149,6 @@ namespace snmalloc { return initialised; } - - // This needs to be a forward reference as the - // thread local state will need to know about this. - // This may allocate, so should only be called once - // a thread local allocator is available. - static void register_clean_up() - { - snmalloc::register_clean_up(); - } }; /** diff --git a/src/snmalloc/backend_helpers/commonconfig.h b/src/snmalloc/backend_helpers/commonconfig.h index a69b6a389..426c61202 100644 --- a/src/snmalloc/backend_helpers/commonconfig.h +++ b/src/snmalloc/backend_helpers/commonconfig.h @@ -4,9 +4,6 @@ namespace snmalloc { - // Forward reference to thread local cleanup. - void register_clean_up(); - /** * Options for a specific snmalloc configuration. Every globals object must * have one `constexpr` instance of this class called `Options`. This should diff --git a/src/snmalloc/ds/ds.h b/src/snmalloc/ds/ds.h index 4cfa22b9b..6a9c010d8 100644 --- a/src/snmalloc/ds/ds.h +++ b/src/snmalloc/ds/ds.h @@ -11,3 +11,4 @@ #include "mpmcstack.h" #include "pagemap.h" #include "singleton.h" +#include "threadlocal.h" diff --git a/src/snmalloc/ds/threadlocal.h b/src/snmalloc/ds/threadlocal.h new file mode 100644 index 000000000..b8acd600c --- /dev/null +++ b/src/snmalloc/ds/threadlocal.h @@ -0,0 +1,162 @@ +#pragma once + +#include "singleton.h" + +#if defined(SNMALLOC_EXTERNAL_THREAD_ALLOC) +# define SNMALLOC_THREAD_TEARDOWN_DEFINED +#endif + +#if defined(SNMALLOC_USE_THREAD_CLEANUP) +# if defined(SNMALLOC_THREAD_TEARDOWN_DEFINED) +# error At most one out of method of thread teardown can be specified. +# else +# define SNMALLOC_THREAD_TEARDOWN_DEFINED +# endif +#endif + +#if defined(SNMALLOC_USE_PTHREAD_DESTRUCTORS) +# if defined(SNMALLOC_THREAD_TEARDOWN_DEFINED) +# error At most one out of method of thread teardown can be specified. +# else +# include +# define SNMALLOC_THREAD_TEARDOWN_DEFINED +# endif +#endif + +#if !defined(SNMALLOC_THREAD_TEARDOWN_DEFINED) +# define SNMALLOC_USE_CXX_THREAD_DESTRUCTORS +#endif + +namespace snmalloc +{ + /** + * @brief Thread local that has a cleanup function that can be registered + * off the fast path + * + * @tparam A - the type of the thread local + * + * @details This is used in the following way + * + * ThreadLocal::get().alloc(16); + * + * Inside, the call to alloc if it detects it is the first time the + * structure is being used, then the Alloc should call + * + * ThreadLocal::register_cleanup(); + * + * This means that the thread local will be cleaned up when the thread + * exits. The detecting of the first time it is used can be moved of the fast + * path, and conflated with other initial checks like the thread local free + * list is empty for that size class. + * + * There are multiple configurations for various platformat that are given + * below. + */ + template + class ThreadLocal + { + public: + SNMALLOC_FAST_PATH static A& get() + { + SNMALLOC_REQUIRE_CONSTINIT static thread_local A alloc; + return alloc; + } + + static void register_cleanup(); + }; + +#ifdef SNMALLOC_USE_PTHREAD_DESTRUCTORS + /** + * Used to give correct signature to teardown required by pthread_key. + */ + template + inline void pthread_cleanup(void*) + { + ThreadLocal::get().teardown(); + } + + /** + * Used to give correct signature to teardown required by atexit. + */ + template + inline void pthread_cleanup_main_thread() + { + ThreadLocal::get().teardown(); + } + + /** + * Used to give correct signature to the pthread call for the Singleton class. + */ + template + inline void pthread_create(pthread_key_t* key) noexcept + { + pthread_key_create(key, &pthread_cleanup); + // Main thread does not call pthread_cleanup if `main` returns or `exit` is + // called, so use an atexit handler to guarantee that the cleanup is run at + // least once. If the main thread exits with `pthread_exit` then it will be + // called twice but this case is already handled because other destructors + // can cause the per-thread allocator to be recreated. + atexit(&pthread_cleanup_main_thread); + } + + /** + * Performs thread local teardown for the allocator using the pthread library. + * + * This removes the dependence on the C++ runtime. + */ + template + inline void ThreadLocal::register_cleanup() + { + Singleton> p_key; + // We need to set a non-null value, so that the destructor is called, + // we never look at the value. + static char p_teardown_val = 1; + pthread_setspecific(p_key.get(), &p_teardown_val); +# ifdef SNMALLOC_TRACING + message<1024>("Using pthread clean up"); +# endif + } +#elif defined(SNMALLOC_USE_CXX_THREAD_DESTRUCTORS) + /** + * This function is called by each thread once it starts using the + * thread local allocator. + * + * This implementation depends on nothing outside of a working C++ + * environment and so should be the simplest for initial bringup on an + * unsupported platform. + */ + template + void ThreadLocal::register_cleanup() + { + static thread_local OnDestruct dummy( + []() { ThreadLocal::get().teardown(); }); + UNUSED(dummy); +# ifdef SNMALLOC_TRACING + message<1024>("Using C++ destructor clean up"); +# endif + } +#elif defined(SNMALLOC_USE_THREAD_CLEANUP) + /** + * Entry point that allows libc to call into the allocator for per-thread + * cleanup. + */ + SNMALLOC_USED_FUNCTION + inline void _malloc_thread_cleanup() + { + // This needs to traverse the list of allocators + // thread locals and call each on. + abort(); + } + + /** + * No-op version of register_clean_up. This is called unconditionally by + * globalconfig but is not necessary when using a libc hook. + */ + template + inline void ThreadLocal::register_clean_up() + { + // Add ThreadLocal::teardown() to the list of thread_cleanup calls. + abort(); + } +#endif +} // namespace snmalloc \ No newline at end of file diff --git a/src/snmalloc/global/threadalloc.h b/src/snmalloc/global/threadalloc.h index 8675b5209..c54d03465 100644 --- a/src/snmalloc/global/threadalloc.h +++ b/src/snmalloc/global/threadalloc.h @@ -1,169 +1,7 @@ #pragma once #include "../backend/globalconfig.h" - -#if defined(SNMALLOC_EXTERNAL_THREAD_ALLOC) -# define SNMALLOC_THREAD_TEARDOWN_DEFINED -#endif - -#if defined(SNMALLOC_USE_THREAD_CLEANUP) -# if defined(SNMALLOC_THREAD_TEARDOWN_DEFINED) -# error At most one out of method of thread teardown can be specified. -# else -# define SNMALLOC_THREAD_TEARDOWN_DEFINED -# endif -#endif - -#if defined(SNMALLOC_USE_PTHREAD_DESTRUCTORS) -# if defined(SNMALLOC_THREAD_TEARDOWN_DEFINED) -# error At most one out of method of thread teardown can be specified. -# else -# include -# define SNMALLOC_THREAD_TEARDOWN_DEFINED -# endif -#endif - -#if !defined(SNMALLOC_THREAD_TEARDOWN_DEFINED) -# define SNMALLOC_USE_CXX_THREAD_DESTRUCTORS -#endif -extern "C" void _malloc_thread_cleanup(); - -namespace snmalloc -{ -#ifdef SNMALLOC_EXTERNAL_THREAD_ALLOC - /** - * Function passed as a template parameter to `Allocator` to allow lazy - * replacement. There is nothing to initialise in this case, so we expect - * this to never be called. - */ -# ifdef _MSC_VER -// 32Bit Windows release MSVC is determining this as having unreachable code for -// f(nullptr), which is true. But other platforms don't. Disabling the warning -// seems simplist. -# pragma warning(push) -# pragma warning(disable : 4702) -# endif - inline void register_clean_up() - { - error("Critical Error: This should never be called."); - } -# ifdef _MSC_VER -# pragma warning(pop) -# endif -#else - /** - * Holds the thread local state for the allocator. The state is constant - * initialised, and has no direct dectructor. Instead snmalloc will call - * `register_clean_up` on the slow path for bringing up thread local state. - * This is responsible for calling `teardown`, which effectively destructs the - * data structure, but in a way that allow it to still be used. - */ - class ThreadAlloc - { - public: - /** - * Handle on thread local allocator - * - * This structure will self initialise if it has not been called yet. - * It can be used during thread teardown, but its performance will be - * less good. - */ - static SNMALLOC_FAST_PATH Alloc& get() - { - SNMALLOC_REQUIRE_CONSTINIT static thread_local Alloc alloc; - return alloc; - } - }; - -# ifdef SNMALLOC_USE_PTHREAD_DESTRUCTORS - /** - * Used to give correct signature to teardown required by pthread_key. - */ - inline void pthread_cleanup(void*) - { - ThreadAlloc::get().teardown(); - } - - /** - * Used to give correct signature to teardown required by atexit. - */ - inline void pthread_cleanup_main_thread() - { - ThreadAlloc::get().teardown(); - } - - /** - * Used to give correct signature to the pthread call for the Singleton class. - */ - inline void pthread_create(pthread_key_t* key) noexcept - { - pthread_key_create(key, &pthread_cleanup); - // Main thread does not call pthread_cleanup if `main` returns or `exit` is - // called, so use an atexit handler to guarantee that the cleanup is run at - // least once. If the main thread exits with `pthread_exit` then it will be - // called twice but this case is already handled because other destructors - // can cause the per-thread allocator to be recreated. - atexit(&pthread_cleanup_main_thread); - } - - /** - * Performs thread local teardown for the allocator using the pthread library. - * - * This removes the dependence on the C++ runtime. - */ - inline void register_clean_up() - { - Singleton p_key; - // We need to set a non-null value, so that the destructor is called, - // we never look at the value. - static char p_teardown_val = 1; - pthread_setspecific(p_key.get(), &p_teardown_val); -# ifdef SNMALLOC_TRACING - message<1024>("Using pthread clean up"); -# endif - } -# elif defined(SNMALLOC_USE_CXX_THREAD_DESTRUCTORS) - /** - * This function is called by each thread once it starts using the - * thread local allocator. - * - * This implementation depends on nothing outside of a working C++ - * environment and so should be the simplest for initial bringup on an - * unsupported platform. - */ - inline void register_clean_up() - { - static thread_local OnDestruct dummy( - []() { ThreadAlloc::get().teardown(); }); - UNUSED(dummy); -# ifdef SNMALLOC_TRACING - message<1024>("Using C++ destructor clean up"); -# endif - } -# endif -#endif -} // namespace snmalloc - -#ifdef SNMALLOC_USE_THREAD_CLEANUP -/** - * Entry point that allows libc to call into the allocator for per-thread - * cleanup. - */ -SNMALLOC_USED_FUNCTION -inline void _malloc_thread_cleanup() -{ - snmalloc::ThreadAlloc::get().teardown(); -} - -namespace snmalloc -{ - /** - * No-op version of register_clean_up. This is called unconditionally by - * globalconfig but is not necessary when using a libc hook. - */ - inline void register_clean_up() {} -} -#endif +#include "../ds_core/ds_core.h" namespace snmalloc { @@ -172,7 +10,7 @@ namespace snmalloc #ifdef SNMALLOC_EXTERNAL_THREAD_ALLOC return ThreadAllocExternal::get(); #else - return ThreadAlloc::get(); + return ThreadLocal::get(); #endif } } // namespace snmalloc \ No newline at end of file diff --git a/src/snmalloc/mem/localalloc.h b/src/snmalloc/mem/localalloc.h index 75be56ecc..2e69f7ac6 100644 --- a/src/snmalloc/mem/localalloc.h +++ b/src/snmalloc/mem/localalloc.h @@ -140,6 +140,7 @@ namespace snmalloc init(); } +#ifndef SNMALLOC_EXTERNAL_THREAD_ALLOC // register_clean_up must be called after init. register clean up may // be implemented with allocation, so need to ensure we have a valid // allocator at this point. @@ -147,7 +148,8 @@ namespace snmalloc // Must be called at least once per thread. // A pthread implementation only calls the thread destruction handle // if the key has been set. - Config::register_clean_up(); + ThreadLocal::register_cleanup(); +#endif // Perform underlying operation auto r = action(core_alloc, args...); diff --git a/src/test/func/domestication/domestication.cc b/src/test/func/domestication/domestication.cc index 8ff4fa977..e6c508ea0 100644 --- a/src/test/func/domestication/domestication.cc +++ b/src/test/func/domestication/domestication.cc @@ -74,11 +74,6 @@ namespace snmalloc return alloc_pool; } - static void register_clean_up() - { - snmalloc::register_clean_up(); - } - static inline bool domesticate_trace; static inline size_t domesticate_count; static inline uintptr_t* domesticate_patch_location; From 3f98ae60053945270e19d3b609c9b54ed40dac78 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 28 Sep 2023 15:37:34 +0100 Subject: [PATCH 3/5] Fixup threadlocal --- src/snmalloc/ds/threadlocal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snmalloc/ds/threadlocal.h b/src/snmalloc/ds/threadlocal.h index b8acd600c..3feca9ded 100644 --- a/src/snmalloc/ds/threadlocal.h +++ b/src/snmalloc/ds/threadlocal.h @@ -153,7 +153,7 @@ namespace snmalloc * globalconfig but is not necessary when using a libc hook. */ template - inline void ThreadLocal::register_clean_up() + inline void ThreadLocal::register_cleanup() { // Add ThreadLocal::teardown() to the list of thread_cleanup calls. abort(); From 8cbce002b8391e47dfc9f68461c0ea4b17f1848d Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 28 Sep 2023 15:38:10 +0100 Subject: [PATCH 4/5] Create index set of allocators --- src/snmalloc/global/threadalloc.h | 8 ++++++-- src/snmalloc/mem/corealloc.h | 2 +- src/snmalloc/mem/localalloc.h | 9 ++++++++- src/snmalloc/override/libc.h | 30 ++++++++++++++++++++---------- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/snmalloc/global/threadalloc.h b/src/snmalloc/global/threadalloc.h index c54d03465..5cfa3f6c9 100644 --- a/src/snmalloc/global/threadalloc.h +++ b/src/snmalloc/global/threadalloc.h @@ -5,12 +5,16 @@ namespace snmalloc { - SNMALLOC_FAST_PATH_INLINE Alloc& get_alloc() + template + SNMALLOC_FAST_PATH_INLINE LocalAllocator& get_alloc() { #ifdef SNMALLOC_EXTERNAL_THREAD_ALLOC + static_assert( + std::is_same_v, + "SNMALLOC_EXTERNAL_THREAD_ALLOC only supports the default allocator."); return ThreadAllocExternal::get(); #else - return ThreadLocal::get(); + return ThreadLocal>::get(); #endif } } // namespace snmalloc \ No newline at end of file diff --git a/src/snmalloc/mem/corealloc.h b/src/snmalloc/mem/corealloc.h index c7fc79b72..991a845d6 100644 --- a/src/snmalloc/mem/corealloc.h +++ b/src/snmalloc/mem/corealloc.h @@ -38,7 +38,7 @@ namespace snmalloc Pooled>, Empty> { - template + template friend class LocalAllocator; /** diff --git a/src/snmalloc/mem/localalloc.h b/src/snmalloc/mem/localalloc.h index 2e69f7ac6..50246ae0a 100644 --- a/src/snmalloc/mem/localalloc.h +++ b/src/snmalloc/mem/localalloc.h @@ -41,6 +41,13 @@ namespace snmalloc OnePastEnd }; + /** + * @brief Class that is used to index types to get partitions. This + * type represents the default partition, other partitions can be created + * by using a different type. + */ + struct MainPartition {}; + /** * A local allocator contains the fast-path allocation routines and * encapsulates all of the behaviour of an allocator that is local to some @@ -58,7 +65,7 @@ namespace snmalloc * core allocator must be provided externally by invoking the `init` method * on this class *before* any allocation-related methods are called. */ - template + template class LocalAllocator { public: diff --git a/src/snmalloc/override/libc.h b/src/snmalloc/override/libc.h index 624da4eb1..55aa49142 100644 --- a/src/snmalloc/override/libc.h +++ b/src/snmalloc/override/libc.h @@ -24,21 +24,25 @@ namespace snmalloc::libc return get_alloc().external_pointer(ptr); } + template SNMALLOC_FAST_PATH_INLINE void* malloc(size_t size) { - return get_alloc().alloc(size); + return get_alloc().alloc(size); } + template SNMALLOC_FAST_PATH_INLINE void free(void* ptr) { - get_alloc().dealloc(ptr); + get_alloc().dealloc(ptr); } + template SNMALLOC_FAST_PATH_INLINE void free_sized(void* ptr, size_t size) { - get_alloc().dealloc(ptr, size); + get_alloc().dealloc(ptr, size); } + template SNMALLOC_FAST_PATH_INLINE void* calloc(size_t nmemb, size_t size) { bool overflow = false; @@ -47,12 +51,13 @@ namespace snmalloc::libc { return set_error(); } - return get_alloc().alloc(sz); + return get_alloc().template alloc(sz); } + template SNMALLOC_FAST_PATH_INLINE void* realloc(void* ptr, size_t size) { - auto& a = get_alloc(); + auto& a = get_alloc(); size_t sz = a.alloc_size(ptr); // Keep the current allocation if the given size is in the same sizeclass. if (sz == round_size(size)) @@ -93,6 +98,7 @@ namespace snmalloc::libc return get_alloc().alloc_size(ptr); } + template inline void* reallocarray(void* ptr, size_t nmemb, size_t size) { bool overflow = false; @@ -101,13 +107,14 @@ namespace snmalloc::libc { return set_error(); } - return realloc(ptr, sz); + return realloc(ptr, sz); } + template inline int reallocarr(void* ptr_, size_t nmemb, size_t size) { int err = errno; - auto& a = get_alloc(); + auto& a = get_alloc(); bool overflow = false; size_t sz = bits::umul(size, nmemb, overflow); if (SNMALLOC_UNLIKELY(sz == 0)) @@ -140,6 +147,7 @@ namespace snmalloc::libc return 0; } + template inline void* memalign(size_t alignment, size_t size) { if (SNMALLOC_UNLIKELY( @@ -148,15 +156,17 @@ namespace snmalloc::libc return set_error(EINVAL); } - return malloc(aligned_size(alignment, size)); + return malloc(aligned_size(alignment, size)); } + template inline void* aligned_alloc(size_t alignment, size_t size) { SNMALLOC_ASSERT((size % alignment) == 0); - return memalign(alignment, size); + return memalign(alignment, size); } + template inline int posix_memalign(void** memptr, size_t alignment, size_t size) { if (SNMALLOC_UNLIKELY( @@ -165,7 +175,7 @@ namespace snmalloc::libc return EINVAL; } - void* p = memalign(alignment, size); + void* p = memalign(alignment, size); if (SNMALLOC_UNLIKELY(p == nullptr)) { if (size != 0) From 04815cb4b71a0f3454d23836b811f6a990ecf6c0 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 28 Sep 2023 15:46:28 +0100 Subject: [PATCH 5/5] Add example usage. --- src/test/func/partition/partition.cc | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/test/func/partition/partition.cc diff --git a/src/test/func/partition/partition.cc b/src/test/func/partition/partition.cc new file mode 100644 index 000000000..67d80f6ed --- /dev/null +++ b/src/test/func/partition/partition.cc @@ -0,0 +1,26 @@ +#include +#include + +struct OtherPartition{}; + +int main() +{ + std::vector allocs; + + for (size_t i = 0; i < 100; i++) + { + allocs.push_back(snmalloc::libc::malloc(1)); + std::cout << "Allocated " << allocs.back() << std::endl; + } + + for (size_t i = 0; i < 100; i++) + { + allocs.push_back(snmalloc::libc::malloc(1)); + std::cout << "Allocated " << allocs.back() << std::endl; + } + + for (auto p : allocs) + { + snmalloc::libc::free(p); + } +}