blob: e30f0726907fca4bb2a007f6067b30f79b57cada [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <memory>
#include <utility>
#include <fbl/alloc_checker.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include <fbl/slab_allocator.h>
#include <zxtest/zxtest.h>
namespace {
enum class ConstructType {
DEFAULT,
LVALUE_REF,
RVALUE_REF,
L_THEN_R_REF,
};
// Test objects.
class TestBase {
public:
// Various constructor forms to exercise SlabAllocator::New
TestBase() : ctype_(ConstructType::DEFAULT) { ++allocated_obj_count_; }
explicit TestBase(const size_t&) : ctype_(ConstructType::LVALUE_REF) { ++allocated_obj_count_; }
explicit TestBase(size_t&&) : ctype_(ConstructType::RVALUE_REF) { ++allocated_obj_count_; }
explicit TestBase(const size_t&, size_t&&) : ctype_(ConstructType::L_THEN_R_REF) {
++allocated_obj_count_;
}
virtual ~TestBase() { --allocated_obj_count_; }
ConstructType ctype() const { return ctype_; }
static void Reset() { allocated_obj_count_ = 0; }
static size_t allocated_obj_count() { return allocated_obj_count_; }
const uint8_t* payload() const { return payload_; }
private:
const ConstructType ctype_;
uint8_t payload_[13]; // 13 bytes, just to make the size/alignment strange
static size_t allocated_obj_count_;
};
// Static storage.
size_t TestBase::allocated_obj_count_;
template <typename SATraits, typename = void>
struct ReleaseHelper;
template <typename SATraits>
struct ReleaseHelper<SATraits,
typename std::enable_if<(SATraits::PtrTraits::IsManaged == false) &&
(SATraits::AllocatorFlavor ==
fbl::SlabAllocatorFlavor::INSTANCED)>::type> {
static void ReleasePtr(fbl::SlabAllocator<SATraits>& allocator, typename SATraits::PtrType& ptr) {
// Instanced slab allocators should static_assert if you attempt to
// expand their Delete method.
#if TEST_WILL_NOT_COMPILE || 0
allocator.Delete(ptr);
#else
delete ptr;
#endif
}
};
template <typename SATraits>
struct ReleaseHelper<SATraits,
typename std::enable_if<(SATraits::PtrTraits::IsManaged == false) &&
(SATraits::AllocatorFlavor ==
fbl::SlabAllocatorFlavor::MANUAL_DELETE)>::type> {
static void ReleasePtr(fbl::SlabAllocator<SATraits>& allocator, typename SATraits::PtrType& ptr) {
// SlabAllocated<> objects which come from MANUAL_DELETE flavors of slab
// allocators should have their delete operator protected in order to
// prevent someone from calling delete on the object.
#if TEST_WILL_NOT_COMPILE || 0
delete ptr;
#else
allocator.Delete(ptr);
#endif
}
};
template <typename SATraits>
struct ReleaseHelper<SATraits, typename std::enable_if<(SATraits::PtrTraits::IsManaged == true) &&
(SATraits::AllocatorFlavor !=
fbl::SlabAllocatorFlavor::STATIC)>::type> {
static void ReleasePtr(fbl::SlabAllocator<SATraits>&, typename SATraits::PtrType& ptr) {
ptr = nullptr;
}
};
template <typename SATraits>
struct ReleaseHelper<SATraits, typename std::enable_if<(SATraits::PtrTraits::IsManaged == false) &&
(SATraits::AllocatorFlavor ==
fbl::SlabAllocatorFlavor::STATIC)>::type> {
static void ReleasePtr(typename SATraits::PtrType& ptr) { delete ptr; }
};
template <typename SATraits>
struct ReleaseHelper<SATraits, typename std::enable_if<(SATraits::PtrTraits::IsManaged == true) &&
(SATraits::AllocatorFlavor ==
fbl::SlabAllocatorFlavor::STATIC)>::type> {
static void ReleasePtr(typename SATraits::PtrType& ptr) { ptr = nullptr; }
};
// Traits which define the various test flavors.
template <typename LockType,
fbl::SlabAllocatorFlavor AllocatorFlavor = fbl::SlabAllocatorFlavor::INSTANCED,
fbl::SlabAllocatorOptions Options = fbl::SlabAllocatorOptions::None>
struct UnmanagedTestTraits {
class ObjType;
using PtrType = ObjType*;
using AllocTraits = fbl::SlabAllocatorTraits<PtrType, 1024, LockType, AllocatorFlavor, Options>;
using AllocatorType = fbl::SlabAllocator<AllocTraits>;
using RefList = fbl::DoublyLinkedList<PtrType>;
class ObjType : public TestBase,
public fbl::SlabAllocated<AllocTraits>,
public fbl::DoublyLinkedListable<PtrType> {
public:
ObjType() : TestBase() {}
explicit ObjType(const size_t& val) : TestBase(val) {}
explicit ObjType(size_t&& val) : TestBase(std::move(val)) {}
explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, std::move(b)) {}
};
static constexpr size_t MaxSlabs = 4;
static constexpr bool IsManaged = false;
static constexpr size_t MaxAllocs(size_t slabs) { return AllocatorType::AllocsPerSlab * slabs; }
};
template <typename LockType, fbl::SlabAllocatorOptions Options = fbl::SlabAllocatorOptions::None>
struct UniquePtrTestTraits {
class ObjType;
using PtrType = std::unique_ptr<ObjType>;
using AllocTraits = fbl::SlabAllocatorTraits<PtrType, 1024, LockType,
fbl::SlabAllocatorFlavor::INSTANCED, Options>;
using AllocatorType = fbl::SlabAllocator<AllocTraits>;
using RefList = fbl::DoublyLinkedList<PtrType>;
class ObjType : public TestBase,
public fbl::SlabAllocated<AllocTraits>,
public fbl::DoublyLinkedListable<PtrType> {
public:
ObjType() : TestBase() {}
explicit ObjType(const size_t& val) : TestBase(val) {}
explicit ObjType(size_t&& val) : TestBase(std::move(val)) {}
explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, std::move(b)) {}
};
static constexpr size_t MaxSlabs = 4;
static constexpr bool IsManaged = true;
static constexpr size_t MaxAllocs(size_t slabs) { return AllocatorType::AllocsPerSlab * slabs; }
};
template <typename LockType, fbl::SlabAllocatorOptions Options = fbl::SlabAllocatorOptions::None>
struct RefPtrTestTraits {
class ObjType;
using PtrType = fbl::RefPtr<ObjType>;
using AllocTraits = fbl::SlabAllocatorTraits<PtrType, 1024, LockType,
fbl::SlabAllocatorFlavor::INSTANCED, Options>;
using AllocatorType = fbl::SlabAllocator<AllocTraits>;
using RefList = fbl::DoublyLinkedList<PtrType>;
class ObjType : public TestBase,
public fbl::RefCounted<ObjType>,
public fbl::SlabAllocated<AllocTraits>,
public fbl::DoublyLinkedListable<PtrType> {
public:
ObjType() : TestBase() {}
explicit ObjType(const size_t& val) : TestBase(val) {}
explicit ObjType(size_t&& val) : TestBase(std::move(val)) {}
explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, std::move(b)) {}
};
static constexpr size_t MaxSlabs = 4;
static constexpr bool IsManaged = true;
static constexpr size_t MaxAllocs(size_t slabs) { return AllocatorType::AllocsPerSlab * slabs; }
};
template <typename, bool>
struct ObjCounterHelper;
template <typename SA>
struct ObjCounterHelper<SA, true> {
static bool CheckObjCount(const SA& allocator, size_t expected) {
return (allocator.obj_count() == expected);
}
static bool CheckMaxObjCount(const SA& allocator, size_t expected) {
return (allocator.max_obj_count() == expected);
}
static void ResetMaxObjCount(SA* allocator) { allocator->ResetMaxObjCount(); }
static bool StaticCheckObjCount(size_t expected) { return (SA::obj_count() == expected); }
static bool StaticCheckMaxObjCount(size_t expected) { return (SA::max_obj_count() == expected); }
static void StaticResetMaxObjCount() { SA::ResetMaxObjCount(); }
};
template <typename SA>
struct ObjCounterHelper<SA, false> {
static bool CheckObjCount(const SA&, size_t) { return true; }
static bool CheckMaxObjCount(const SA&, size_t) { return true; }
static void ResetMaxObjCount(SA*) {}
static bool StaticCheckObjCount(size_t) { return true; }
static bool StaticCheckMaxObjCount(size_t) { return true; }
static void StaticResetMaxObjCount() {}
};
template <typename Traits>
void do_slab_test(typename Traits::AllocatorType& allocator, size_t test_allocs) {
const size_t MAX_ALLOCS = Traits::MaxAllocs(allocator.max_slabs());
typename Traits::RefList ref_list;
const bool ENB_OBJ_COUNT =
Traits::AllocTraits::Options & fbl::SlabAllocatorOptions::EnableObjectCount;
using AllocatorType = typename Traits::AllocatorType;
ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::ResetMaxObjCount(&allocator);
bool res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount(allocator, 0);
EXPECT_TRUE(res);
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount(allocator, 0);
EXPECT_TRUE(res);
// Allocate up to the test limit.
for (size_t i = 0; i < test_allocs; ++i) {
typename Traits::PtrType ptr;
EXPECT_EQ(std::min(i, MAX_ALLOCS), TestBase::allocated_obj_count());
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount(
allocator, TestBase::allocated_obj_count());
EXPECT_TRUE(res);
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount(
allocator, TestBase::allocated_obj_count());
EXPECT_TRUE(res);
// Allocate the object; exercise the various constructors
switch (i % 4) {
case 0:
ptr = allocator.New();
break;
case 1:
ptr = allocator.New(i);
break;
case 2:
ptr = allocator.New(std::move(i));
break;
case 3:
ptr = allocator.New(i, std::move(i));
break;
}
if (i < MAX_ALLOCS) {
ASSERT_NOT_NULL(ptr, "Allocation failed when it should not have!");
ref_list.push_front(std::move(ptr));
} else {
ASSERT_NULL(ptr, "Allocation succeeded when it should not have!");
}
EXPECT_EQ(std::min(i + 1, MAX_ALLOCS), TestBase::allocated_obj_count());
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount(
allocator, TestBase::allocated_obj_count());
EXPECT_TRUE(res);
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount(
allocator, TestBase::allocated_obj_count());
EXPECT_TRUE(res);
}
// Now remove and de-allocate.
size_t max_obj_count = TestBase::allocated_obj_count();
size_t i;
for (i = 0; !ref_list.is_empty(); ++i) {
auto ptr = ref_list.pop_back();
ASSERT_NOT_NULL(ptr, "nullptr in ref list! This should be impossible.");
EXPECT_EQ(std::min(test_allocs, MAX_ALLOCS) - i, TestBase::allocated_obj_count());
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount(
allocator, TestBase::allocated_obj_count());
EXPECT_TRUE(res);
res =
ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount(allocator, max_obj_count);
EXPECT_TRUE(res);
switch (i % 4) {
case 0:
EXPECT_EQ(ConstructType::DEFAULT, ptr->ctype());
break;
case 1:
EXPECT_EQ(ConstructType::LVALUE_REF, ptr->ctype());
break;
case 2:
EXPECT_EQ(ConstructType::RVALUE_REF, ptr->ctype());
break;
case 3:
EXPECT_EQ(ConstructType::L_THEN_R_REF, ptr->ctype());
break;
}
// Release the reference (how this gets done depends on allocator flavor and pointer type)
ReleaseHelper<typename Traits::AllocTraits>::ReleasePtr(allocator, ptr);
if (i % 2 == 1) {
ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::ResetMaxObjCount(&allocator);
max_obj_count = TestBase::allocated_obj_count();
}
res =
ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount(allocator, max_obj_count);
EXPECT_TRUE(res);
}
EXPECT_EQ(std::min(test_allocs, MAX_ALLOCS), i);
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount(allocator, 0);
EXPECT_TRUE(res);
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount(allocator, i % 2);
EXPECT_TRUE(res);
ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::ResetMaxObjCount(&allocator);
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount(allocator, 0);
EXPECT_TRUE(res);
#if TEST_WILL_NOT_COMPILE || 0
allocator.obj_count();
#endif
#if TEST_WILL_NOT_COMPILE || 0
allocator.max_obj_count();
#endif
#if TEST_WILL_NOT_COMPILE || 0
allocator.ResetMaxObjCount();
#endif
}
template <typename Traits, size_t SlabCount = Traits::MaxSlabs>
void slab_test() {
typename Traits::AllocatorType allocator(SlabCount);
TestBase::Reset();
{
auto fn = [&allocator]() { do_slab_test<Traits>(allocator, 1); };
ASSERT_NO_FAILURES(fn(), "Single allocator test failed");
}
{
auto fn = [&allocator]() { do_slab_test<Traits>(allocator, Traits::MaxAllocs(SlabCount) / 2); };
ASSERT_NO_FAILURES(fn(), "1/2 capacity allocator test failed");
}
{
auto fn = [&allocator]() { do_slab_test<Traits>(allocator, Traits::MaxAllocs(SlabCount) + 4); };
ASSERT_NO_FAILURES(fn(), "Over-capacity allocator test failed");
}
}
template <typename LockType, fbl::SlabAllocatorOptions Options = fbl::SlabAllocatorOptions::None>
struct StaticUnmanagedTestTraits {
class ObjType;
using PtrType = ObjType*;
using AllocTraits = fbl::StaticSlabAllocatorTraits<PtrType, 1024, LockType, Options>;
using AllocatorType = fbl::SlabAllocator<AllocTraits>;
using RefList = fbl::DoublyLinkedList<PtrType>;
class ObjType : public TestBase,
public fbl::SlabAllocated<AllocTraits>,
public fbl::DoublyLinkedListable<PtrType> {
public:
ObjType() : TestBase() {}
explicit ObjType(const size_t& val) : TestBase(val) {}
explicit ObjType(size_t&& val) : TestBase(std::move(val)) {}
explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, std::move(b)) {}
};
static size_t MaxAllocs() { return AllocatorType::AllocsPerSlab * AllocatorType::max_slabs(); }
static constexpr size_t MaxSlabs = 4;
static constexpr bool IsManaged = false;
};
template <typename LockType>
using StaticCountedUnmanagedTestTraits =
StaticUnmanagedTestTraits<LockType, fbl::SlabAllocatorOptions::EnableObjectCount>;
template <typename LockType, fbl::SlabAllocatorOptions Options = fbl::SlabAllocatorOptions::None>
struct StaticUniquePtrTestTraits {
class ObjType;
using PtrType = std::unique_ptr<ObjType>;
using AllocTraits = fbl::StaticSlabAllocatorTraits<PtrType, 1024, LockType, Options>;
using AllocatorType = fbl::SlabAllocator<AllocTraits>;
using RefList = fbl::DoublyLinkedList<PtrType>;
class ObjType : public TestBase,
public fbl::SlabAllocated<AllocTraits>,
public fbl::DoublyLinkedListable<PtrType> {
public:
ObjType() : TestBase() {}
explicit ObjType(const size_t& val) : TestBase(val) {}
explicit ObjType(size_t&& val) : TestBase(std::move(val)) {}
explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, std::move(b)) {}
};
static size_t MaxAllocs() { return AllocatorType::AllocsPerSlab * AllocatorType::max_slabs(); }
static constexpr size_t MaxSlabs = 4;
static constexpr bool IsManaged = false;
};
template <typename LockType>
using StaticCountedUniquePtrTestTraits =
StaticUniquePtrTestTraits<LockType, fbl::SlabAllocatorOptions::EnableObjectCount>;
template <typename LockType, fbl::SlabAllocatorOptions Options = fbl::SlabAllocatorOptions::None>
struct StaticRefPtrTestTraits {
class ObjType;
using PtrType = fbl::RefPtr<ObjType>;
using AllocTraits = fbl::StaticSlabAllocatorTraits<PtrType, 1024, LockType, Options>;
using AllocatorType = fbl::SlabAllocator<AllocTraits>;
using RefList = fbl::DoublyLinkedList<PtrType>;
class ObjType : public TestBase,
public fbl::SlabAllocated<AllocTraits>,
public fbl::RefCounted<ObjType>,
public fbl::DoublyLinkedListable<PtrType> {
public:
ObjType() : TestBase() {}
explicit ObjType(const size_t& val) : TestBase(val) {}
explicit ObjType(size_t&& val) : TestBase(std::move(val)) {}
explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, std::move(b)) {}
};
static constexpr size_t MaxSlabs = 4;
static constexpr bool IsManaged = false;
static size_t MaxAllocs() { return AllocatorType::AllocsPerSlab * AllocatorType::max_slabs(); }
};
template <typename LockType>
using StaticCountedRefPtrTestTraits =
StaticRefPtrTestTraits<LockType, fbl::SlabAllocatorOptions::EnableObjectCount>;
template <typename Traits>
void do_static_slab_test(size_t test_allocs) {
const bool ENB_OBJ_COUNT =
Traits::AllocTraits::Options & fbl::SlabAllocatorOptions::EnableObjectCount;
using AllocatorType = typename Traits::AllocatorType;
const size_t MAX_ALLOCS = Traits::MaxAllocs();
typename Traits::RefList ref_list;
ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticResetMaxObjCount();
bool res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount(0);
EXPECT_TRUE(res);
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(0);
EXPECT_TRUE(res);
// Allocate up to the test limit.
for (size_t i = 0; i < test_allocs; ++i) {
typename Traits::PtrType ptr;
EXPECT_EQ(std::min(i, MAX_ALLOCS), TestBase::allocated_obj_count());
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount(
TestBase::allocated_obj_count());
EXPECT_TRUE(res);
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(
TestBase::allocated_obj_count());
EXPECT_TRUE(res);
// Allocate the object; exercise the various constructors
switch (i % 4) {
case 0:
ptr = AllocatorType::New();
break;
case 1:
ptr = AllocatorType::New(i);
break;
case 2:
ptr = AllocatorType::New(std::move(i));
break;
case 3:
ptr = AllocatorType::New(i, std::move(i));
break;
}
if (i < MAX_ALLOCS) {
ASSERT_NOT_NULL(ptr, "Allocation failed when it should not have!");
ref_list.push_front(std::move(ptr));
} else {
ASSERT_NULL(ptr, "Allocation succeeded when it should not have!");
}
EXPECT_EQ(std::min(i + 1, MAX_ALLOCS), TestBase::allocated_obj_count());
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount(
TestBase::allocated_obj_count());
EXPECT_TRUE(res);
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(
TestBase::allocated_obj_count());
EXPECT_TRUE(res);
}
// Now remove and de-allocate.
size_t max_obj_count = TestBase::allocated_obj_count();
size_t i;
for (i = 0; !ref_list.is_empty(); ++i) {
auto ptr = ref_list.pop_back();
ASSERT_NOT_NULL(ptr, "nullptr in ref list! This should be impossible.");
EXPECT_EQ(std::min(test_allocs, MAX_ALLOCS) - i, TestBase::allocated_obj_count());
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount(
TestBase::allocated_obj_count());
EXPECT_TRUE(res);
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(max_obj_count);
EXPECT_TRUE(res);
switch (i % 4) {
case 0:
EXPECT_EQ(ConstructType::DEFAULT, ptr->ctype());
break;
case 1:
EXPECT_EQ(ConstructType::LVALUE_REF, ptr->ctype());
break;
case 2:
EXPECT_EQ(ConstructType::RVALUE_REF, ptr->ctype());
break;
case 3:
EXPECT_EQ(ConstructType::L_THEN_R_REF, ptr->ctype());
break;
}
// Release the reference (how this gets done depends on allocator flavor and pointer type)
ReleaseHelper<typename Traits::AllocTraits>::ReleasePtr(ptr);
if (i % 2 == 1) {
ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticResetMaxObjCount();
max_obj_count = TestBase::allocated_obj_count();
}
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(max_obj_count);
EXPECT_TRUE(res);
}
EXPECT_EQ(std::min(test_allocs, MAX_ALLOCS), i);
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount(0);
EXPECT_TRUE(res);
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(i % 2);
EXPECT_TRUE(res);
ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticResetMaxObjCount();
res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(0);
EXPECT_TRUE(res);
#if TEST_WILL_NOT_COMPILE || 0
AllocatorType::obj_count();
#endif
#if TEST_WILL_NOT_COMPILE || 0
AllocatorType::max_obj_count();
#endif
#if TEST_WILL_NOT_COMPILE || 0
AllocatorType::ResetMaxObjCount();
#endif
}
template <typename Traits>
void static_slab_test() {
TestBase::Reset();
ASSERT_NO_FAILURES(do_static_slab_test<Traits>(1), "Single allocator test failed");
ASSERT_NO_FAILURES(do_static_slab_test<Traits>(Traits::MaxAllocs() / 2),
"1/2 capacity allocator test failed");
ASSERT_NO_FAILURES(do_static_slab_test<Traits>(Traits::MaxAllocs() + 4),
"Over-capacity allocator test failed");
}
} // namespace
using MutexLock = ::fbl::Mutex;
using NullLock = ::fbl::NullLock;
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticUnmanagedTestTraits<MutexLock>::AllocTraits, 1);
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticUniquePtrTestTraits<MutexLock>::AllocTraits, 1);
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticRefPtrTestTraits<MutexLock>::AllocTraits, 1);
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticUnmanagedTestTraits<NullLock>::AllocTraits, 1);
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticUniquePtrTestTraits<NullLock>::AllocTraits, 1);
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticRefPtrTestTraits<NullLock>::AllocTraits, 1);
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedUnmanagedTestTraits<MutexLock>::AllocTraits, 1);
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedUniquePtrTestTraits<MutexLock>::AllocTraits, 1);
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedRefPtrTestTraits<MutexLock>::AllocTraits, 1);
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedUnmanagedTestTraits<NullLock>::AllocTraits, 1);
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedUniquePtrTestTraits<NullLock>::AllocTraits, 1);
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedRefPtrTestTraits<NullLock>::AllocTraits, 1);
#define MAKE_TEST(_name, ...) \
TEST(SlabAllocatorTest, _name) { \
auto fn = __VA_ARGS__; \
ASSERT_NO_FAILURES(fn()); \
}
MAKE_TEST(UnmanagedSingleSlabMutex, slab_test<UnmanagedTestTraits<MutexLock>, 1>)
MAKE_TEST(UnmanagedMultiSlabMutex, slab_test<UnmanagedTestTraits<MutexLock>>)
MAKE_TEST(UniquePtrSingleSlabMutex, slab_test<UniquePtrTestTraits<MutexLock>, 1>)
MAKE_TEST(UniquePtrMultiSlabMutex, slab_test<UniquePtrTestTraits<MutexLock>>)
MAKE_TEST(RefPtrSingleSlabMutex, slab_test<RefPtrTestTraits<MutexLock>, 1>)
MAKE_TEST(RefPtrMultiSlabMutex, slab_test<RefPtrTestTraits<MutexLock>>)
MAKE_TEST(UnmanagedSingleSlabUnlock, slab_test<UnmanagedTestTraits<NullLock>, 1>)
MAKE_TEST(UnmanagedMultiSlabUnlock, slab_test<UnmanagedTestTraits<NullLock>>)
MAKE_TEST(UniquePtrSingleSlabUnlock, slab_test<UniquePtrTestTraits<NullLock>, 1>)
MAKE_TEST(UniquePtrMultiSlabUnlock, slab_test<UniquePtrTestTraits<NullLock>>)
MAKE_TEST(RefPtrSingleSlabUnlock, slab_test<RefPtrTestTraits<NullLock>, 1>)
MAKE_TEST(RefPtrMultiSlabUnlock, slab_test<RefPtrTestTraits<NullLock>>)
MAKE_TEST(ManualDeleteUnmanagedMutex,
slab_test<UnmanagedTestTraits<MutexLock, fbl::SlabAllocatorFlavor::MANUAL_DELETE>>)
MAKE_TEST(ManualDeleteUnmanagedUnlock,
slab_test<UnmanagedTestTraits<NullLock, fbl::SlabAllocatorFlavor::MANUAL_DELETE>>)
MAKE_TEST(StaticUnmanagedUnlock, static_slab_test<StaticUnmanagedTestTraits<NullLock>>)
MAKE_TEST(StaticUniquePtrUnlock, static_slab_test<StaticUniquePtrTestTraits<NullLock>>)
MAKE_TEST(StaticRefPtrUnlock, static_slab_test<StaticRefPtrTestTraits<NullLock>>)
MAKE_TEST(StaticUnmanagedMutex, static_slab_test<StaticUnmanagedTestTraits<MutexLock>>)
MAKE_TEST(StaticUniquePtrMutex, static_slab_test<StaticUniquePtrTestTraits<MutexLock>>)
MAKE_TEST(StaticRefPtrMutex, static_slab_test<StaticRefPtrTestTraits<MutexLock>>)
MAKE_TEST(CountedUnmanagedSingleSlabMutex,
slab_test<UnmanagedTestTraits<MutexLock, fbl::SlabAllocatorFlavor::INSTANCED,
fbl::SlabAllocatorOptions::EnableObjectCount>,
1>)
MAKE_TEST(CountedUnmanagedMultiSlabMutex,
slab_test<UnmanagedTestTraits<MutexLock, fbl::SlabAllocatorFlavor::INSTANCED,
fbl::SlabAllocatorOptions::EnableObjectCount>>)
MAKE_TEST(
CountedUniquePtrSingleSlabMutex,
slab_test<UniquePtrTestTraits<MutexLock, fbl::SlabAllocatorOptions::EnableObjectCount>, 1>)
MAKE_TEST(CountedUniquePtrMultiSlabMutex,
slab_test<UniquePtrTestTraits<MutexLock, fbl::SlabAllocatorOptions::EnableObjectCount>>)
MAKE_TEST(CountedRefPtrSingleSlabMutex,
slab_test<RefPtrTestTraits<MutexLock, fbl::SlabAllocatorOptions::EnableObjectCount>, 1>)
MAKE_TEST(CountedRefPtrMultiSlabMutex,
slab_test<RefPtrTestTraits<MutexLock, fbl::SlabAllocatorOptions::EnableObjectCount>>)
MAKE_TEST(CountedUnmanagedSingleSlabUnlock,
slab_test<UnmanagedTestTraits<NullLock, fbl::SlabAllocatorFlavor::INSTANCED,
fbl::SlabAllocatorOptions::EnableObjectCount>,
1>)
MAKE_TEST(CountedUnmanagedMultiSlabUnlock,
slab_test<UnmanagedTestTraits<NullLock, fbl::SlabAllocatorFlavor::INSTANCED,
fbl::SlabAllocatorOptions::EnableObjectCount>>)
MAKE_TEST(CountedUniquePtrSingleSlabUnlock,
slab_test<UniquePtrTestTraits<NullLock, fbl::SlabAllocatorOptions::EnableObjectCount>, 1>)
MAKE_TEST(CountedUniquePtrMultiSlabUnlock,
slab_test<UniquePtrTestTraits<NullLock, fbl::SlabAllocatorOptions::EnableObjectCount>>)
MAKE_TEST(CountedRefPtrSingleSlabUnlock,
slab_test<RefPtrTestTraits<NullLock, fbl::SlabAllocatorOptions::EnableObjectCount>, 1>)
MAKE_TEST(CountedRefPtrMultiSlabUnlock,
slab_test<RefPtrTestTraits<NullLock, fbl::SlabAllocatorOptions::EnableObjectCount>>)
MAKE_TEST(CountedManualDeleteUnmanagedMutex,
slab_test<UnmanagedTestTraits<MutexLock, fbl::SlabAllocatorFlavor::MANUAL_DELETE,
fbl::SlabAllocatorOptions::EnableObjectCount>>)
MAKE_TEST(CountedManualDeleteUnmanagedUnlock,
slab_test<UnmanagedTestTraits<NullLock, fbl::SlabAllocatorFlavor::MANUAL_DELETE,
fbl::SlabAllocatorOptions::EnableObjectCount>>)
MAKE_TEST(CountedStaticUnmanagedMutex,
static_slab_test<
StaticUnmanagedTestTraits<MutexLock, fbl::SlabAllocatorOptions::EnableObjectCount>>)
MAKE_TEST(CountedStaticUniquePtrMutex,
static_slab_test<
StaticUniquePtrTestTraits<MutexLock, fbl::SlabAllocatorOptions::EnableObjectCount>>)
MAKE_TEST(CountedStaticRefPtrMutex,
static_slab_test<
StaticRefPtrTestTraits<MutexLock, fbl::SlabAllocatorOptions::EnableObjectCount>>)
MAKE_TEST(CountedStaticUnmanagedUnlock,
static_slab_test<
StaticUnmanagedTestTraits<NullLock, fbl::SlabAllocatorOptions::EnableObjectCount>>)
MAKE_TEST(CountedStaticUniquePtrUnlock,
static_slab_test<
StaticUniquePtrTestTraits<NullLock, fbl::SlabAllocatorOptions::EnableObjectCount>>)
MAKE_TEST(CountedStaticRefPtrUnlock,
static_slab_test<
StaticRefPtrTestTraits<NullLock, fbl::SlabAllocatorOptions::EnableObjectCount>>)
#undef MAKE_TEST
TEST(SlabAllocatorTest, AllowManualDeleteOperator) {
#if TEST_WILL_NOT_COMPILE || 0
{
struct Obj;
using Traits =
fbl::StaticSlabAllocatorTraits<std::unique_ptr<Obj>, 4096, fbl::Mutex,
fbl::SlabAllocatorOptions::AllowManualDeleteOperator>;
struct Obj : public fbl::SlabAllocated<Traits> {};
[[maybe_unused]] fbl::SlabAllocator<Traits> allocator(1);
}
#endif
#if TEST_WILL_NOT_COMPILE || 0
{
struct Obj;
using Traits = fbl::UnlockedStaticSlabAllocatorTraits<
std::unique_ptr<Obj>, 4096, fbl::SlabAllocatorOptions::AllowManualDeleteOperator>;
struct Obj : public fbl::SlabAllocated<Traits> {};
[[maybe_unused]] fbl::SlabAllocator<Traits> allocator(1);
}
#endif
#if TEST_WILL_NOT_COMPILE || 0
{
struct Obj;
using Traits =
fbl::InstancedSlabAllocatorTraits<std::unique_ptr<Obj>, 4096, fbl::Mutex,
fbl::SlabAllocatorOptions::AllowManualDeleteOperator>;
struct Obj : public fbl::SlabAllocated<Traits> {};
[[maybe_unused]] fbl::SlabAllocator<Traits> allocator(1);
}
#endif
#if TEST_WILL_NOT_COMPILE || 0
{
struct Obj;
using Traits = fbl::UnlockedInstancedSlabAllocatorTraits<
std::unique_ptr<Obj>, 4096, fbl::SlabAllocatorOptions::AllowManualDeleteOperator>;
struct Obj : public fbl::SlabAllocated<Traits> {};
[[maybe_unused]] fbl::SlabAllocator<Traits> allocator(1);
}
#endif
{
struct Obj;
struct DeleteFriend;
using Traits = fbl::UnlockedManualDeleteSlabAllocatorTraits<
Obj*, 4096, fbl::SlabAllocatorOptions::AllowManualDeleteOperator>;
struct Obj : public fbl::SlabAllocated<Traits> {
friend struct DeleteFriend;
};
struct DeleteFriend {
static void Delete(Obj* obj) { delete obj; }
};
[[maybe_unused]] fbl::SlabAllocator<Traits> allocator(1);
// Heap allocate and then delete an object. This should never ASSERT
// because of the option flag that we passed. Note that we still need to be
// a friend of the object in order to even attempt to do this.
Obj* the_obj = new Obj();
ASSERT_NOT_NULL(the_obj);
DeleteFriend::Delete(the_obj);
}
}