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