diff --git a/src/mem/pool.h b/src/mem/pool.h index 76950c2..34f58e9 100644 --- a/src/mem/pool.h +++ b/src/mem/pool.h @@ -39,19 +39,53 @@ namespace snmalloc /** * Class used to instantiate a global non-allocator PoolState. */ - template + template class SingletonPoolState { static inline PoolState state; + static thread_local inline bool initialized; + + /** + * SFINAE helper. Matched only if `T` implements `ensure_init`. Calls it + * if it exists. + */ + SNMALLOC_FAST_PATH static auto call_ensure_init(SharedStateHandle*, int) + -> decltype(SharedStateHandle::ensure_init()) + { + SharedStateHandle::ensure_init(); + } + + /** + * SFINAE helper. Matched only if `T` does not implement `ensure_init`. + * Does nothing if called. + */ + SNMALLOC_FAST_PATH static auto call_ensure_init(SharedStateHandle*, long) {} + + /** + * Call `SharedStateHandle::ensure_init()` if it is implemented, do nothing + * otherwise. + */ + SNMALLOC_FAST_PATH static void ensure_init() + { + call_ensure_init(nullptr, 0); + } public: - static PoolState& pool() + SNMALLOC_FAST_PATH static PoolState& pool() { + if (unlikely(!initialized)) + { + ensure_init(); + initialized = true; + } return state; } }; - template& get_state()> + template< + typename T, + typename SharedStateHandle, + PoolState& get_state() = SingletonPoolState::pool> class Pool { public: diff --git a/src/test/func/pool/pool.cc b/src/test/func/pool/pool.cc new file mode 100644 index 0000000..550dd4e --- /dev/null +++ b/src/test/func/pool/pool.cc @@ -0,0 +1,117 @@ +#include +#include +#include +#include + +using namespace snmalloc; + +struct PoolAEntry : Pooled +{ + size_t field; + + PoolAEntry() : field(1){}; +}; + +using PoolA = Pool; + +struct PoolBEntry : Pooled +{ + size_t field; + + PoolBEntry() : field(0){}; + PoolBEntry(size_t f) : field(f){}; +}; + +using PoolB = Pool; + +void test_alloc() +{ + auto ptr = PoolA::acquire(); + SNMALLOC_CHECK(ptr != nullptr); + // Pool allocations should not be visible to debug_check_empty. + snmalloc::debug_check_empty(); +} + +void test_constructor() +{ + auto ptr1 = PoolA::acquire(); + SNMALLOC_CHECK(ptr1 != nullptr); + SNMALLOC_CHECK(ptr1->field == 1); + + auto ptr2 = PoolB::acquire(); + SNMALLOC_CHECK(ptr2 != nullptr); + SNMALLOC_CHECK(ptr2->field == 0); + + auto ptr3 = PoolB::acquire(1); + SNMALLOC_CHECK(ptr3 != nullptr); + SNMALLOC_CHECK(ptr3->field == 1); +} + +void test_alloc_many() +{ + constexpr size_t count = 16'000'000 / MIN_CHUNK_SIZE; + + std::unordered_set allocated; + + for (size_t i = 0; i < count; ++i) + { + auto ptr = PoolA::acquire(); + SNMALLOC_CHECK(ptr != nullptr); + allocated.insert(ptr); + } + + for (auto ptr : allocated) + { + PoolA::release(ptr); + } +} + +void test_alloc_dealloc() +{ + auto ptr = PoolA::acquire(); + SNMALLOC_CHECK(ptr != nullptr); + PoolA::release(ptr); +} + +void test_double_alloc() +{ + auto ptr1 = PoolA::acquire(); + SNMALLOC_CHECK(ptr1 != nullptr); + auto ptr2 = PoolA::acquire(); + SNMALLOC_CHECK(ptr2 != nullptr); + SNMALLOC_CHECK(ptr1 != ptr2); + PoolA::release(ptr2); + auto ptr3 = PoolA::acquire(); + SNMALLOC_CHECK(ptr2 == ptr3); +} + +void test_different_alloc() +{ + auto ptr1 = PoolA::acquire(); + SNMALLOC_CHECK(ptr1 != nullptr); + PoolA::release(ptr1); + auto ptr2 = PoolB::acquire(); + SNMALLOC_CHECK(ptr2 != nullptr); + SNMALLOC_CHECK(static_cast(ptr1) != static_cast(ptr2)); +} + +int main(int argc, char** argv) +{ + setup(); +#ifdef USE_SYSTEMATIC_TESTING + opt::Opt opt(argc, argv); + size_t seed = opt.is("--seed", 0); + Virtual::systematic_bump_ptr() += seed << 17; +#else + UNUSED(argc); + UNUSED(argv); +#endif + + test_alloc(); + test_constructor(); + test_alloc_many(); + test_alloc_dealloc(); + test_double_alloc(); + test_different_alloc(); + return 0; +}