Refactor: Remove unused features and functions, and move most allocator operations to a global namespace. (#750)

* Factor out explicit Config type

Instead of using snmalloc::Alloc::Config, expose snmalloc::Config, which is then used to derive the allocator type.

* Move globalalloc to front end.

* Remove unneed template parameter from global snmalloc functions.

* Remove SNMALLOC_PASS_THROUGH

VeronaRT now has an abstraction layer which can easily replace the allocator.
Having such a complex integration still in snmalloc does not make sense.

* Take some global functions off of local alloc.

* Drop comparison overloads on atomic Capptr.

Performing a comparison on two atomic ptr is a complex operation, and should not be implicit.  The memory model order and such things needs to be considered by the caller.

* Remove function_ref and use templates

The implementation prefers to use templates over the function_ref.  This now only exists in the Pal for a currently unused feature.

* Removing function_ref reduces stl needs.

* Remove use of __is_convertible to support older g++

* Inline function that is only used once.

* Remove unused function

* Restrict ThreadAlloc usage to globalalloc

This commit introduces various inline functions on snmalloc:: that perform allocation/deallocation using the thread local allocator.

They remove all usage from a particular test.

* Move cheri checks to own file.

* Refactor is_owned checks.

* Move alloc_size and check_size to globalalloc.

* Minor simplification of dealloc path

* Fix up is_owned to take a config

* Improve usage of scoped allocator.

* Handle Config_ in globalalloc.
This commit is contained in:
Matthew Parkinson
2025-02-22 19:53:27 +00:00
committed by GitHub
parent 6d50141e35
commit 5f7baef755
50 changed files with 818 additions and 1403 deletions

View File

@@ -1,6 +1,6 @@
#include <iostream>
#if defined(SNMALLOC_PASS_THROUGH) || !defined(__CHERI_PURE_CAPABILITY__)
#if !defined(__CHERI_PURE_CAPABILITY__)
// This test does not make sense in pass-through or w/o CHERI
int main()
{

View File

@@ -14,8 +14,8 @@
namespace snmalloc
{
// Create an allocator that stores an std::atomic<size_t>> per allocation.
using Alloc = snmalloc::LocalAllocator<snmalloc::StandardConfigClientMeta<
ArrayClientMetaDataProvider<std::atomic<size_t>>>>;
using Config = snmalloc::StandardConfigClientMeta<
ArrayClientMetaDataProvider<std::atomic<size_t>>>;
}
#define SNMALLOC_PROVIDE_OWN_CONFIG
@@ -23,9 +23,8 @@ namespace snmalloc
int main()
{
#if defined(SNMALLOC_PASS_THROUGH) || \
defined(SNMALLOC_ENABLE_GWP_ASAN_INTEGRATION)
// This test does not make sense in pass-through
#if defined(SNMALLOC_ENABLE_GWP_ASAN_INTEGRATION)
// This test does not make sense in GWP-ASan mode.
return 0;
#else
// Allocate a bunch of objects, and store the index into the meta-data.
@@ -33,7 +32,7 @@ int main()
for (size_t i = 0; i < 10000; i++)
{
auto p = snmalloc::libc::malloc(1024);
auto& meta = snmalloc::libc::get_client_meta_data(p);
auto& meta = snmalloc::get_client_meta_data(p);
meta = i;
ptrs.push_back(p);
memset(p, (uint8_t)i, 1024);
@@ -44,7 +43,7 @@ int main()
for (size_t i = 0; i < 10000; i++)
{
auto p = ptrs[i];
auto& meta = snmalloc::libc::get_client_meta_data(p);
auto& meta = snmalloc::get_client_meta_data(p);
if (meta != i)
{
std::cout << "Failed at index " << i << std::endl;
@@ -63,7 +62,7 @@ int main()
// Access in a read-only way meta-data associated with the stack.
// This would fail if it was accessed for write.
auto& meta = snmalloc::libc::get_client_meta_data_const(&ptrs);
auto& meta = snmalloc::get_client_meta_data_const(&ptrs);
std::cout << "meta for stack" << meta << std::endl;
return 0;

View File

@@ -1,22 +1,14 @@
#include <iostream>
#ifdef SNMALLOC_PASS_THROUGH
// This test does not make sense in pass-through
int main()
{
return 0;
}
#else
// # define SNMALLOC_TRACING
# include <snmalloc/backend/backend.h>
# include <snmalloc/backend/standard_range.h>
# include <snmalloc/backend_helpers/backend_helpers.h>
# include <snmalloc/snmalloc_core.h>
#include <snmalloc/backend/backend.h>
#include <snmalloc/backend/standard_range.h>
#include <snmalloc/backend_helpers/backend_helpers.h>
#include <snmalloc/snmalloc_core.h>
// Specify type of allocator
# define SNMALLOC_PROVIDE_OWN_CONFIG
#define SNMALLOC_PROVIDE_OWN_CONFIG
namespace snmalloc
{
@@ -98,9 +90,9 @@ namespace snmalloc
if (domesticate_trace)
{
std::cout << "Domesticating " << p.unsafe_ptr()
# if __has_builtin(__builtin_return_address)
#if __has_builtin(__builtin_return_address)
<< " from " << __builtin_return_address(0)
# endif
#endif
<< std::endl;
}
@@ -121,11 +113,11 @@ namespace snmalloc
}
};
using Alloc = LocalAllocator<CustomConfig>;
using Config = CustomConfig;
}
# define SNMALLOC_NAME_MANGLE(a) test_##a
# include <snmalloc/override/malloc.cc>
#define SNMALLOC_NAME_MANGLE(a) test_##a
#include <snmalloc/override/malloc.cc>
int main()
{
@@ -141,7 +133,7 @@ int main()
entropy.make_free_list_key(RemoteAllocator::key_global);
entropy.make_free_list_key(freelist::Object::key_root);
auto alloc1 = new Alloc();
ScopedAllocator alloc1;
// Allocate from alloc1; the size doesn't matter a whole lot, it just needs to
// be a small object and so definitely owned by this allocator rather.
@@ -149,7 +141,7 @@ int main()
std::cout << "Allocated p " << p << std::endl;
// Put that free object on alloc1's remote queue
auto alloc2 = new Alloc();
ScopedAllocator alloc2;
alloc2->dealloc(p);
alloc2->flush();
@@ -184,12 +176,5 @@ int main()
static constexpr size_t expected_count =
snmalloc::CustomConfig::Options.QueueHeadsAreTame ? 2 : 3;
SNMALLOC_CHECK(snmalloc::CustomConfig::domesticate_count == expected_count);
// Prevent the allocators from going out of scope during the above test
alloc1->flush();
alloc2->flush();
return 0;
}
#endif
}

View File

@@ -1,5 +1,4 @@
#if defined(SNMALLOC_PASS_THROUGH) || defined(_WIN32) || \
!defined(TODO_REINSTATE_POSSIBLY)
#if defined(_WIN32) || !defined(TODO_REINSTATE_POSSIBLY)
// This test does not make sense with malloc pass-through, skip it.
// The malloc definitions are also currently incompatible with Windows headers
// so skip this test on Windows as well.

View File

@@ -13,29 +13,14 @@
void alloc1(size_t size)
{
void* r = snmalloc::ThreadAlloc::get().alloc(size);
snmalloc::ThreadAlloc::get().dealloc(r);
void* r = snmalloc::alloc(size);
snmalloc::dealloc(r);
}
void alloc2(size_t size)
{
auto& a = snmalloc::ThreadAlloc::get();
void* r = a.alloc(size);
a.dealloc(r);
}
void alloc3(size_t size)
{
auto& a = snmalloc::ThreadAlloc::get();
void* r = a.alloc(size);
a.dealloc(r, size);
}
void alloc4(size_t size)
{
auto& a = snmalloc::ThreadAlloc::get();
void* r = a.alloc(size);
a.dealloc(r);
void* r = snmalloc::alloc(size);
snmalloc::dealloc(r, size);
}
void check_calloc(void* p, size_t size)
@@ -62,79 +47,43 @@ void check_calloc(void* p, size_t size)
void calloc1(size_t size)
{
void* r =
snmalloc::ThreadAlloc::get().alloc<snmalloc::ZeroMem::YesZero>(size);
void* r = snmalloc::alloc<snmalloc::ZeroMem::YesZero>(size);
check_calloc(r, size);
snmalloc::ThreadAlloc::get().dealloc(r);
snmalloc::dealloc(r);
}
void calloc2(size_t size)
{
auto& a = snmalloc::ThreadAlloc::get();
void* r = a.alloc<snmalloc::ZeroMem::YesZero>(size);
void* r = snmalloc::alloc<snmalloc::ZeroMem::YesZero>(size);
check_calloc(r, size);
a.dealloc(r);
}
void calloc3(size_t size)
{
auto& a = snmalloc::ThreadAlloc::get();
void* r = a.alloc<snmalloc::ZeroMem::YesZero>(size);
check_calloc(r, size);
a.dealloc(r, size);
}
void calloc4(size_t size)
{
auto& a = snmalloc::ThreadAlloc::get();
void* r = a.alloc<snmalloc::ZeroMem::YesZero>(size);
check_calloc(r, size);
a.dealloc(r);
snmalloc::dealloc(r, size);
}
void dealloc1(void* p, size_t)
{
snmalloc::ThreadAlloc::get().dealloc(p);
snmalloc::dealloc(p);
}
void dealloc2(void* p, size_t size)
{
snmalloc::ThreadAlloc::get().dealloc(p, size);
}
void dealloc3(void* p, size_t)
{
snmalloc::ThreadAlloc::get().dealloc(p);
}
void dealloc4(void* p, size_t size)
{
snmalloc::ThreadAlloc::get().dealloc(p, size);
snmalloc::dealloc(p, size);
}
void f(size_t size)
{
auto t1 = std::thread(alloc1, size);
auto t2 = std::thread(alloc2, size);
auto t3 = std::thread(alloc3, size);
auto t4 = std::thread(alloc4, size);
auto t5 = std::thread(calloc1, size);
auto t6 = std::thread(calloc2, size);
auto t7 = std::thread(calloc3, size);
auto t8 = std::thread(calloc4, size);
auto t3 = std::thread(calloc1, size);
auto t4 = std::thread(calloc2, size);
{
auto a = snmalloc::get_scoped_allocator();
auto p1 = a->alloc(size);
auto p2 = a->alloc(size);
auto p3 = a->alloc(size);
auto p4 = a->alloc(size);
auto t9 = std::thread(dealloc1, p1, size);
auto t10 = std::thread(dealloc2, p2, size);
auto t11 = std::thread(dealloc3, p3, size);
auto t12 = std::thread(dealloc4, p4, size);
auto t5 = std::thread(dealloc1, p1, size);
auto t6 = std::thread(dealloc2, p2, size);
t1.join();
t2.join();
@@ -142,14 +91,8 @@ void f(size_t size)
t4.join();
t5.join();
t6.join();
t7.join();
t8.join();
t9.join();
t10.join();
t11.join();
t12.join();
} // Drops a.
// snmalloc::current_alloc_pool()->debug_in_use(0);
snmalloc::debug_in_use(0);
printf(".");
fflush(stdout);
}

View File

@@ -17,7 +17,6 @@ using FixedAlloc = LocalAllocator<CustomGlobals>;
int main()
{
#ifndef SNMALLOC_PASS_THROUGH // Depends on snmalloc specific features
setup();
// 28 is large enough to produce a nested allocator.
@@ -31,14 +30,14 @@ int main()
<< pointer_offset(oe_base, size) << std::endl;
CustomGlobals::init(nullptr, oe_base, size);
FixedAlloc a;
auto a = get_scoped_allocator<FixedAlloc>();
size_t object_size = 128;
size_t count = 0;
size_t i = 0;
while (true)
{
auto r1 = a.alloc(object_size);
auto r1 = a->alloc(object_size);
count += object_size;
i++;
@@ -48,9 +47,9 @@ int main()
if (r1 == nullptr)
break;
if (!a.is_snmalloc_owned(r1))
if (!snmalloc::is_owned<CustomGlobals>(r1))
{
a.dealloc(r1);
a->dealloc(r1);
continue;
}
@@ -75,7 +74,4 @@ int main()
std::cout << "Total allocated: " << count << " out of " << size << std::endl;
std::cout << "Overhead: 1/" << (double)size / (double)(size - count)
<< std::endl;
a.teardown();
#endif
}

View File

@@ -318,9 +318,6 @@ extern "C"
int main()
{
#ifdef SNMALLOC_PASS_THROUGH
return 0;
#endif
check_lg_align_macro<63>();
static_assert(
OUR_MALLOCX_ZERO == MALLOCX_ZERO, "Our MALLOCX_ZERO macro is wrong");

View File

@@ -33,21 +33,7 @@ void check_result(size_t size, size_t align, void* p, int err, bool null)
}
const auto alloc_size = our_malloc_usable_size(p);
auto expected_size = our_malloc_good_size(size);
#ifdef SNMALLOC_PASS_THROUGH
// Calling system allocator may allocate a larger block than
// snmalloc. Note, we have called the system allocator with
// the size snmalloc would allocate, so it won't be smaller.
const auto exact_size = false;
// We allocate MIN_ALLOC_SIZE byte for 0-sized allocations (and so round_size
// will tell us that the minimum size is MIN_ALLOC_SIZE), but the system
// allocator may return a 0-sized allocation.
if (size == 0)
{
expected_size = 0;
}
#else
const auto exact_size = align == 1;
#endif
#ifdef __CHERI_PURE_CAPABILITY__
const auto cheri_size = __builtin_cheri_length_get(p);
if (cheri_size != alloc_size && (size != 0))
@@ -376,6 +362,6 @@ int main(int argc, char** argv)
our_malloc_usable_size(nullptr) == 0,
"malloc_usable_size(nullptr) should be zero");
snmalloc::debug_check_empty<snmalloc::Alloc::Config>();
snmalloc::debug_check_empty();
return 0;
}

View File

@@ -19,12 +19,7 @@ int main()
# endif
# define SNMALLOC_FAIL_FAST false
# define SNMALLOC_STATIC_LIBRARY_PREFIX my_
# ifndef SNMALLOC_PASS_THROUGH
# include "snmalloc/override/malloc.cc"
# else
# define my_malloc(x) malloc(x)
# define my_free(x) free(x)
# endif
# include "snmalloc/override/malloc.cc"
# include "snmalloc/override/memcpy.cc"
# include "test/helpers.h"
@@ -150,9 +145,6 @@ void check_bounds(size_t size, size_t out_of_bounds)
int main()
{
// Skip the checks that expect bounds checks to fail when we are not the
// malloc implementation.
# if !defined(SNMALLOC_PASS_THROUGH)
// Some sizes to check for out-of-bounds access. As we are only able to
// catch overflows past the end of the sizeclass-padded allocation, make
// sure we don't try to test on smaller allocations.
@@ -173,10 +165,9 @@ int main()
// Check one object out of bounds
check_bounds(sz, sz);
}
# endif
for (size_t x = 0; x < 2048; x++)
{
check_size(x);
}
}
#endif
#endif

View File

@@ -69,10 +69,9 @@ void test_limited(rlim64_t as_limit, size_t& count)
upper_bound, static_cast<unsigned long long>(info.freeram >> 3u));
std::cout << "trying to alloc " << upper_bound / KiB << " KiB" << std::endl;
# endif
auto& alloc = ThreadAlloc::get();
std::cout << "allocator initialised" << std::endl;
auto chunk = alloc.alloc(upper_bound);
alloc.dealloc(chunk);
auto chunk = snmalloc::alloc(upper_bound);
snmalloc::dealloc(chunk);
std::cout << "success" << std::endl;
std::exit(0);
}
@@ -91,8 +90,6 @@ void test_limited(rlim64_t as_limit, size_t& count)
void test_alloc_dealloc_64k()
{
auto& alloc = ThreadAlloc::get();
constexpr size_t count = 1 << 12;
constexpr size_t outer_count = 12;
void* garbage[count];
@@ -104,26 +101,25 @@ void test_alloc_dealloc_64k()
// This will fill the short slab, and then start a new slab.
for (size_t i = 0; i < count; i++)
{
garbage[i] = alloc.alloc(16);
garbage[i] = snmalloc::alloc(16);
}
// Allocate one object on the second slab
keep_alive[j] = alloc.alloc(16);
keep_alive[j] = snmalloc::alloc(16);
for (size_t i = 0; i < count; i++)
{
alloc.dealloc(garbage[i]);
snmalloc::dealloc(garbage[i]);
}
}
for (size_t j = 0; j < outer_count; j++)
{
alloc.dealloc(keep_alive[j]);
snmalloc::dealloc(keep_alive[j]);
}
}
void test_random_allocation()
{
auto& alloc = ThreadAlloc::get();
std::unordered_set<void*> allocated;
constexpr size_t count = 10000;
@@ -146,13 +142,13 @@ void test_random_allocation()
if (cell != nullptr)
{
allocated.erase(cell);
alloc.dealloc(cell);
snmalloc::dealloc(cell);
cell = nullptr;
alloc_count--;
}
if (!just_dealloc)
{
cell = alloc.alloc(16);
cell = snmalloc::alloc(16);
auto pair = allocated.insert(cell);
// Check not already allocated
SNMALLOC_CHECK(pair.second);
@@ -170,20 +166,18 @@ void test_random_allocation()
// Deallocate all the remaining objects
for (size_t i = 0; i < count; i++)
if (objects[i] != nullptr)
alloc.dealloc(objects[i]);
snmalloc::dealloc(objects[i]);
}
void test_calloc()
{
auto& alloc = ThreadAlloc::get();
for (size_t size = 16; size <= (1 << 24); size <<= 1)
{
void* p = alloc.alloc(size);
void* p = snmalloc::alloc(size);
memset(p, 0xFF, size);
alloc.dealloc(p, size);
snmalloc::dealloc(p, size);
p = alloc.alloc<YesZero>(size);
p = snmalloc::alloc<YesZero>(size);
for (size_t i = 0; i < size; i++)
{
@@ -191,10 +185,10 @@ void test_calloc()
abort();
}
alloc.dealloc(p, size);
snmalloc::dealloc(p, size);
}
snmalloc::debug_check_empty<snmalloc::Alloc::Config>();
snmalloc::debug_check_empty();
}
void test_double_alloc()
@@ -227,48 +221,46 @@ void test_double_alloc()
while (!set1.empty())
{
auto it = set1.begin();
a2->dealloc(*it, 20);
a2->dealloc(*it);
set1.erase(it);
}
while (!set2.empty())
{
auto it = set2.begin();
a1->dealloc(*it, 20);
a1->dealloc(*it);
set2.erase(it);
}
}
}
snmalloc::debug_check_empty<snmalloc::Alloc::Config>();
snmalloc::debug_check_empty();
}
void test_external_pointer()
{
// Malloc does not have an external pointer querying mechanism.
auto& alloc = ThreadAlloc::get();
for (snmalloc::smallsizeclass_t sc = size_to_sizeclass(MIN_ALLOC_SIZE);
sc < NUM_SMALL_SIZECLASSES;
sc++)
{
size_t size = sizeclass_to_size(sc);
void* p1 = alloc.alloc(size);
void* p1 = snmalloc::alloc(size);
if (size != alloc.alloc_size(p1))
if (size != snmalloc::alloc_size(p1))
{
std::cout << "Requested size: " << size
<< " alloc_size: " << alloc.alloc_size(p1) << std::endl;
<< " alloc_size: " << snmalloc::alloc_size(p1) << std::endl;
abort();
}
for (size_t offset = 0; offset < size; offset += 17)
{
void* p2 = pointer_offset(p1, offset);
void* p3 = alloc.external_pointer(p2);
void* p4 = alloc.external_pointer<End>(p2);
void* p3 = snmalloc::external_pointer(p2);
void* p4 = snmalloc::external_pointer<End>(p2);
if (p1 != p3)
{
std::cout << "size: " << size << " alloc_size: " << alloc.alloc_size(p1)
std::cout << "size: " << size
<< " alloc_size: " << snmalloc::alloc_size(p1)
<< " offset: " << offset << " p1: " << p1 << " p3: " << p3
<< std::endl;
}
@@ -282,16 +274,15 @@ void test_external_pointer()
SNMALLOC_CHECK((size_t)p4 == (size_t)p1 + size - 1);
}
alloc.dealloc(p1, size);
snmalloc::dealloc(p1, size);
}
snmalloc::debug_check_empty<snmalloc::Alloc::Config>();
snmalloc::debug_check_empty();
};
void check_offset(void* base, void* interior)
{
auto& alloc = ThreadAlloc::get();
void* calced_base = alloc.external_pointer((void*)interior);
void* calced_base = snmalloc::external_pointer((void*)interior);
if (calced_base != (void*)base)
{
std::cout << "Calced base: " << calced_base << " actual base: " << base
@@ -315,8 +306,6 @@ void test_external_pointer_large()
{
xoroshiro::p128r64 r;
auto& alloc = ThreadAlloc::get();
constexpr size_t count_log = DefaultPal::address_bits > 32 ? 5 : 3;
constexpr size_t count = 1 << count_log;
// Pre allocate all the objects
@@ -331,9 +320,9 @@ void test_external_pointer_large()
size_t size = (1 << 24) + rand;
total_size += size;
// store object
objects[i] = (size_t*)alloc.alloc(size);
objects[i] = (size_t*)snmalloc::alloc(size);
// Store allocators size for this object
*objects[i] = alloc.alloc_size(objects[i]);
*objects[i] = snmalloc::alloc_size(objects[i]);
check_external_pointer_large(objects[i]);
if (i > 0)
@@ -351,33 +340,32 @@ void test_external_pointer_large()
// Deallocate everything
for (size_t i = 0; i < count; i++)
{
alloc.dealloc(objects[i]);
snmalloc::dealloc(objects[i]);
}
}
void test_external_pointer_dealloc_bug()
{
std::cout << "Testing external pointer dealloc bug" << std::endl;
auto& alloc = ThreadAlloc::get();
constexpr size_t count = MIN_CHUNK_SIZE;
void* allocs[count];
for (size_t i = 0; i < count; i++)
{
allocs[i] = alloc.alloc(MIN_CHUNK_BITS / 2);
allocs[i] = snmalloc::alloc(MIN_CHUNK_BITS / 2);
}
for (size_t i = 1; i < count; i++)
{
alloc.dealloc(allocs[i]);
snmalloc::dealloc(allocs[i]);
}
for (size_t i = 0; i < count; i++)
{
alloc.external_pointer(allocs[i]);
snmalloc::external_pointer(allocs[i]);
}
alloc.dealloc(allocs[0]);
snmalloc::dealloc(allocs[0]);
std::cout << "Testing external pointer dealloc bug - done" << std::endl;
}
@@ -387,15 +375,12 @@ void test_external_pointer_stack()
std::array<int, 2000> stack;
auto& alloc = ThreadAlloc::get();
for (size_t i = 0; i < stack.size(); i++)
{
if (alloc.external_pointer(&stack[i]) > &stack[i])
if (snmalloc::external_pointer(&stack[i]) > &stack[i])
{
std::cout << "Stack pointer: " << &stack[i]
<< " external pointer: " << alloc.external_pointer(&stack[i])
<< std::endl;
std::cout << "Stack pointer: " << &stack[i] << " external pointer: "
<< snmalloc::external_pointer(&stack[i]) << std::endl;
abort();
}
}
@@ -405,92 +390,97 @@ void test_external_pointer_stack()
void test_alloc_16M()
{
auto& alloc = ThreadAlloc::get();
// sizes >= 16M use large_alloc
const size_t size = 16'000'000;
void* p1 = alloc.alloc(size);
SNMALLOC_CHECK(alloc.alloc_size(alloc.external_pointer(p1)) >= size);
alloc.dealloc(p1);
void* p1 = snmalloc::alloc(size);
SNMALLOC_CHECK(snmalloc::alloc_size(snmalloc::external_pointer(p1)) >= size);
snmalloc::dealloc(p1);
}
void test_calloc_16M()
{
auto& alloc = ThreadAlloc::get();
// sizes >= 16M use large_alloc
const size_t size = 16'000'000;
void* p1 = alloc.alloc<YesZero>(size);
SNMALLOC_CHECK(alloc.alloc_size(alloc.external_pointer(p1)) >= size);
alloc.dealloc(p1);
void* p1 = snmalloc::alloc<YesZero>(size);
SNMALLOC_CHECK(snmalloc::alloc_size(snmalloc::external_pointer(p1)) >= size);
snmalloc::dealloc(p1);
}
void test_calloc_large_bug()
{
auto& alloc = ThreadAlloc::get();
// 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
// not a multiple of page size.
const size_t size = (MAX_SMALL_SIZECLASS_SIZE << 3) - 7;
void* p1 = alloc.alloc<YesZero>(size);
SNMALLOC_CHECK(alloc.alloc_size(alloc.external_pointer(p1)) >= size);
alloc.dealloc(p1);
void* p1 = snmalloc::alloc<YesZero>(size);
SNMALLOC_CHECK(snmalloc::alloc_size(snmalloc::external_pointer(p1)) >= size);
snmalloc::dealloc(p1);
}
template<size_t asz, int dealloc>
template<size_t asz, int dealloc = 2>
void test_static_sized_alloc()
{
auto& alloc = ThreadAlloc::get();
auto p = alloc.alloc<asz>();
auto p = snmalloc::alloc<asz>();
static_assert((dealloc >= 0) && (dealloc <= 2), "bad dealloc flavor");
switch (dealloc)
{
case 0:
alloc.dealloc(p);
snmalloc::dealloc(p);
break;
case 1:
alloc.dealloc(p, asz);
snmalloc::dealloc(p, asz);
break;
case 2:
alloc.dealloc<asz>(p);
snmalloc::dealloc<asz>(p);
break;
}
if constexpr (dealloc != 0)
test_static_sized_alloc<asz, dealloc - 1>();
}
template<size_t max_size = bits::one_at_bit(23)>
void test_static_sized_allocs()
{
// For each small, medium, and large class, do each kind dealloc. This is
// mostly to ensure that all of these forms compile.
for (size_t sc = 0; sc < NUM_SMALL_SIZECLASSES; sc++)
{
// test_static_sized_alloc<sc, 0>();
// test_static_sized_alloc<sc, 1>();
// test_static_sized_alloc<sc, 2>();
}
// test_static_sized_alloc<sizeclass_to_size(NUM_SMALL_CLASSES + 1), 0>();
// test_static_sized_alloc<sizeclass_to_size(NUM_SMALL_CLASSES + 1), 1>();
// test_static_sized_alloc<sizeclass_to_size(NUM_SMALL_CLASSES + 1), 2>();
if (max_size < 16)
return;
// test_static_sized_alloc<large_sizeclass_to_size(0), 0>();
// test_static_sized_alloc<large_sizeclass_to_size(0), 1>();
// test_static_sized_alloc<large_sizeclass_to_size(0), 2>();
constexpr size_t next_size = max_size >> 1;
test_static_sized_allocs<next_size>();
test_static_sized_alloc<max_size * 3>();
test_static_sized_alloc<max_size * 5>();
test_static_sized_alloc<max_size * 7>();
test_static_sized_alloc<max_size * 1>();
test_static_sized_alloc<max_size * 3 - 1>();
test_static_sized_alloc<max_size * 5 - 1>();
test_static_sized_alloc<max_size * 7 - 1>();
test_static_sized_alloc<max_size * 1 - 1>();
test_static_sized_alloc<max_size * 3 + 1>();
test_static_sized_alloc<max_size * 5 + 1>();
test_static_sized_alloc<max_size * 7 + 1>();
test_static_sized_alloc<max_size * 1 + 1>();
}
void test_remaining_bytes()
{
auto& alloc = ThreadAlloc::get();
for (snmalloc::smallsizeclass_t sc = size_to_sizeclass(MIN_ALLOC_SIZE);
sc < NUM_SMALL_SIZECLASSES;
sc++)
{
auto size = sizeclass_to_size(sc);
char* p = (char*)alloc.alloc(size);
char* p = (char*)snmalloc::alloc(size);
for (size_t offset = 0; offset < size; offset++)
{
auto rem = alloc.remaining_bytes(address_cast(pointer_offset(p, offset)));
auto rem =
snmalloc::remaining_bytes(address_cast(pointer_offset(p, offset)));
if (rem != (size - offset))
{
printf(
@@ -503,7 +493,7 @@ void test_remaining_bytes()
abort();
}
}
alloc.dealloc(p);
snmalloc::dealloc(p);
}
}
@@ -513,18 +503,16 @@ 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();
for (size_t i = 0; i < 27; i++)
{
std::vector<void*> allocs;
for (size_t j = 0; j < 4; j++)
{
allocs.push_back(alloc.alloc(bits::one_at_bit(i)));
allocs.push_back(snmalloc::alloc(bits::one_at_bit(i)));
}
for (auto a : allocs)
{
alloc.dealloc(a);
snmalloc::dealloc(a);
}
}
}
@@ -557,7 +545,6 @@ int main(int argc, char** argv)
test_random_allocation();
test_calloc();
test_double_alloc();
#ifndef SNMALLOC_PASS_THROUGH // Depends on snmalloc specific features
test_remaining_bytes();
test_static_sized_allocs();
test_calloc_large_bug();
@@ -567,7 +554,6 @@ int main(int argc, char** argv)
test_external_pointer();
test_alloc_16M();
test_calloc_16M();
#endif
test_consolidaton_bug();
return 0;
}

View File

@@ -79,7 +79,6 @@ int main(int argc, char** argv)
{
UNUSED(argc);
UNUSED(argv);
#ifndef SNMALLOC_PASS_THROUGH // Depends on snmalloc specific features
setup();
add_n_allocs(5);
@@ -103,5 +102,4 @@ int main(int argc, char** argv)
remove_n_allocs(3);
std::cout << "Teardown complete!" << std::endl;
#endif
}

View File

@@ -25,8 +25,8 @@ namespace snmalloc
{
// Instantiate the allocator with a client meta data provider that uses an
// atomic size_t to store the reference count.
using Alloc = snmalloc::LocalAllocator<snmalloc::StandardConfigClientMeta<
ArrayClientMetaDataProvider<std::atomic<size_t>>>>;
using Config = snmalloc::StandardConfigClientMeta<
ArrayClientMetaDataProvider<std::atomic<size_t>>>;
}
# define SNMALLOC_PROVIDE_OWN_CONFIG
@@ -58,7 +58,7 @@ namespace snmalloc::miracle
if (SNMALLOC_UNLIKELY(p == nullptr))
return nullptr;
snmalloc::libc::get_client_meta_data(p) = 1;
snmalloc::get_client_meta_data(p) = 1;
return p;
}
@@ -68,8 +68,7 @@ namespace snmalloc::miracle
return;
// TODO could build a check into this that it is the start of the object?
auto previous =
snmalloc::libc::get_client_meta_data(ptr).fetch_add((size_t)-1);
auto previous = snmalloc::get_client_meta_data(ptr).fetch_add((size_t)-1);
if (SNMALLOC_LIKELY(previous == 1))
{
@@ -88,8 +87,7 @@ namespace snmalloc::miracle
inline void acquire(void* p)
{
auto previous =
snmalloc::libc::get_client_meta_data(p).fetch_add((size_t)2);
auto previous = snmalloc::get_client_meta_data(p).fetch_add((size_t)2);
// Can we take new pointers to a deallocated object?
check((previous & 1) == 1, "Acquiring a deallocated object");
@@ -97,8 +95,7 @@ namespace snmalloc::miracle
inline void release(void* p)
{
auto previous =
snmalloc::libc::get_client_meta_data(p).fetch_add((size_t)-2);
auto previous = snmalloc::get_client_meta_data(p).fetch_add((size_t)-2);
if (previous > 2)
return;
@@ -185,7 +182,6 @@ void operator delete(void* p, size_t)
int main()
{
# ifndef SNMALLOC_PASS_THROUGH
snmalloc::miracle::raw_ptr<int> p;
{
auto up1 = std::make_unique<int>(41);
@@ -199,7 +195,6 @@ int main()
// raw_ptr has kept the memory live.
// Current implementation zeros the memory when the unique_ptr is destroyed.
check(*p == 0, "Failed to keep memory live");
# endif
return 0;
}
#endif

View File

@@ -1,4 +1,4 @@
#if defined(SNMALLOC_PASS_THROUGH) || true
#if true
/*
* This test does not make sense with malloc pass-through, skip it.
*/
@@ -244,7 +244,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 snmalloc::alloc(sb_size);
}
};
}
@@ -260,7 +260,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);
snmalloc::dealloc(ptr);
};
auto check_with_sb = [&](Sandbox& sb) {
// Check with a range of sizes

View File

@@ -1,23 +1,16 @@
#ifdef SNMALLOC_PASS_THROUGH // This test depends on snmalloc internals
int main()
{
return 0;
}
#else
# include <iostream>
# include <snmalloc/snmalloc.h>
# include <vector>
#include <iostream>
#include <snmalloc/snmalloc.h>
#include <vector>
template<size_t size>
void debug_check_empty_1()
{
std::cout << "debug_check_empty_1 " << size << std::endl;
snmalloc::Alloc& a = snmalloc::ThreadAlloc::get();
bool result;
auto r = a.alloc(size);
auto r = snmalloc::alloc(size);
snmalloc::debug_check_empty<snmalloc::Alloc::Config>(&result);
snmalloc::debug_check_empty(&result);
if (result != false)
{
std::cout << "debug_check_empty failed to detect leaked memory:" << size
@@ -25,18 +18,18 @@ void debug_check_empty_1()
abort();
}
a.dealloc(r);
snmalloc::dealloc(r);
snmalloc::debug_check_empty<snmalloc::Alloc::Config>(&result);
snmalloc::debug_check_empty(&result);
if (result != true)
{
std::cout << "debug_check_empty failed to say empty:" << size << std::endl;
abort();
}
r = a.alloc(size);
r = snmalloc::alloc(size);
snmalloc::debug_check_empty<snmalloc::Alloc::Config>(&result);
snmalloc::debug_check_empty(&result);
if (result != false)
{
std::cout << "debug_check_empty failed to detect leaked memory:" << size
@@ -44,9 +37,9 @@ void debug_check_empty_1()
abort();
}
a.dealloc(r);
snmalloc::dealloc(r);
snmalloc::debug_check_empty<snmalloc::Alloc::Config>(&result);
snmalloc::debug_check_empty(&result);
if (result != true)
{
std::cout << "debug_check_empty failed to say empty:" << size << std::endl;
@@ -58,7 +51,6 @@ template<size_t size>
void debug_check_empty_2()
{
std::cout << "debug_check_empty_2 " << size << std::endl;
snmalloc::Alloc& a = snmalloc::ThreadAlloc::get();
bool result;
std::vector<void*> allocs;
// 1GB of allocations
@@ -70,9 +62,9 @@ void debug_check_empty_2()
{
std::cout << "." << std::flush;
}
auto r = a.alloc(size);
auto r = snmalloc::alloc(size);
allocs.push_back(r);
snmalloc::debug_check_empty<snmalloc::Alloc::Config>(&result);
snmalloc::debug_check_empty(&result);
if (result != false)
{
std::cout << "False empty after " << i << " allocations of " << size
@@ -88,17 +80,17 @@ void debug_check_empty_2()
{
std::cout << "." << std::flush;
}
snmalloc::debug_check_empty<snmalloc::Alloc::Config>(&result);
snmalloc::debug_check_empty(&result);
if (result != false)
{
std::cout << "False empty after " << i << " deallocations of " << size
<< std::endl;
abort();
}
a.dealloc(allocs[i]);
snmalloc::dealloc(allocs[i]);
}
std::cout << std::endl;
snmalloc::debug_check_empty<snmalloc::Alloc::Config>();
snmalloc::debug_check_empty();
}
int main()
@@ -115,4 +107,3 @@ int main()
return 0;
}
#endif

View File

@@ -13,43 +13,25 @@
void trigger_teardown()
{
auto& a = snmalloc::ThreadAlloc::get();
// Trigger init
void* r = a.alloc(16);
a.dealloc(r);
void* r = snmalloc::alloc(16);
snmalloc::dealloc(r);
// Force teardown
a.teardown();
snmalloc::debug_teardown();
}
void alloc1(size_t size)
{
trigger_teardown();
void* r = snmalloc::ThreadAlloc::get().alloc(size);
snmalloc::ThreadAlloc::get().dealloc(r);
void* r = snmalloc::alloc(size);
snmalloc::dealloc(r);
}
void alloc2(size_t size)
{
trigger_teardown();
auto& a = snmalloc::ThreadAlloc::get();
void* r = a.alloc(size);
a.dealloc(r);
}
void alloc3(size_t size)
{
trigger_teardown();
auto& a = snmalloc::ThreadAlloc::get();
void* r = a.alloc(size);
a.dealloc(r, size);
}
void alloc4(size_t size)
{
trigger_teardown();
auto& a = snmalloc::ThreadAlloc::get();
void* r = a.alloc(size);
a.dealloc(r);
void* r = snmalloc::alloc(size);
snmalloc::dealloc(r, size);
}
void check_calloc(void* p, size_t size)
@@ -77,86 +59,46 @@ void check_calloc(void* p, size_t size)
void calloc1(size_t size)
{
trigger_teardown();
void* r =
snmalloc::ThreadAlloc::get().alloc<snmalloc::ZeroMem::YesZero>(size);
void* r = snmalloc::alloc<snmalloc::ZeroMem::YesZero>(size);
check_calloc(r, size);
snmalloc::ThreadAlloc::get().dealloc(r);
snmalloc::dealloc(r);
}
void calloc2(size_t size)
{
trigger_teardown();
auto& a = snmalloc::ThreadAlloc::get();
void* r = a.alloc<snmalloc::ZeroMem::YesZero>(size);
void* r = snmalloc::alloc<snmalloc::ZeroMem::YesZero>(size);
check_calloc(r, size);
a.dealloc(r);
}
void calloc3(size_t size)
{
trigger_teardown();
auto& a = snmalloc::ThreadAlloc::get();
void* r = a.alloc<snmalloc::ZeroMem::YesZero>(size);
check_calloc(r, size);
a.dealloc(r, size);
}
void calloc4(size_t size)
{
trigger_teardown();
auto& a = snmalloc::ThreadAlloc::get();
void* r = a.alloc<snmalloc::ZeroMem::YesZero>(size);
check_calloc(r, size);
a.dealloc(r);
snmalloc::dealloc(r, size);
}
void dealloc1(void* p, size_t)
{
trigger_teardown();
snmalloc::ThreadAlloc::get().dealloc(p);
snmalloc::dealloc(p);
}
void dealloc2(void* p, size_t size)
{
trigger_teardown();
snmalloc::ThreadAlloc::get().dealloc(p, size);
}
void dealloc3(void* p, size_t)
{
trigger_teardown();
snmalloc::ThreadAlloc::get().dealloc(p);
}
void dealloc4(void* p, size_t size)
{
trigger_teardown();
snmalloc::ThreadAlloc::get().dealloc(p, size);
snmalloc::dealloc(p, size);
}
void f(size_t size)
{
auto t1 = std::thread(alloc1, size);
auto t2 = std::thread(alloc2, size);
auto t3 = std::thread(alloc3, size);
auto t4 = std::thread(alloc4, size);
auto t5 = std::thread(calloc1, size);
auto t6 = std::thread(calloc2, size);
auto t7 = std::thread(calloc3, size);
auto t8 = std::thread(calloc4, size);
auto t3 = std::thread(calloc1, size);
auto t4 = std::thread(calloc2, size);
{
auto a = snmalloc::get_scoped_allocator();
auto p1 = a->alloc(size);
auto p2 = a->alloc(size);
auto p3 = a->alloc(size);
auto p4 = a->alloc(size);
auto t9 = std::thread(dealloc1, p1, size);
auto t10 = std::thread(dealloc2, p2, size);
auto t11 = std::thread(dealloc3, p3, size);
auto t12 = std::thread(dealloc4, p4, size);
auto t5 = std::thread(dealloc1, p1, size);
auto t6 = std::thread(dealloc2, p2, size);
t1.join();
t2.join();
@@ -164,14 +106,8 @@ void f(size_t size)
t4.join();
t5.join();
t6.join();
t7.join();
t8.join();
t9.join();
t10.join();
t11.join();
t12.join();
} // Drops a.
// snmalloc::current_alloc_pool()->debug_in_use(0);
snmalloc::debug_in_use(0);
printf(".");
fflush(stdout);
}

View File

@@ -13,8 +13,8 @@
namespace snmalloc
{
using Alloc = snmalloc::LocalAllocator<
snmalloc::StandardConfigClientMeta<NoClientMetaDataProvider>>;
using Config = snmalloc::StandardConfigClientMeta<NoClientMetaDataProvider>;
using Alloc = snmalloc::LocalAllocator<Config>;
}
using namespace snmalloc;
@@ -65,16 +65,14 @@ int main()
setup();
allocator_thread_init();
auto& a = ThreadAlloc::get();
for (size_t i = 0; i < 1000; i++)
{
auto r1 = a.alloc(i);
auto r1 = snmalloc::alloc(i);
a.dealloc(r1);
snmalloc::dealloc(r1);
}
ThreadAlloc::get().teardown();
snmalloc::debug_teardown();
// This checks that the scoped allocator does not call
// register clean up, as this configuration will fault

View File

@@ -13,8 +13,7 @@
namespace snmalloc
{
using CustomGlobals = FixedRangeConfig<PALNoAlloc<DefaultPal>>;
using Alloc = LocalAllocator<CustomGlobals>;
using Config = FixedRangeConfig<PALNoAlloc<DefaultPal>>;
}
#define SNMALLOC_NAME_MANGLE(a) enclave_##a
@@ -22,6 +21,5 @@ namespace snmalloc
extern "C" void oe_allocator_init(void* base, void* end)
{
snmalloc::CustomGlobals::init(
nullptr, base, address_cast(end) - address_cast(base));
snmalloc::Config::init(nullptr, base, address_cast(end) - address_cast(base));
}

View File

@@ -75,13 +75,12 @@ size_t swapcount;
void test_tasks_f(size_t id)
{
auto& a = ThreadAlloc::get();
xoroshiro::p128r32 r(id + 5000);
for (size_t n = 0; n < swapcount; n++)
{
size_t size = 16 + (r.next() % 1024);
size_t* res = (size_t*)(use_malloc ? malloc(size) : a.alloc(size));
size_t* res = (size_t*)(use_malloc ? malloc(size) : snmalloc::alloc(size));
if (res != nullptr)
{
@@ -102,7 +101,7 @@ void test_tasks_f(size_t id)
if (use_malloc)
free(out);
else
a.dealloc(out, size);
snmalloc::dealloc(out, size);
}
}
};
@@ -111,8 +110,6 @@ void test_tasks(size_t num_tasks, size_t count, size_t size)
{
std::cout << "Sequential setup" << std::endl;
auto& a = ThreadAlloc::get();
contention = new std::atomic<size_t*>[size];
xoroshiro::p128r32 r;
@@ -120,7 +117,7 @@ void test_tasks(size_t num_tasks, size_t count, size_t size)
{
size_t alloc_size = 16 + (r.next() % 1024);
size_t* res =
(size_t*)(use_malloc ? malloc(alloc_size) : a.alloc(alloc_size));
(size_t*)(use_malloc ? malloc(alloc_size) : snmalloc::alloc(alloc_size));
*res = alloc_size;
contention[n] = res;
}
@@ -146,7 +143,7 @@ void test_tasks(size_t num_tasks, size_t count, size_t size)
if (use_malloc)
free(contention[n]);
else
a.dealloc(contention[n], *contention[n]);
snmalloc::dealloc(contention[n], *contention[n]);
}
}
@@ -154,7 +151,7 @@ void test_tasks(size_t num_tasks, size_t count, size_t size)
}
#ifndef NDEBUG
snmalloc::debug_check_empty<snmalloc::Alloc::Config>();
snmalloc::debug_check_empty();
#endif
};

View File

@@ -13,7 +13,7 @@ namespace test
// Pre allocate all the objects
size_t* objects[count];
NOINLINE void setup(xoroshiro::p128r64& r, Alloc& alloc)
NOINLINE void setup(xoroshiro::p128r64& r)
{
for (size_t i = 0; i < count; i++)
{
@@ -31,28 +31,27 @@ namespace test
if (size < 16)
size = 16;
// store object
objects[i] = (size_t*)alloc.alloc(size);
objects[i] = (size_t*)snmalloc::alloc(size);
if (objects[i] == nullptr)
abort();
// Store allocators size for this object
*objects[i] = alloc.alloc_size(objects[i]);
*objects[i] = snmalloc::alloc_size(objects[i]);
}
}
NOINLINE void teardown(Alloc& alloc)
NOINLINE void teardown()
{
// Deallocate everything
for (size_t i = 0; i < count; i++)
{
alloc.dealloc(objects[i]);
snmalloc::dealloc(objects[i]);
}
snmalloc::debug_check_empty<snmalloc::Alloc::Config>();
snmalloc::debug_check_empty();
}
void test_external_pointer(xoroshiro::p128r64& r)
{
auto& alloc = ThreadAlloc::get();
// This is very slow on Windows at the moment. Until this is fixed, help
// CI terminate.
#if defined(NDEBUG) && !defined(_MSC_VER)
@@ -66,7 +65,7 @@ namespace test
static constexpr size_t iterations = 100000;
# endif
#endif
setup(r, alloc);
setup(r);
{
MeasureTime m;
@@ -76,12 +75,12 @@ namespace test
size_t rand = (size_t)r.next();
size_t oid = rand & (((size_t)1 << count_log) - 1);
size_t* external_ptr = objects[oid];
if (!alloc.is_snmalloc_owned(external_ptr))
if (!snmalloc::is_owned(external_ptr))
continue;
size_t size = *external_ptr;
size_t offset = (size >> 4) * (rand & 15);
void* interior_ptr = pointer_offset(external_ptr, offset);
void* calced_external = alloc.external_pointer(interior_ptr);
void* calced_external = snmalloc::external_pointer(interior_ptr);
if (calced_external != external_ptr)
{
abort();
@@ -89,13 +88,12 @@ namespace test
}
}
teardown(alloc);
teardown();
}
}
int main(int, char**)
{
#ifndef SNMALLOC_PASS_THROUGH // Depends on snmalloc specific features
setup();
xoroshiro::p128r64 r;
@@ -105,5 +103,4 @@ int main(int, char**)
for (size_t n = 0; n < nn; n++)
test::test_external_pointer(r);
return 0;
#endif
}

View File

@@ -20,7 +20,7 @@ class Queue
Node* new_node(size_t size)
{
auto result = (Node*)ThreadAlloc::get().alloc(size);
auto result = (Node*)snmalloc::alloc(size);
result->next = nullptr;
return result;
}
@@ -44,7 +44,7 @@ public:
return false;
Node* next = head->next;
ThreadAlloc::get().dealloc(head);
snmalloc::dealloc(head);
head = next;
return true;
}

View File

@@ -35,7 +35,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 = snmalloc::alloc(rsize);
s.dst = static_cast<unsigned char*>(s.object) + offset;
// Bring into cache the destination of the copy.
memset(s.dst, 0xFF, size);
@@ -47,7 +47,7 @@ void unshape()
{
for (auto& s : allocs)
{
ThreadAlloc::get().dealloc(s.object);
snmalloc::dealloc(s.object);
}
allocs.clear();
}
@@ -68,7 +68,7 @@ void test(
Memcpy mc,
std::vector<std::pair<size_t, std::chrono::nanoseconds>>& stats)
{
auto src = ThreadAlloc::get().alloc(size);
auto src = snmalloc::alloc(size);
shape(size);
for (size_t i = 0; i < 10; i++)
{
@@ -77,7 +77,7 @@ void test(
auto time = m.get_time();
stats.push_back({size, time});
}
ThreadAlloc::get().dealloc(src);
snmalloc::dealloc(src);
unshape();
}
@@ -108,7 +108,6 @@ void memcpy_platform_checked(void* dst, const void* src, size_t size)
int main(int argc, char** argv)
{
opt::Opt opt(argc, argv);
#ifndef SNMALLOC_PASS_THROUGH
bool full_test = opt.has("--full_test");
// size_t size = 0;
@@ -182,8 +181,5 @@ int main(int argc, char** argv)
stats_platform.clear();
stats_platform_checked.clear();
}
#else
snmalloc::UNUSED(opt);
#endif
return 0;
}

View File

@@ -36,46 +36,6 @@ void chatty(const char* p, ...)
}
}
/*
* Interpret SNMALLOC_PASS_THROUGH ourselves to make this a bit more fair of a
* comparison, since relying of snmalloc itself to do the passing through
* results in it imposing its own idea of alignment onto the underlying
* allocator, which might result in it taking less optimized paths.
*/
#ifdef SNMALLOC_PASS_THROUGH
struct MyAlloc
{
MyAlloc() {}
void* alloc(size_t sz)
{
return malloc(sz);
}
void dealloc(void* p)
{
free(p);
}
};
#else
struct MyAlloc
{
snmalloc::Alloc& a;
MyAlloc() : a(ThreadAlloc::get()) {}
void* alloc(size_t sz)
{
return a.alloc(sz);
}
void dealloc(void* p)
{
a.dealloc(p);
}
};
#endif
/*
* FreeListMPSCQ make for convenient MPSC queues, so we use those for sending
* "messages". Each consumer or proxy has its own (source) queue.
@@ -106,7 +66,6 @@ freelist::HeadPtr domesticate_nop(freelist::QueuePtr p)
void consumer(const struct params* param, size_t qix)
{
MyAlloc a{};
auto& myq = param->msgqueue[qix];
chatty("Cl %zu q is %p\n", qix, &myq);
@@ -118,13 +77,11 @@ void consumer(const struct params* param, size_t qix)
if (myq.can_dequeue(domesticate_nop, domesticate_nop))
{
myq.dequeue(
domesticate_nop,
domesticate_nop,
[qix, &a, &reap](freelist::HeadPtr o) {
domesticate_nop, domesticate_nop, [qix, &reap](freelist::HeadPtr o) {
UNUSED(qix);
auto p = o.as_void().unsafe_ptr();
chatty("Cl %zu free %p\n", qix, p);
a.dealloc(p);
snmalloc::dealloc(p);
reap++;
return true;
});
@@ -145,7 +102,7 @@ void consumer(const struct params* param, size_t qix)
producers_live || (queue_gate > param->N_CONSUMER));
chatty("Cl %zu fini\n", qix);
a.dealloc(myq.destroy().unsafe_ptr());
snmalloc::dealloc(myq.destroy().unsafe_ptr());
}
void proxy(const struct params* param, size_t qix)
@@ -178,13 +135,12 @@ void proxy(const struct params* param, size_t qix)
chatty("Px %zu fini\n", qix);
MyAlloc().dealloc(myq.destroy().unsafe_ptr());
snmalloc::dealloc(myq.destroy().unsafe_ptr());
queue_gate--;
}
void producer(const struct params* param, size_t pix)
{
MyAlloc a{};
static constexpr size_t msgsizes[] = {48, 64, 96, 128};
static constexpr size_t nmsgsizes = sizeof(msgsizes) / sizeof(msgsizes[0]);
@@ -206,7 +162,7 @@ void producer(const struct params* param, size_t pix)
/* Allocate batch and form list */
for (size_t msgix = 0; msgix < nmsg; msgix++)
{
auto msg = a.alloc(msgsize);
auto msg = snmalloc::alloc(msgsize);
chatty("Pd %zu make %p\n", pix, msg);
auto msgc = capptr::Alloc<void>::unsafe_from(msg)

View File

@@ -8,8 +8,6 @@ using namespace snmalloc;
template<ZeroMem zero_mem>
void test_alloc_dealloc(size_t count, size_t size, bool write)
{
auto& alloc = ThreadAlloc::get();
{
MeasureTime m;
m << "Count: " << std::setw(6) << count << ", Size: " << std::setw(6)
@@ -20,7 +18,7 @@ void test_alloc_dealloc(size_t count, size_t size, bool write)
// alloc 1.5x objects
for (size_t i = 0; i < ((count * 3) / 2); i++)
{
void* p = alloc.alloc<zero_mem>(size);
void* p = snmalloc::alloc<zero_mem>(size);
SNMALLOC_CHECK(set.find(p) == set.end());
if (write)
@@ -36,13 +34,13 @@ void test_alloc_dealloc(size_t count, size_t size, bool write)
void* p = *it;
set.erase(it);
SNMALLOC_CHECK(set.find(p) == set.end());
alloc.dealloc(p, size);
snmalloc::dealloc(p, size);
}
// alloc 1x objects
for (size_t i = 0; i < count; i++)
{
void* p = alloc.alloc<zero_mem>(size);
void* p = snmalloc::alloc<zero_mem>(size);
SNMALLOC_CHECK(set.find(p) == set.end());
if (write)
@@ -55,12 +53,12 @@ void test_alloc_dealloc(size_t count, size_t size, bool write)
while (!set.empty())
{
auto it = set.begin();
alloc.dealloc(*it, size);
snmalloc::dealloc(*it, size);
set.erase(it);
}
}
snmalloc::debug_check_empty<snmalloc::Alloc::Config>();
snmalloc::debug_check_empty();
}
int main(int, char**)

View File

@@ -77,8 +77,7 @@ int main()
ParallelTest test(
[](size_t id) {
auto start = Aal::tick();
auto& alloc = snmalloc::ThreadAlloc::get();
alloc.dealloc(alloc.alloc(1));
snmalloc::dealloc(snmalloc::alloc(1));
auto end = Aal::tick();
counters[id] = end - start;
},