1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <fbl/alloc_checker.h>
6 #include <fbl/intrusive_double_list.h>
7 #include <fbl/ref_counted.h>
8 #include <fbl/ref_ptr.h>
9 #include <fbl/slab_allocator.h>
10 #include <fbl/unique_ptr.h>
11 #include <unittest/unittest.h>
12 
13 #include <utility>
14 
15 namespace {
16 
17 enum class ConstructType {
18     DEFAULT,
19     LVALUE_REF,
20     RVALUE_REF,
21     L_THEN_R_REF,
22 };
23 
24 // Test objects.
25 class TestBase {
26 public:
27     // Various constructor forms to exercise SlabAllocator::New
TestBase()28     TestBase()                       : ctype_(ConstructType::DEFAULT)    { ++allocated_obj_count_; }
TestBase(const size_t &)29     explicit TestBase(const size_t&) : ctype_(ConstructType::LVALUE_REF) { ++allocated_obj_count_; }
TestBase(size_t &&)30     explicit TestBase(size_t&&)      : ctype_(ConstructType::RVALUE_REF) { ++allocated_obj_count_; }
TestBase(const size_t &,size_t &&)31     explicit TestBase(const size_t&, size_t&&)
32         : ctype_(ConstructType::L_THEN_R_REF) {
33         ++allocated_obj_count_;
34     }
35 
~TestBase()36     virtual ~TestBase() { --allocated_obj_count_; }
37 
ctype() const38     ConstructType ctype() const { return ctype_; }
39 
Reset()40     static void Reset() { allocated_obj_count_ = 0; }
allocated_obj_count()41     static size_t allocated_obj_count() { return allocated_obj_count_; }
payload() const42     const uint8_t* payload() const { return payload_; }
43 
44 private:
45     const ConstructType ctype_;
46     uint8_t             payload_[13];   // 13 bytes, just to make the size/alignment strange
47 
48     static size_t allocated_obj_count_;
49 };
50 
51 // Static storage.
52 size_t TestBase::allocated_obj_count_;
53 
54 template <typename SATraits, typename = void> struct ReleaseHelper;
55 
56 template <typename SATraits>
57 struct ReleaseHelper<SATraits, typename fbl::enable_if<
58                         (SATraits::PtrTraits::IsManaged == false) &&
59                         (SATraits::AllocatorFlavor == fbl::SlabAllocatorFlavor::INSTANCED)
60                     >::type> {
ReleasePtr__anon1dd898cb0111::ReleaseHelper61     static void ReleasePtr(fbl::SlabAllocator<SATraits>& allocator,
62                            typename SATraits::PtrType& ptr) {
63         // Instanced slab allocators should static_assert if you attempt to
64         // expand their delete method.
65 #if TEST_WILL_NOT_COMPILE || 0
66         allocator.Delete(ptr);
67 #else
68         delete ptr;
69 #endif
70     }
71 };
72 
73 template <typename SATraits>
74 struct ReleaseHelper<SATraits, typename fbl::enable_if<
75                         (SATraits::PtrTraits::IsManaged == false) &&
76                         (SATraits::AllocatorFlavor == fbl::SlabAllocatorFlavor::MANUAL_DELETE)
77                     >::type> {
ReleasePtr__anon1dd898cb0111::ReleaseHelper78     static void ReleasePtr(fbl::SlabAllocator<SATraits>& allocator,
79                            typename SATraits::PtrType& ptr) {
80         // SlabAllocated<> objects which come from MANUAL_DELETE flavors of slab
81         // allocators should have their delete operator protected in order to
82         // prevent someone from calling delete on the object.
83 #if TEST_WILL_NOT_COMPILE || 0
84         delete ptr;
85 #else
86         allocator.Delete(ptr);
87 #endif
88     }
89 };
90 
91 template <typename SATraits>
92 struct ReleaseHelper<SATraits, typename fbl::enable_if<
93                         (SATraits::PtrTraits::IsManaged == true) &&
94                         (SATraits::AllocatorFlavor != fbl::SlabAllocatorFlavor::STATIC)
95                     >::type> {
ReleasePtr__anon1dd898cb0111::ReleaseHelper96     static void ReleasePtr(fbl::SlabAllocator<SATraits>&, typename SATraits::PtrType& ptr) {
97         ptr = nullptr;
98     }
99 };
100 
101 template <typename SATraits>
102 struct ReleaseHelper<SATraits, typename fbl::enable_if<
103                         (SATraits::PtrTraits::IsManaged == false) &&
104                         (SATraits::AllocatorFlavor == fbl::SlabAllocatorFlavor::STATIC)
105                     >::type> {
ReleasePtr__anon1dd898cb0111::ReleaseHelper106     static void ReleasePtr(typename SATraits::PtrType& ptr) {
107         delete ptr;
108     }
109 };
110 
111 template <typename SATraits>
112 struct ReleaseHelper<SATraits, typename fbl::enable_if<
113                         (SATraits::PtrTraits::IsManaged == true) &&
114                         (SATraits::AllocatorFlavor == fbl::SlabAllocatorFlavor::STATIC)
115                     >::type> {
ReleasePtr__anon1dd898cb0111::ReleaseHelper116     static void ReleasePtr(typename SATraits::PtrType& ptr) {
117         ptr = nullptr;
118     }
119 };
120 
121 // Traits which define the various test flavors.
122 template <typename LockType,
123           fbl::SlabAllocatorFlavor AllocatorFlavor = fbl::SlabAllocatorFlavor::INSTANCED,
124           bool ENABLE_OBJ_COUNT = false>
125 struct UnmanagedTestTraits {
126     class ObjType;
127     using PtrType       = ObjType*;
128     using AllocTraits   = fbl::SlabAllocatorTraits
129         <PtrType, 1024, LockType, AllocatorFlavor, ENABLE_OBJ_COUNT>;
130     using AllocatorType = fbl::SlabAllocator<AllocTraits>;
131     using RefList       = fbl::DoublyLinkedList<PtrType>;
132 
133     class ObjType : public TestBase,
134                     public fbl::SlabAllocated<AllocTraits>,
135                     public fbl::DoublyLinkedListable<PtrType> {
136     public:
ObjType()137         ObjType()                                     : TestBase() { }
ObjType(const size_t & val)138         explicit ObjType(const size_t& val)           : TestBase(val) { }
ObjType(size_t && val)139         explicit ObjType(size_t&& val)                : TestBase(std::move(val)) { }
ObjType(const size_t & a,size_t && b)140         explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, std::move(b)) { }
141     };
142 
143     static constexpr size_t MaxSlabs  = 4;
144     static constexpr bool   IsManaged = false;
MaxAllocs__anon1dd898cb0111::UnmanagedTestTraits145     static constexpr size_t MaxAllocs(size_t slabs) { return AllocatorType::AllocsPerSlab * slabs; }
146 };
147 
148 template <typename LockType, bool ENABLE_OBJ_COUNT = false>
149 struct UniquePtrTestTraits {
150     class ObjType;
151     using PtrType       = fbl::unique_ptr<ObjType>;
152     using AllocTraits   = fbl::SlabAllocatorTraits
153         <PtrType, 1024, LockType, fbl::SlabAllocatorFlavor::INSTANCED, ENABLE_OBJ_COUNT>;
154     using AllocatorType = fbl::SlabAllocator<AllocTraits>;
155     using RefList       = fbl::DoublyLinkedList<PtrType>;
156 
157     class ObjType : public TestBase,
158                     public fbl::SlabAllocated<AllocTraits>,
159                     public fbl::DoublyLinkedListable<PtrType> {
160     public:
ObjType()161         ObjType()                                     : TestBase() { }
ObjType(const size_t & val)162         explicit ObjType(const size_t& val)           : TestBase(val) { }
ObjType(size_t && val)163         explicit ObjType(size_t&& val)                : TestBase(std::move(val)) { }
ObjType(const size_t & a,size_t && b)164         explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, std::move(b)) { }
165     };
166 
167 
168     static constexpr size_t MaxSlabs  = 4;
169     static constexpr bool   IsManaged = true;
MaxAllocs__anon1dd898cb0111::UniquePtrTestTraits170     static constexpr size_t MaxAllocs(size_t slabs) { return AllocatorType::AllocsPerSlab * slabs; }
171 };
172 
173 template <typename LockType, bool ENABLE_OBJ_COUNT = false>
174 struct RefPtrTestTraits {
175     class ObjType;
176     using PtrType       = fbl::RefPtr<ObjType>;
177     using AllocTraits   = fbl::SlabAllocatorTraits
178         <PtrType, 1024,LockType, fbl::SlabAllocatorFlavor::INSTANCED, ENABLE_OBJ_COUNT>;
179     using AllocatorType = fbl::SlabAllocator<AllocTraits>;
180     using RefList       = fbl::DoublyLinkedList<PtrType>;
181 
182     class ObjType : public TestBase,
183                     public fbl::RefCounted<ObjType>,
184                     public fbl::SlabAllocated<AllocTraits>,
185                     public fbl::DoublyLinkedListable<PtrType> {
186     public:
ObjType()187         ObjType()                                     : TestBase() { }
ObjType(const size_t & val)188         explicit ObjType(const size_t& val)           : TestBase(val) { }
ObjType(size_t && val)189         explicit ObjType(size_t&& val)                : TestBase(std::move(val)) { }
ObjType(const size_t & a,size_t && b)190         explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, std::move(b)) { }
191     };
192 
193     static constexpr size_t MaxSlabs  = 4;
194     static constexpr bool   IsManaged = true;
MaxAllocs__anon1dd898cb0111::RefPtrTestTraits195     static constexpr size_t MaxAllocs(size_t slabs) { return AllocatorType::AllocsPerSlab * slabs; }
196 };
197 
198 template <typename, bool> struct ObjCounterHelper;
199 
200 template <typename SA> struct ObjCounterHelper<SA, true> {
CheckObjCount__anon1dd898cb0111::ObjCounterHelper201     static bool CheckObjCount(const SA& allocator, size_t expected) {
202         return (allocator.obj_count() == expected);
203     }
CheckMaxObjCount__anon1dd898cb0111::ObjCounterHelper204     static bool CheckMaxObjCount(const SA& allocator, size_t expected) {
205         return (allocator.max_obj_count() == expected);
206     }
ResetMaxObjCount__anon1dd898cb0111::ObjCounterHelper207     static void ResetMaxObjCount(SA* allocator) {
208         allocator->ResetMaxObjCount();
209     }
StaticCheckObjCount__anon1dd898cb0111::ObjCounterHelper210     static bool StaticCheckObjCount(size_t expected) {
211         return (SA::obj_count() == expected);
212     }
StaticCheckMaxObjCount__anon1dd898cb0111::ObjCounterHelper213     static bool StaticCheckMaxObjCount(size_t expected) {
214         return (SA::max_obj_count() == expected);
215     }
StaticResetMaxObjCount__anon1dd898cb0111::ObjCounterHelper216     static void StaticResetMaxObjCount() {
217         SA::ResetMaxObjCount();
218     }
219 };
220 
221 template <typename SA> struct ObjCounterHelper<SA, false> {
CheckObjCount__anon1dd898cb0111::ObjCounterHelper222     static bool CheckObjCount(const SA&, size_t) {
223         return true;
224     }
CheckMaxObjCount__anon1dd898cb0111::ObjCounterHelper225     static bool CheckMaxObjCount(const SA&, size_t) {
226         return true;
227     }
ResetMaxObjCount__anon1dd898cb0111::ObjCounterHelper228     static void ResetMaxObjCount(SA*) {}
StaticCheckObjCount__anon1dd898cb0111::ObjCounterHelper229     static bool StaticCheckObjCount(size_t) {
230         return true;
231     }
StaticCheckMaxObjCount__anon1dd898cb0111::ObjCounterHelper232     static bool StaticCheckMaxObjCount(size_t) {
233         return true;
234     }
StaticResetMaxObjCount__anon1dd898cb0111::ObjCounterHelper235     static void StaticResetMaxObjCount() {}
236 };
237 
238 template <typename Traits>
do_slab_test(typename Traits::AllocatorType & allocator,size_t test_allocs)239 bool do_slab_test(typename Traits::AllocatorType& allocator, size_t test_allocs) {
240     BEGIN_TEST;
241 
242     const size_t MAX_ALLOCS = Traits::MaxAllocs(allocator.max_slabs());
243     typename Traits::RefList ref_list;
244     const bool ENB_OBJ_COUNT = Traits::AllocTraits::ENABLE_OBJ_COUNT;
245     using AllocatorType = typename Traits::AllocatorType;
246 
247     ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::ResetMaxObjCount(&allocator);
248     bool res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount(allocator, 0);
249     EXPECT_TRUE(res);
250     res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount(allocator, 0);
251     EXPECT_TRUE(res);
252 
253     // Allocate up to the test limit.
254     for (size_t i = 0; i < test_allocs; ++i) {
255         typename Traits::PtrType ptr;
256 
257         EXPECT_EQ(fbl::min(i, MAX_ALLOCS), TestBase::allocated_obj_count());
258         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount
259             (allocator,TestBase::allocated_obj_count());
260         EXPECT_TRUE(res);
261         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount
262             (allocator, TestBase::allocated_obj_count());
263         EXPECT_TRUE(res);
264 
265         // Allocate the object; exercise the various constructors
266         switch (i % 4) {
267         case 0: ptr = allocator.New(); break;
268         case 1: ptr = allocator.New(i); break;
269         case 2: ptr = allocator.New(std::move(i)); break;
270         case 3: ptr = allocator.New(i, std::move(i)); break;
271         }
272 
273         if (i < MAX_ALLOCS) {
274             ASSERT_NONNULL(ptr, "Allocation failed when it should not have!");
275             ref_list.push_front(std::move(ptr));
276         } else {
277             ASSERT_NULL(ptr, "Allocation succeeded when it should not have!");
278         }
279 
280         EXPECT_EQ(fbl::min(i + 1, MAX_ALLOCS), TestBase::allocated_obj_count());
281         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount
282             (allocator, TestBase::allocated_obj_count());
283         EXPECT_TRUE(res);
284         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount
285             (allocator, TestBase::allocated_obj_count());
286         EXPECT_TRUE(res);
287     }
288 
289     // Now remove and de-allocate.
290     size_t max_obj_count = TestBase::allocated_obj_count();
291     size_t i;
292     for (i = 0; !ref_list.is_empty(); ++i) {
293         auto ptr = ref_list.pop_back();
294 
295         ASSERT_NONNULL(ptr, "nullptr in ref list!  This should be impossible.");
296         EXPECT_EQ(fbl::min(test_allocs, MAX_ALLOCS) - i, TestBase::allocated_obj_count());
297         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount
298             (allocator, TestBase::allocated_obj_count());
299         EXPECT_TRUE(res);
300         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount
301             (allocator, max_obj_count);
302         EXPECT_TRUE(res);
303 
304         switch (i % 4) {
305         case 0:
306             EXPECT_EQ(ConstructType::DEFAULT, ptr->ctype());
307             break;
308         case 1:
309             EXPECT_EQ(ConstructType::LVALUE_REF, ptr->ctype());
310             break;
311         case 2:
312             EXPECT_EQ(ConstructType::RVALUE_REF, ptr->ctype());
313             break;
314         case 3:
315             EXPECT_EQ(ConstructType::L_THEN_R_REF, ptr->ctype());
316             break;
317         }
318 
319         // Release the reference (how this gets done depends on allocator flavor and pointer type)
320         ReleaseHelper<typename Traits::AllocTraits>::ReleasePtr(allocator, ptr);
321 
322         if (i % 2 == 1) {
323             ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::ResetMaxObjCount(&allocator);
324             max_obj_count = TestBase::allocated_obj_count();
325         }
326         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount
327             (allocator, max_obj_count);
328         EXPECT_TRUE(res);
329     }
330 
331     EXPECT_EQ(fbl::min(test_allocs, MAX_ALLOCS), i);
332     res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount
333         (allocator, 0);
334     EXPECT_TRUE(res);
335     res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount
336         (allocator, i % 2);
337     EXPECT_TRUE(res);
338     ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::ResetMaxObjCount(&allocator);
339     res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount
340         (allocator, 0);
341     EXPECT_TRUE(res);
342 
343 #if TEST_WILL_NOT_COMPILE || 0
344     allocator.obj_count();
345 #endif
346 #if TEST_WILL_NOT_COMPILE || 0
347     allocator.max_obj_count();
348 #endif
349 #if TEST_WILL_NOT_COMPILE || 0
350     allocator.ResetMaxObjCount();
351 #endif
352     END_TEST;
353 }
354 
355 template <typename Traits, size_t SlabCount = Traits::MaxSlabs>
slab_test()356 bool slab_test() {
357     BEGIN_TEST;
358     typename Traits::AllocatorType allocator(SlabCount);
359 
360     TestBase::Reset();
361 
362     EXPECT_TRUE(do_slab_test<Traits>(allocator, 1),
363                 "Single allocator test failed");
364 
365     EXPECT_TRUE(do_slab_test<Traits>(allocator, Traits::MaxAllocs(SlabCount) / 2),
366                 "1/2 capacity allocator test failed");
367 
368     EXPECT_TRUE(do_slab_test<Traits>(allocator, Traits::MaxAllocs(SlabCount) + 4),
369                 "Over-capacity allocator test failed");
370 
371     END_TEST;
372 }
373 
374 template <typename LockType, bool ENABLE_OBJ_COUNT = false>
375 struct StaticUnmanagedTestTraits {
376     class ObjType;
377     using PtrType       = ObjType*;
378     using AllocTraits   = fbl::StaticSlabAllocatorTraits<PtrType, 1024, LockType, ENABLE_OBJ_COUNT>;
379     using AllocatorType = fbl::SlabAllocator<AllocTraits>;
380     using RefList       = fbl::DoublyLinkedList<PtrType>;
381 
382     class ObjType : public TestBase,
383                     public fbl::SlabAllocated<AllocTraits>,
384                     public fbl::DoublyLinkedListable<PtrType> {
385     public:
ObjType()386         ObjType()                                     : TestBase() { }
ObjType(const size_t & val)387         explicit ObjType(const size_t& val)           : TestBase(val) { }
ObjType(size_t && val)388         explicit ObjType(size_t&& val)                : TestBase(std::move(val)) { }
ObjType(const size_t & a,size_t && b)389         explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, std::move(b)) { }
390     };
391 
MaxAllocs__anon1dd898cb0111::StaticUnmanagedTestTraits392     static size_t MaxAllocs() { return AllocatorType::AllocsPerSlab * AllocatorType::max_slabs(); }
393 
394     static constexpr size_t MaxSlabs  = 4;
395     static constexpr bool   IsManaged = false;
396 };
397 template <typename LockType>
398 using StaticCountedUnmanagedTestTraits = StaticUnmanagedTestTraits<LockType, true>;
399 
400 template <typename LockType, bool ENABLE_OBJ_COUNT = false>
401 struct StaticUniquePtrTestTraits {
402     class ObjType;
403     using PtrType       = fbl::unique_ptr<ObjType>;
404     using AllocTraits   = fbl::StaticSlabAllocatorTraits<PtrType, 1024, LockType, ENABLE_OBJ_COUNT>;
405     using AllocatorType = fbl::SlabAllocator<AllocTraits>;
406     using RefList       = fbl::DoublyLinkedList<PtrType>;
407 
408     class ObjType : public TestBase,
409                     public fbl::SlabAllocated<AllocTraits>,
410                     public fbl::DoublyLinkedListable<PtrType> {
411     public:
ObjType()412         ObjType()                                     : TestBase() { }
ObjType(const size_t & val)413         explicit ObjType(const size_t& val)           : TestBase(val) { }
ObjType(size_t && val)414         explicit ObjType(size_t&& val)                : TestBase(std::move(val)) { }
ObjType(const size_t & a,size_t && b)415         explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, std::move(b)) { }
416     };
417 
MaxAllocs__anon1dd898cb0111::StaticUniquePtrTestTraits418     static size_t MaxAllocs() { return AllocatorType::AllocsPerSlab * AllocatorType::max_slabs(); }
419 
420     static constexpr size_t MaxSlabs  = 4;
421     static constexpr bool   IsManaged = false;
422 };
423 
424 template <typename LockType>
425 using StaticCountedUniquePtrTestTraits = StaticUniquePtrTestTraits<LockType, true>;
426 
427 template <typename LockType, bool ENABLE_OBJ_COUNT = false>
428 struct StaticRefPtrTestTraits {
429     class ObjType;
430     using PtrType       = fbl::RefPtr<ObjType>;
431     using AllocTraits   = fbl::StaticSlabAllocatorTraits<PtrType, 1024, LockType, ENABLE_OBJ_COUNT>;
432     using AllocatorType = fbl::SlabAllocator<AllocTraits>;
433     using RefList       = fbl::DoublyLinkedList<PtrType>;
434 
435     class ObjType : public TestBase,
436                     public fbl::SlabAllocated<AllocTraits>,
437                     public fbl::RefCounted<ObjType>,
438                     public fbl::DoublyLinkedListable<PtrType> {
439     public:
ObjType()440         ObjType()                                     : TestBase() { }
ObjType(const size_t & val)441         explicit ObjType(const size_t& val)           : TestBase(val) { }
ObjType(size_t && val)442         explicit ObjType(size_t&& val)                : TestBase(std::move(val)) { }
ObjType(const size_t & a,size_t && b)443         explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, std::move(b)) { }
444     };
445 
446     static constexpr size_t MaxSlabs  = 4;
447     static constexpr bool   IsManaged = false;
448 
MaxAllocs__anon1dd898cb0111::StaticRefPtrTestTraits449     static size_t MaxAllocs() {
450         return AllocatorType::AllocsPerSlab * AllocatorType::max_slabs();
451     }
452 };
453 
454 template <typename LockType>
455 using StaticCountedRefPtrTestTraits = StaticRefPtrTestTraits<LockType, true>;
456 
457 template <typename Traits>
do_static_slab_test(size_t test_allocs)458 bool do_static_slab_test(size_t test_allocs) {
459     BEGIN_TEST;
460 
461     const bool ENB_OBJ_COUNT = Traits::AllocTraits::ENABLE_OBJ_COUNT;
462     using AllocatorType = typename Traits::AllocatorType;
463 
464     const size_t MAX_ALLOCS = Traits::MaxAllocs();
465     typename Traits::RefList ref_list;
466     ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticResetMaxObjCount();
467     bool res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount(0);
468     EXPECT_TRUE(res);
469     res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(0);
470     EXPECT_TRUE(res);
471 
472     // Allocate up to the test limit.
473     for (size_t i = 0; i < test_allocs; ++i) {
474         typename Traits::PtrType ptr;
475 
476         EXPECT_EQ(fbl::min(i, MAX_ALLOCS), TestBase::allocated_obj_count());
477         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount
478             (TestBase::allocated_obj_count());
479         EXPECT_TRUE(res);
480         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount
481             (TestBase::allocated_obj_count());
482         EXPECT_TRUE(res);
483 
484         // Allocate the object; exercise the various constructors
485         switch (i % 4) {
486         case 0: ptr = AllocatorType::New(); break;
487         case 1: ptr = AllocatorType::New(i); break;
488         case 2: ptr = AllocatorType::New(std::move(i)); break;
489         case 3: ptr = AllocatorType::New(i, std::move(i)); break;
490         }
491 
492         if (i < MAX_ALLOCS) {
493             ASSERT_NONNULL(ptr, "Allocation failed when it should not have!");
494             ref_list.push_front(std::move(ptr));
495         } else {
496             ASSERT_NULL(ptr, "Allocation succeeded when it should not have!");
497         }
498 
499         EXPECT_EQ(fbl::min(i + 1, MAX_ALLOCS), TestBase::allocated_obj_count());
500         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount
501             (TestBase::allocated_obj_count());
502         EXPECT_TRUE(res);
503         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount
504             (TestBase::allocated_obj_count());
505         EXPECT_TRUE(res);
506     }
507 
508     // Now remove and de-allocate.
509     size_t max_obj_count = TestBase::allocated_obj_count();
510     size_t i;
511     for (i = 0; !ref_list.is_empty(); ++i) {
512         auto ptr = ref_list.pop_back();
513 
514         ASSERT_NONNULL(ptr, "nullptr in ref list!  This should be impossible.");
515         EXPECT_EQ(fbl::min(test_allocs, MAX_ALLOCS) - i, TestBase::allocated_obj_count());
516         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount
517             (TestBase::allocated_obj_count());
518         EXPECT_TRUE(res);
519         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(max_obj_count);
520         EXPECT_TRUE(res);
521 
522         switch (i % 4) {
523         case 0:
524             EXPECT_EQ(ConstructType::DEFAULT, ptr->ctype());
525             break;
526         case 1:
527             EXPECT_EQ(ConstructType::LVALUE_REF, ptr->ctype());
528             break;
529         case 2:
530             EXPECT_EQ(ConstructType::RVALUE_REF, ptr->ctype());
531             break;
532         case 3:
533             EXPECT_EQ(ConstructType::L_THEN_R_REF, ptr->ctype());
534             break;
535         }
536 
537         // Release the reference (how this gets done depends on allocator flavor and pointer type)
538         ReleaseHelper<typename Traits::AllocTraits>::ReleasePtr(ptr);
539         if (i % 2 == 1) {
540             ObjCounterHelper<AllocatorType,
541                            ENB_OBJ_COUNT>::StaticResetMaxObjCount();
542             max_obj_count = TestBase::allocated_obj_count();
543         }
544         res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(max_obj_count);
545         EXPECT_TRUE(res);
546     }
547 
548     EXPECT_EQ(fbl::min(test_allocs, MAX_ALLOCS), i);
549     res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount(0);
550     EXPECT_TRUE(res);
551     res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(i % 2);
552     EXPECT_TRUE(res);
553     ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticResetMaxObjCount();
554     res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(0);
555     EXPECT_TRUE(res);
556 #if TEST_WILL_NOT_COMPILE || 0
557     AllocatorType::obj_count();
558 #endif
559 #if TEST_WILL_NOT_COMPILE || 0
560     AllocatorType::max_obj_count();
561 #endif
562 #if TEST_WILL_NOT_COMPILE || 0
563     AllocatorType::ResetMaxObjCount();
564 #endif
565 
566     END_TEST;
567 }
568 
569 
570 template <typename Traits>
static_slab_test()571 bool static_slab_test() {
572     BEGIN_TEST;
573 
574     TestBase::Reset();
575 
576     EXPECT_TRUE(do_static_slab_test<Traits>(1),
577                 "Single allocator test failed");
578 
579     EXPECT_TRUE(do_static_slab_test<Traits>(Traits::MaxAllocs() / 2),
580                 "1/2 capacity allocator test failed");
581 
582     EXPECT_TRUE(do_static_slab_test<Traits>(Traits::MaxAllocs() + 4),
583                 "Over-capacity allocator test failed");
584 
585     END_TEST;
586 }
587 }  // anon namespace
588 
589 using MutexLock = ::fbl::Mutex;
590 using NullLock  = ::fbl::NullLock;
591 
592 DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticUnmanagedTestTraits<MutexLock>::AllocTraits, 1);
593 DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticUniquePtrTestTraits<MutexLock>::AllocTraits, 1);
594 DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticRefPtrTestTraits<MutexLock>::AllocTraits, 1);
595 
596 DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticUnmanagedTestTraits<NullLock>::AllocTraits, 1);
597 DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticUniquePtrTestTraits<NullLock>::AllocTraits, 1);
598 DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticRefPtrTestTraits<NullLock>::AllocTraits, 1);
599 
600 DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedUnmanagedTestTraits<MutexLock>::AllocTraits, 1);
601 DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedUniquePtrTestTraits<MutexLock>::AllocTraits, 1);
602 DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedRefPtrTestTraits<MutexLock>::AllocTraits, 1);
603 
604 DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedUnmanagedTestTraits<NullLock>::AllocTraits, 1);
605 DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedUniquePtrTestTraits<NullLock>::AllocTraits, 1);
606 DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedRefPtrTestTraits<NullLock>::AllocTraits, 1);
607 
608 BEGIN_TEST_CASE(slab_allocator_tests)
609 RUN_NAMED_TEST("Unmanaged Single Slab (mutex)", (slab_test<UnmanagedTestTraits<MutexLock>, 1>))
610 RUN_NAMED_TEST("Unmanaged Multi Slab  (mutex)", (slab_test<UnmanagedTestTraits<MutexLock>>))
611 RUN_NAMED_TEST("UniquePtr Single Slab (mutex)", (slab_test<UniquePtrTestTraits<MutexLock>, 1>))
612 RUN_NAMED_TEST("UniquePtr Multi Slab  (mutex)", (slab_test<UniquePtrTestTraits<MutexLock>>))
613 RUN_NAMED_TEST("RefPtr Single Slab    (mutex)", (slab_test<RefPtrTestTraits<MutexLock>, 1>))
614 RUN_NAMED_TEST("RefPtr Multi Slab     (mutex)", (slab_test<RefPtrTestTraits<MutexLock>>))
615 
616 RUN_NAMED_TEST("Unmanaged Single Slab (unlock)", (slab_test<UnmanagedTestTraits<NullLock>, 1>))
617 RUN_NAMED_TEST("Unmanaged Multi Slab  (unlock)", (slab_test<UnmanagedTestTraits<NullLock>>))
618 RUN_NAMED_TEST("UniquePtr Single Slab (unlock)", (slab_test<UniquePtrTestTraits<NullLock>, 1>))
619 RUN_NAMED_TEST("UniquePtr Multi Slab  (unlock)", (slab_test<UniquePtrTestTraits<NullLock>>))
620 RUN_NAMED_TEST("RefPtr Single Slab    (unlock)", (slab_test<RefPtrTestTraits<NullLock>, 1>))
621 RUN_NAMED_TEST("RefPtr Multi Slab     (unlock)", (slab_test<RefPtrTestTraits<NullLock>>))
622 
623 RUN_NAMED_TEST("Manual Delete Unmanaged (mutex)",
624               (slab_test<UnmanagedTestTraits<MutexLock, fbl::SlabAllocatorFlavor::MANUAL_DELETE>>))
625 RUN_NAMED_TEST("Manual Delete Unmanaged (unlock)",
626               (slab_test<UnmanagedTestTraits<NullLock, fbl::SlabAllocatorFlavor::MANUAL_DELETE>>))
627 
628 RUN_NAMED_TEST("Static Unmanaged (unlock)", (static_slab_test<StaticUnmanagedTestTraits<NullLock>>))
629 RUN_NAMED_TEST("Static UniquePtr (unlock)", (static_slab_test<StaticUniquePtrTestTraits<NullLock>>))
630 RUN_NAMED_TEST("Static RefPtr    (unlock)", (static_slab_test<StaticRefPtrTestTraits<NullLock>>))
631 
632 RUN_NAMED_TEST("Static Unmanaged (mutex)", (static_slab_test<StaticUnmanagedTestTraits<MutexLock>>))
633 RUN_NAMED_TEST("Static UniquePtr (mutex)", (static_slab_test<StaticUniquePtrTestTraits<MutexLock>>))
634 RUN_NAMED_TEST("Static RefPtr    (mutex)", (static_slab_test<StaticRefPtrTestTraits<MutexLock>>))
635 
636 RUN_NAMED_TEST("Static Unmanaged (unlock)", (static_slab_test<StaticUnmanagedTestTraits<NullLock>>))
637 RUN_NAMED_TEST("Static UniquePtr (unlock)", (static_slab_test<StaticUniquePtrTestTraits<NullLock>>))
638 RUN_NAMED_TEST("Static RefPtr    (unlock)", (static_slab_test<StaticRefPtrTestTraits<NullLock>>))
639 
640 RUN_NAMED_TEST("Counted Unmanaged Single Slab (mutex)",(slab_test
641     <UnmanagedTestTraits<MutexLock, fbl::SlabAllocatorFlavor::INSTANCED, true>, 1>))
642 RUN_NAMED_TEST("Counted Unmanaged Multi Slab  (mutex)", (slab_test
643     <UnmanagedTestTraits<MutexLock, fbl::SlabAllocatorFlavor::INSTANCED, true>>))
644 RUN_NAMED_TEST("Counted UniquePtr Single Slab (mutex)", (slab_test
645     <UniquePtrTestTraits<MutexLock, true>, 1>))
646 RUN_NAMED_TEST("Counted UniquePtr Multi Slab  (mutex)", (slab_test
647     <UniquePtrTestTraits<MutexLock, true>>))
648 RUN_NAMED_TEST("Counted RefPtr Single Slab    (mutex)", (slab_test
649     <RefPtrTestTraits<MutexLock, true>, 1>))
650 RUN_NAMED_TEST("Counted RefPtr Multi Slab     (mutex)", (slab_test
651     <RefPtrTestTraits<MutexLock, true>>))
652 
653 RUN_NAMED_TEST("Counted Unmanaged Single Slab (unlock)", (slab_test
654     <UnmanagedTestTraits<NullLock, fbl::SlabAllocatorFlavor::INSTANCED, true>, 1>))
655 RUN_NAMED_TEST("Counted Unmanaged Multi Slab  (unlock)", (slab_test
656     <UnmanagedTestTraits<NullLock, fbl::SlabAllocatorFlavor::INSTANCED, true>>))
657 RUN_NAMED_TEST("Counted UniquePtr Single Slab (unlock)", (slab_test
658     <UniquePtrTestTraits<NullLock, true>, 1>))
659 RUN_NAMED_TEST("Counted UniquePtr Multi Slab  (unlock)", (slab_test
660     <UniquePtrTestTraits<NullLock, true>>))
661 RUN_NAMED_TEST("Counted RefPtr Single Slab    (unlock)", (slab_test
662     <RefPtrTestTraits<NullLock, true>, 1>))
663 RUN_NAMED_TEST("Counted RefPtr Multi Slab     (unlock)", (slab_test
664     <RefPtrTestTraits<NullLock, true>>))
665 
666 RUN_NAMED_TEST("Counted Manual Delete Unmanaged (mutex)", (slab_test
667     <UnmanagedTestTraits<MutexLock, fbl::SlabAllocatorFlavor::MANUAL_DELETE, true>>))
668 RUN_NAMED_TEST("Counted Manual Delete Unmanaged (unlock)", (slab_test
669     <UnmanagedTestTraits<NullLock, fbl::SlabAllocatorFlavor::MANUAL_DELETE, true>>))
670 
671 RUN_NAMED_TEST("Counted Static Unmanaged (unlock)", (static_slab_test
672     <StaticUnmanagedTestTraits<NullLock, true>>))
673 RUN_NAMED_TEST("Counted Static UniquePtr (unlock)", (static_slab_test
674     <StaticUniquePtrTestTraits<NullLock, true>>))
675 RUN_NAMED_TEST("Counted Static RefPtr    (unlock)", (static_slab_test
676     <StaticRefPtrTestTraits<NullLock, true>>))
677 
678 RUN_NAMED_TEST("Counted Static Unmanaged (mutex)", (static_slab_test
679     <StaticUnmanagedTestTraits<MutexLock, true>>))
680 RUN_NAMED_TEST("Counted Static UniquePtr (mutex)", (static_slab_test
681     <StaticUniquePtrTestTraits<MutexLock, true>>))
682 RUN_NAMED_TEST("Counted Static RefPtr    (mutex)", (static_slab_test
683     <StaticRefPtrTestTraits<MutexLock, true>>))
684 
685 RUN_NAMED_TEST("Counted Static Unmanaged (unlock)", (static_slab_test
686     <StaticUnmanagedTestTraits<NullLock, true>>))
687 RUN_NAMED_TEST("Counted Static UniquePtr (unlock)", (static_slab_test
688     <StaticUniquePtrTestTraits<NullLock, true>>))
689 RUN_NAMED_TEST("Counted Static RefPtr    (unlock)", (static_slab_test
690     <StaticRefPtrTestTraits<NullLock, true>>))
691 END_TEST_CASE(slab_allocator_tests);
692