1use crate::recyclable::Recyclable;
58use crate::ref_counted::HasRefCount;
59use crate::ref_ptr::RefPtr;
60use crate::singly_linked_list::{SinglyLinkedList, SinglyLinkedListNode};
61use crate::unique_ptr::UniquePtr;
62use core::alloc::Layout;
63use core::cmp::max;
64use core::marker::PhantomData;
65use core::mem::{align_of, size_of};
66use core::pin::Pin;
67use core::ptr::{NonNull, drop_in_place, write};
68use kalloc::AllocError;
69pub use ksync::RawLock;
70use ksync::{KCell, KMutex, RawMutex, guarded, lock};
71use pin_init::{pin_data, pin_init, pinned_drop};
72
73pub const DEFAULT_SLAB_ALLOCATOR_SLAB_SIZE: usize = 16384;
75
76#[derive(crate::SinglyLinkedListContainable)]
77#[repr(C)]
78struct SlabHeader {
79 #[sll_node]
80 node: SinglyLinkedListNode<SlabHeader>,
81 bytes_used: usize,
82}
83
84impl SlabHeader {
85 fn new(bytes_used: usize) -> Self {
86 assert!(
87 bytes_used >= size_of::<Self>(),
88 "bytes_used ({}) must be at least as large as SlabHeader ({})",
89 bytes_used,
90 size_of::<Self>()
91 );
92 Self { node: SinglyLinkedListNode::new(), bytes_used }
93 }
94
95 fn allocate(&mut self, alloc_size: usize, slab_size: usize) -> Option<NonNull<u8>> {
96 if self.bytes_used + alloc_size > slab_size {
97 return None;
98 }
99 let self_ptr = self as *mut SlabHeader as *mut u8;
100 let ret = unsafe { self_ptr.add(self.bytes_used) };
102 self.bytes_used += alloc_size;
103 Some(unsafe { NonNull::new_unchecked(ret) })
106 }
107}
108
109#[derive(crate::SinglyLinkedListContainable)]
110#[repr(C)]
111struct FreeListEntry {
112 #[sll_node]
113 node: SinglyLinkedListNode<FreeListEntry>,
114}
115
116#[guarded]
186#[pin_data(PinnedDrop)]
187pub struct SlabAllocator<
188 T,
189 L: RawLock = RawMutex,
190 const SLAB_SIZE: usize = DEFAULT_SLAB_ALLOCATOR_SLAB_SIZE,
191 const TRACK_OBJECT_COUNT: bool = false,
192> {
193 #[mutex]
194 mu: KMutex<L>,
195
196 #[guarded_by(mu)]
197 free_list: SinglyLinkedList<NonNull<FreeListEntry>>,
198 #[guarded_by(mu)]
199 slab_list: SinglyLinkedList<NonNull<SlabHeader>>,
200 #[guarded_by(mu)]
201 slab_count: usize,
202 #[guarded_by(mu)]
205 obj_count: usize,
206 #[guarded_by(mu)]
207 max_obj_count: usize,
208
209 max_slabs: usize,
210 _phantom: PhantomData<T>,
211}
212
213unsafe impl<T, L: RawLock + Sync, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool> Sync
214 for SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>
215{
216}
217unsafe impl<T, L: RawLock + Send, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool> Send
218 for SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>
219{
220}
221
222pub struct SlabOrigin<
227 T,
228 L: RawLock = RawMutex,
229 const SLAB_SIZE: usize = DEFAULT_SLAB_ALLOCATOR_SLAB_SIZE,
230 const TRACK_OBJECT_COUNT: bool = false,
231> {
232 origin: Option<NonNull<SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>>>,
233}
234
235impl<T, L: RawLock, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool>
236 SlabOrigin<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>
237{
238 pub const fn new() -> Self {
240 Self { origin: None }
241 }
242
243 pub fn set(&mut self, origin: NonNull<SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>>) {
245 self.origin = Some(origin);
246 }
247
248 pub fn get(&self) -> Option<NonNull<SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>>> {
250 self.origin
251 }
252}
253
254impl<T, L: RawLock, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool> Default
255 for SlabOrigin<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>
256{
257 fn default() -> Self {
258 Self::new()
259 }
260}
261
262unsafe impl<T, L: RawLock + Sync, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool> Sync
264 for SlabOrigin<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>
265{
266}
267unsafe impl<T, L: RawLock + Send, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool> Send
268 for SlabOrigin<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>
269{
270}
271
272pub trait InstancedSlabAllocated<
277 L: RawLock,
278 const SLAB_SIZE: usize,
279 const TRACK_OBJECT_COUNT: bool = false,
280>: Sized
281{
282 fn slab_origin(&self)
284 -> Option<NonNull<SlabAllocator<Self, L, SLAB_SIZE, TRACK_OBJECT_COUNT>>>;
285 fn set_slab_origin(
287 &mut self,
288 origin: NonNull<SlabAllocator<Self, L, SLAB_SIZE, TRACK_OBJECT_COUNT>>,
289 );
290}
291
292pub trait StaticSlabAllocated<
297 L: RawLock,
298 const SLAB_SIZE: usize,
299 const TRACK_OBJECT_COUNT: bool = false,
300>: Sized
301{
302 fn get_allocator() -> &'static SlabAllocator<Self, L, SLAB_SIZE, TRACK_OBJECT_COUNT>;
304}
305
306#[macro_export]
311macro_rules! impl_instanced_slab_allocatable {
312 ($ty:ty, $lock:ty, $slab_size:expr) => {
313 $crate::impl_instanced_slab_allocatable!($ty, $lock, $slab_size, false);
314 };
315 ($ty:ty, $lock:ty, $slab_size:expr, $track_obj_count:expr) => {
316 impl $crate::InstancedSlabAllocated<$lock, $slab_size, $track_obj_count> for $ty {
317 fn slab_origin(
318 &self,
319 ) -> Option<
320 ::core::ptr::NonNull<
321 $crate::SlabAllocator<Self, $lock, $slab_size, $track_obj_count>,
322 >,
323 > {
324 self.slab_origin.get()
325 }
326
327 fn set_slab_origin(
328 &mut self,
329 origin: ::core::ptr::NonNull<
330 $crate::SlabAllocator<Self, $lock, $slab_size, $track_obj_count>,
331 >,
332 ) {
333 self.slab_origin.set(origin);
334 }
335 }
336
337 unsafe impl $crate::Recyclable for $ty {
338 fn allocate(_value: Self) -> Result<::core::ptr::NonNull<Self>, ::kalloc::AllocError> {
339 Err(::kalloc::AllocError)
340 }
341
342 unsafe fn recycle(ptr: ::core::ptr::NonNull<Self>) {
343 let origin = unsafe { ptr.as_ref().slab_origin() };
346 if let Some(origin) = origin {
347 unsafe {
351 ::core::ptr::drop_in_place(ptr.as_ptr());
352 origin.as_ref().return_to_free_list(ptr);
353 }
354 }
355 }
356 }
357 };
358}
359
360#[macro_export]
362macro_rules! impl_static_slab_allocatable {
363 ($ty:ty, $lock:ty, $slab_size:expr, $allocator:expr) => {
364 $crate::impl_static_slab_allocatable!($ty, $lock, $slab_size, $allocator, false);
365 };
366 ($ty:ty, $lock:ty, $slab_size:expr, $allocator:expr, $track_obj_count:expr) => {
367 impl $crate::StaticSlabAllocated<$lock, $slab_size, $track_obj_count> for $ty {
368 fn get_allocator()
369 -> &'static $crate::SlabAllocator<Self, $lock, $slab_size, $track_obj_count> {
370 &$allocator
371 }
372 }
373
374 unsafe impl $crate::Recyclable for $ty {
375 fn allocate(value: Self) -> Result<::core::ptr::NonNull<Self>, ::kalloc::AllocError> {
376 let allocator = <Self as $crate::StaticSlabAllocated<
377 $lock,
378 $slab_size,
379 $track_obj_count,
380 >>::get_allocator();
381 let ptr = allocator.alloc_raw()?;
382 unsafe {
385 ::core::ptr::write(ptr.as_ptr(), value);
386 }
387 Ok(ptr)
388 }
389
390 unsafe fn recycle(ptr: ::core::ptr::NonNull<Self>) {
391 let allocator = <Self as $crate::StaticSlabAllocated<
392 $lock,
393 $slab_size,
394 $track_obj_count,
395 >>::get_allocator();
396 unsafe {
400 ::core::ptr::drop_in_place(ptr.as_ptr());
401 allocator.return_to_free_list(ptr);
402 }
403 }
404 }
405 };
406}
407
408impl<T, L: RawLock, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool>
409 SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>
410{
411 pub const ALLOC_ALIGN: usize = if align_of::<FreeListEntry>() > align_of::<T>() {
412 align_of::<FreeListEntry>()
413 } else {
414 align_of::<T>()
415 };
416 pub const ALLOC_SIZE: usize = {
417 let raw_size = if size_of::<FreeListEntry>() > size_of::<T>() {
418 size_of::<FreeListEntry>()
419 } else {
420 size_of::<T>()
421 };
422 match Layout::from_size_align(raw_size, Self::ALLOC_ALIGN) {
423 Ok(layout) => layout.pad_to_align().size(),
424 Err(_) => panic!("invalid layout"),
425 }
426 };
427 pub const STORAGE_OFFSET: usize =
428 match Layout::from_size_align(size_of::<SlabHeader>(), Self::ALLOC_ALIGN) {
429 Ok(layout) => layout.pad_to_align().size(),
430 Err(_) => panic!("invalid layout"),
431 };
432
433 pub const ALLOCS_PER_SLAB: usize = (SLAB_SIZE - Self::STORAGE_OFFSET) / Self::ALLOC_SIZE;
435
436 const _ASSERT: () = {
437 assert!(
438 Self::ALLOC_SIZE % Self::ALLOC_ALIGN == 0,
439 "Allocation size must be a multiple of alignment"
440 );
441 assert!(
442 size_of::<SlabHeader>() < SLAB_SIZE,
443 "SLAB_SIZE too small to hold slab bookkeeping"
444 );
445 assert!(
446 SLAB_SIZE >= Self::STORAGE_OFFSET + Self::ALLOC_SIZE,
447 "SLAB_SIZE too small to hold even 1 allocation"
448 );
449 };
450
451 pub fn preallocate(&self) -> Result<(), AllocError> {
456 let ptr = self.alloc_raw()?;
457 unsafe {
459 self.return_to_free_list(ptr);
460 }
461 if TRACK_OBJECT_COUNT {
462 lock!(let guard = self.lock_mu());
463 let fields = guard.fields_mut();
464 *fields.obj_count = 0;
465 *fields.max_obj_count = 0;
466 }
467 Ok(())
468 }
469
470 pub const fn const_new(max_slabs: usize, lock: L) -> Self {
474 let _ = Self::_ASSERT;
475 Self {
476 mu: KMutex::new(lock),
477 free_list: KCell::new(SinglyLinkedList::new()),
478 slab_list: KCell::new(SinglyLinkedList::new()),
479 slab_count: KCell::new(0),
480 obj_count: KCell::new(0),
481 max_obj_count: KCell::new(0),
482 max_slabs,
483 _phantom: PhantomData,
484 }
485 }
486
487 pub fn init(max_slabs: usize) -> impl pin_init::PinInit<Self, core::convert::Infallible> {
489 pin_init!(Self {
490 mu <- KMutex::init(),
491 free_list: SinglyLinkedList::new().into(),
492 slab_list: SinglyLinkedList::new().into(),
493 slab_count: 0.into(),
494 obj_count: 0.into(),
495 max_obj_count: 0.into(),
496 max_slabs,
497 _phantom: PhantomData,
498 })
499 }
500
501 #[inline(always)]
502 fn slab_layout() -> Layout {
503 let alloc_align = Self::ALLOC_ALIGN;
504 let slab_align = max(align_of::<SlabHeader>(), alloc_align);
505 unsafe { Layout::from_size_align_unchecked(SLAB_SIZE, slab_align) }
509 }
510
511 fn alloc_slab() -> Result<NonNull<SlabHeader>, AllocError> {
512 let layout = Self::slab_layout();
513 let slab_mem = unsafe { kalloc::alloc(layout).ok_or(AllocError)? };
515 let slab_ptr = slab_mem.cast::<SlabHeader>();
516
517 unsafe {
519 write(slab_ptr.as_ptr(), SlabHeader::new(Self::STORAGE_OFFSET));
520 }
521 Ok(slab_ptr)
522 }
523
524 unsafe fn dealloc_slab(slab_ptr: NonNull<SlabHeader>) {
528 let layout = Self::slab_layout();
529 unsafe {
531 drop_in_place(slab_ptr.as_ptr());
532 kalloc::dealloc(slab_ptr.cast::<u8>().as_ptr(), layout);
533 }
534 }
535
536 pub fn alloc_raw(&self) -> Result<NonNull<T>, AllocError> {
538 lock!(let guard = self.lock_mu());
539 let mut fields = guard.fields_mut();
540
541 if let Some(entry_ptr) = fields.free_list.pop_front() {
543 fields.record_allocation();
544 return Ok(entry_ptr.cast::<T>());
545 }
546
547 if let Some(active_slab) = fields.slab_list.front_mut() {
549 if let Some(mem) = active_slab.allocate(Self::ALLOC_SIZE, SLAB_SIZE) {
550 let ptr = mem.cast::<T>();
551 fields.record_allocation();
552 return Ok(ptr);
553 }
554 }
555
556 if *fields.slab_count < self.max_slabs {
558 let mut slab_ptr = Self::alloc_slab()?;
559 *fields.slab_count += 1;
560 unsafe {
562 fields.slab_list.push_front_raw(slab_ptr);
563 }
564
565 let active_slab = unsafe { slab_ptr.as_mut() };
567 let mem = active_slab.allocate(Self::ALLOC_SIZE, SLAB_SIZE).unwrap();
568 let ptr = mem.cast::<T>();
569 fields.record_allocation();
570 return Ok(ptr);
571 }
572
573 Err(AllocError)
574 }
575
576 pub unsafe fn return_to_free_list(&self, ptr: NonNull<T>) {
583 let entry_ptr = ptr.cast::<FreeListEntry>();
584 unsafe {
586 write(entry_ptr.as_ptr(), FreeListEntry { node: SinglyLinkedListNode::new() });
587 }
588
589 lock!(let guard = self.lock_mu());
590 let mut fields = guard.fields_mut();
591 unsafe {
593 fields.free_list.push_front_raw(entry_ptr);
594 }
595 fields.record_deallocation();
596 }
597
598 pub fn new_unique(&self, value: T) -> Result<UniquePtr<T>, AllocError>
600 where
601 T: Recyclable + InstancedSlabAllocated<L, SLAB_SIZE, TRACK_OBJECT_COUNT>,
602 {
603 let ptr = self.alloc_raw()?;
604 unsafe {
606 write(ptr.as_ptr(), value);
607 (&mut *ptr.as_ptr()).set_slab_origin(NonNull::from(self));
608 Ok(UniquePtr::from_raw(ptr.as_ptr()))
609 }
610 }
611
612 pub fn new_ref(&self, value: T) -> Result<RefPtr<T>, AllocError>
615 where
616 T: HasRefCount + Recyclable + InstancedSlabAllocated<L, SLAB_SIZE, TRACK_OBJECT_COUNT>,
617 {
618 let ptr = self.alloc_raw()?;
619 unsafe {
621 write(ptr.as_ptr(), value);
622 (&mut *ptr.as_ptr()).set_slab_origin(NonNull::from(self));
623 (*ptr.as_ptr()).ref_count().adopt();
624 Ok(RefPtr::from_raw(ptr.as_ptr()))
625 }
626 }
627
628 pub unsafe fn delete(&self, ptr: NonNull<T>) {
634 unsafe {
636 drop_in_place(ptr.as_ptr());
637 self.return_to_free_list(ptr);
638 }
639 }
640
641 pub fn obj_count(&self) -> usize {
643 const {
644 assert!(TRACK_OBJECT_COUNT, "Error accessing obj_count: Object counter not enabled");
645 }
646 lock!(let guard = self.lock_mu());
647 *guard.fields().obj_count
648 }
649
650 pub fn max_obj_count(&self) -> usize {
653 const {
654 assert!(
655 TRACK_OBJECT_COUNT,
656 "Error accessing max_obj_count: Object counter not enabled"
657 );
658 }
659 lock!(let guard = self.lock_mu());
660 *guard.fields().max_obj_count
661 }
662
663 pub fn slab_count(&self) -> usize {
665 lock!(let guard = self.lock_mu());
666 *guard.fields().slab_count
667 }
668
669 pub fn max_slabs(&self) -> usize {
671 self.max_slabs
672 }
673
674 pub fn reset_max_obj_count(&self) {
676 const {
677 assert!(
678 TRACK_OBJECT_COUNT,
679 "Error performing reset_max_obj_count: Object counter not enabled"
680 );
681 }
682 lock!(let guard = self.lock_mu());
683 let fields = guard.fields_mut();
684 *fields.max_obj_count = *fields.obj_count;
685 }
686}
687
688impl<'b, T, L: RawLock, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool>
689 SlabAllocatorMuFieldsMut<'b, T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>
690{
691 #[inline(always)]
692 fn record_allocation(&mut self) {
693 if TRACK_OBJECT_COUNT {
694 *self.obj_count += 1;
695 *self.max_obj_count = max(*self.max_obj_count, *self.obj_count);
696 }
697 }
698
699 #[inline(always)]
700 fn record_deallocation(&mut self) {
701 if TRACK_OBJECT_COUNT {
702 *self.obj_count -= 1;
703 }
704 }
705}
706
707#[pinned_drop]
708impl<T, L: RawLock, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool> PinnedDrop
709 for SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>
710{
711 fn drop(self: Pin<&mut Self>) {
712 let me = unsafe { self.get_unchecked_mut() };
714 let free_list = me.free_list.get_inner_mut();
715 let slab_list = me.slab_list.get_inner_mut();
716 #[cfg(debug_assertions)]
718 {
719 let obj_count = me.obj_count.get_inner_mut();
720 if TRACK_OBJECT_COUNT {
721 debug_assert_eq!(
722 *obj_count, 0,
723 "SlabAllocator destroyed with outstanding allocations!"
724 );
725 } else {
726 let free_list_size = free_list.iter().count();
728 let mut allocated_count = 0;
729 for slab in slab_list.iter() {
730 let bytes_used = slab.bytes_used - Self::STORAGE_OFFSET;
731 allocated_count += bytes_used / Self::ALLOC_SIZE;
732 }
733 debug_assert_eq!(
734 free_list_size, allocated_count,
735 "SlabAllocator destroyed with outstanding allocations!"
736 );
737 }
738 }
739
740 free_list.clear();
743
744 while let Some(slab_ptr) = slab_list.pop_front() {
745 unsafe {
748 Self::dealloc_slab(slab_ptr);
749 }
750 }
751 }
752}
753
754#[cfg(test)]
755mod tests {
756 use super::*;
757 use crate::RefCounted;
758 use alloc::vec::Vec;
759 use core::cmp::min;
760 use core::ptr::write;
761 use core::sync::atomic::{AtomicUsize, Ordering};
762 use lock_api::RawMutex as _;
763 use pin_init::stack_pin_init;
764 extern crate alloc;
765
766 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
767 enum ConstructType {
768 Default,
769 LvalueRef,
770 RvalueRef,
771 LThenRRef,
772 }
773
774 trait TestConstructors {
775 fn new_default() -> Self;
776 fn new_lvalue(val: usize) -> Self;
777 fn new_rvalue(val: usize) -> Self;
778 fn new_l_then_r(a: usize, b: usize) -> Self;
779 fn ctype(&self) -> ConstructType;
780 }
781
782 trait MaybeTracked {
785 fn maybe_obj_count(&self) -> usize;
786 fn maybe_max_obj_count(&self) -> usize;
787 fn maybe_reset_max_obj_count(&self);
788 }
789
790 impl<T, L: RawLock, const SLAB_SIZE: usize> MaybeTracked for SlabAllocator<T, L, SLAB_SIZE, true> {
791 fn maybe_obj_count(&self) -> usize {
792 self.obj_count()
793 }
794 fn maybe_max_obj_count(&self) -> usize {
795 self.max_obj_count()
796 }
797 fn maybe_reset_max_obj_count(&self) {
798 self.reset_max_obj_count()
799 }
800 }
801
802 impl<T, L: RawLock, const SLAB_SIZE: usize> MaybeTracked for SlabAllocator<T, L, SLAB_SIZE, false> {
803 fn maybe_obj_count(&self) -> usize {
804 0
805 }
806 fn maybe_max_obj_count(&self) -> usize {
807 0
808 }
809 fn maybe_reset_max_obj_count(&self) {}
810 }
811
812 fn run_instanced_unique_test<T, L, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool>(
814 allocated_obj_count: &'static AtomicUsize,
815 max_slabs: usize,
816 test_allocs: usize,
817 ) where
818 T: Recyclable
819 + InstancedSlabAllocated<L, SLAB_SIZE, TRACK_OBJECT_COUNT>
820 + TestConstructors
821 + 'static,
822 L: RawLock + 'static,
823 SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>: MaybeTracked,
824 {
825 allocated_obj_count.store(0, Ordering::SeqCst);
826 let init = SlabAllocator::<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>::init(max_slabs);
827 stack_pin_init!(let allocator = init);
828
829 assert_eq!(allocator.slab_count(), 0);
830 if TRACK_OBJECT_COUNT {
831 assert_eq!(allocator.maybe_obj_count(), 0);
832 assert_eq!(allocator.maybe_max_obj_count(), 0);
833 }
834
835 let max_allocs =
836 SlabAllocator::<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>::ALLOCS_PER_SLAB * max_slabs;
837 let mut ref_list = Vec::new();
838
839 for i in 0..test_allocs {
840 let expected_count = min(i, max_allocs);
841 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), expected_count);
842
843 if TRACK_OBJECT_COUNT {
844 assert_eq!(allocator.maybe_obj_count(), expected_count);
845 assert_eq!(allocator.maybe_max_obj_count(), expected_count);
846 }
847
848 let val = match i % 4 {
849 0 => T::new_default(),
850 1 => T::new_lvalue(i),
851 2 => T::new_rvalue(i),
852 _ => T::new_l_then_r(i, i),
853 };
854
855 let ptr = allocator.new_unique(val);
856
857 if i < max_allocs {
858 let obj = ptr.expect("Allocation failed when it should not have!");
859 ref_list.push(obj);
860 } else {
861 assert!(ptr.is_err(), "Allocation succeeded when it should not have!");
862 }
863
864 let expected_count_after = min(i + 1, max_allocs);
865 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), expected_count_after);
866
867 if TRACK_OBJECT_COUNT {
868 assert_eq!(allocator.maybe_obj_count(), expected_count_after);
869 assert_eq!(allocator.maybe_max_obj_count(), expected_count_after);
870 }
871 }
872
873 let mut max_obj_count = allocated_obj_count.load(Ordering::SeqCst);
874 let total_allocated = ref_list.len();
875
876 for (i, obj) in ref_list.into_iter().enumerate() {
877 let current_expected = total_allocated - i;
878 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), current_expected);
879
880 if TRACK_OBJECT_COUNT {
881 assert_eq!(allocator.maybe_obj_count(), current_expected);
882 assert_eq!(allocator.maybe_max_obj_count(), max_obj_count);
883 }
884
885 match i % 4 {
886 0 => assert_eq!(obj.ctype(), ConstructType::Default),
887 1 => assert_eq!(obj.ctype(), ConstructType::LvalueRef),
888 2 => assert_eq!(obj.ctype(), ConstructType::RvalueRef),
889 _ => assert_eq!(obj.ctype(), ConstructType::LThenRRef),
890 }
891
892 drop(obj); if TRACK_OBJECT_COUNT {
895 if i % 2 == 1 {
896 allocator.maybe_reset_max_obj_count();
897 max_obj_count = allocated_obj_count.load(Ordering::SeqCst);
898 }
899 assert_eq!(allocator.maybe_max_obj_count(), max_obj_count);
900 }
901 }
902
903 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), 0);
904 if TRACK_OBJECT_COUNT {
905 assert_eq!(allocator.maybe_obj_count(), 0);
906 assert_eq!(allocator.maybe_max_obj_count(), total_allocated % 2);
907 allocator.maybe_reset_max_obj_count();
908 assert_eq!(allocator.maybe_max_obj_count(), 0);
909 }
910 }
911
912 fn run_instanced_ref_test<T, L, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool>(
913 allocated_obj_count: &'static AtomicUsize,
914 max_slabs: usize,
915 test_allocs: usize,
916 ) where
917 T: HasRefCount
918 + Recyclable
919 + InstancedSlabAllocated<L, SLAB_SIZE, TRACK_OBJECT_COUNT>
920 + TestConstructors
921 + 'static,
922 L: RawLock + 'static,
923 SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>: MaybeTracked,
924 {
925 allocated_obj_count.store(0, Ordering::SeqCst);
926 let init = SlabAllocator::<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>::init(max_slabs);
927 stack_pin_init!(let allocator = init);
928
929 assert_eq!(allocator.slab_count(), 0);
930 if TRACK_OBJECT_COUNT {
931 assert_eq!(allocator.maybe_obj_count(), 0);
932 assert_eq!(allocator.maybe_max_obj_count(), 0);
933 }
934
935 let max_allocs =
936 SlabAllocator::<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>::ALLOCS_PER_SLAB * max_slabs;
937 let mut ref_list = Vec::new();
938
939 for i in 0..test_allocs {
940 let expected_count = min(i, max_allocs);
941 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), expected_count);
942
943 if TRACK_OBJECT_COUNT {
944 assert_eq!(allocator.maybe_obj_count(), expected_count);
945 assert_eq!(allocator.maybe_max_obj_count(), expected_count);
946 }
947
948 let val = match i % 4 {
949 0 => T::new_default(),
950 1 => T::new_lvalue(i),
951 2 => T::new_rvalue(i),
952 _ => T::new_l_then_r(i, i),
953 };
954
955 let ptr = allocator.new_ref(val);
956
957 if i < max_allocs {
958 let obj = ptr.expect("Allocation failed when it should not have!");
959 ref_list.push(obj);
960 } else {
961 assert!(ptr.is_err(), "Allocation succeeded when it should not have!");
962 }
963
964 let expected_count_after = min(i + 1, max_allocs);
965 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), expected_count_after);
966
967 if TRACK_OBJECT_COUNT {
968 assert_eq!(allocator.maybe_obj_count(), expected_count_after);
969 assert_eq!(allocator.maybe_max_obj_count(), expected_count_after);
970 }
971 }
972
973 let mut max_obj_count = allocated_obj_count.load(Ordering::SeqCst);
974 let total_allocated = ref_list.len();
975
976 for (i, obj) in ref_list.into_iter().enumerate() {
977 let current_expected = total_allocated - i;
978 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), current_expected);
979
980 if TRACK_OBJECT_COUNT {
981 assert_eq!(allocator.maybe_obj_count(), current_expected);
982 assert_eq!(allocator.maybe_max_obj_count(), max_obj_count);
983 }
984
985 match i % 4 {
986 0 => assert_eq!(obj.ctype(), ConstructType::Default),
987 1 => assert_eq!(obj.ctype(), ConstructType::LvalueRef),
988 2 => assert_eq!(obj.ctype(), ConstructType::RvalueRef),
989 _ => assert_eq!(obj.ctype(), ConstructType::LThenRRef),
990 }
991
992 {
994 let _clone = obj.clone();
995 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), current_expected);
996 }
997
998 drop(obj); if TRACK_OBJECT_COUNT {
1001 if i % 2 == 1 {
1002 allocator.maybe_reset_max_obj_count();
1003 max_obj_count = allocated_obj_count.load(Ordering::SeqCst);
1004 }
1005 assert_eq!(allocator.maybe_max_obj_count(), max_obj_count);
1006 }
1007 }
1008
1009 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), 0);
1010 if TRACK_OBJECT_COUNT {
1011 assert_eq!(allocator.maybe_obj_count(), 0);
1012 assert_eq!(allocator.maybe_max_obj_count(), total_allocated % 2);
1013 allocator.maybe_reset_max_obj_count();
1014 assert_eq!(allocator.maybe_max_obj_count(), 0);
1015 }
1016 }
1017
1018 fn run_unmanaged_test<T, L, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool>(
1019 allocated_obj_count: &'static AtomicUsize,
1020 max_slabs: usize,
1021 test_allocs: usize,
1022 ) where
1023 T: TestConstructors + 'static,
1024 L: RawLock + 'static,
1025 SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>: MaybeTracked,
1026 {
1027 allocated_obj_count.store(0, Ordering::SeqCst);
1028 let init = SlabAllocator::<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>::init(max_slabs);
1029 stack_pin_init!(let allocator = init);
1030
1031 assert_eq!(allocator.slab_count(), 0);
1032 if TRACK_OBJECT_COUNT {
1033 assert_eq!(allocator.maybe_obj_count(), 0);
1034 assert_eq!(allocator.maybe_max_obj_count(), 0);
1035 }
1036
1037 let max_allocs =
1038 SlabAllocator::<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>::ALLOCS_PER_SLAB * max_slabs;
1039 let mut ref_list = Vec::new();
1040
1041 for i in 0..test_allocs {
1042 let expected_count = min(i, max_allocs);
1043 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), expected_count);
1044
1045 if TRACK_OBJECT_COUNT {
1046 assert_eq!(allocator.maybe_obj_count(), expected_count);
1047 assert_eq!(allocator.maybe_max_obj_count(), expected_count);
1048 }
1049
1050 let ptr = allocator.alloc_raw();
1051
1052 if i < max_allocs {
1053 let p = ptr.expect("Allocation failed when it should not have!");
1054 unsafe {
1056 let val = match i % 4 {
1057 0 => T::new_default(),
1058 1 => T::new_lvalue(i),
1059 2 => T::new_rvalue(i),
1060 _ => T::new_l_then_r(i, i),
1061 };
1062 write(p.as_ptr(), val);
1063 }
1064 ref_list.push(p);
1065 } else {
1066 assert!(ptr.is_err(), "Allocation succeeded when it should not have!");
1067 }
1068
1069 let expected_count_after = min(i + 1, max_allocs);
1070 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), expected_count_after);
1071
1072 if TRACK_OBJECT_COUNT {
1073 assert_eq!(allocator.maybe_obj_count(), expected_count_after);
1074 assert_eq!(allocator.maybe_max_obj_count(), expected_count_after);
1075 }
1076 }
1077
1078 let mut max_obj_count = allocated_obj_count.load(Ordering::SeqCst);
1079 let total_allocated = ref_list.len();
1080
1081 for (i, p) in ref_list.into_iter().enumerate() {
1082 let current_expected = total_allocated - i;
1083 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), current_expected);
1084
1085 if TRACK_OBJECT_COUNT {
1086 assert_eq!(allocator.maybe_obj_count(), current_expected);
1087 assert_eq!(allocator.maybe_max_obj_count(), max_obj_count);
1088 }
1089
1090 unsafe {
1092 match i % 4 {
1093 0 => assert_eq!(p.as_ref().ctype(), ConstructType::Default),
1094 1 => assert_eq!(p.as_ref().ctype(), ConstructType::LvalueRef),
1095 2 => assert_eq!(p.as_ref().ctype(), ConstructType::RvalueRef),
1096 _ => assert_eq!(p.as_ref().ctype(), ConstructType::LThenRRef),
1097 }
1098
1099 allocator.delete(p);
1100 }
1101
1102 if TRACK_OBJECT_COUNT {
1103 if i % 2 == 1 {
1104 allocator.maybe_reset_max_obj_count();
1105 max_obj_count = allocated_obj_count.load(Ordering::SeqCst);
1106 }
1107 assert_eq!(allocator.maybe_max_obj_count(), max_obj_count);
1108 }
1109 }
1110
1111 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), 0);
1112 if TRACK_OBJECT_COUNT {
1113 assert_eq!(allocator.maybe_obj_count(), 0);
1114 assert_eq!(allocator.maybe_max_obj_count(), total_allocated % 2);
1115 allocator.maybe_reset_max_obj_count();
1116 assert_eq!(allocator.maybe_max_obj_count(), 0);
1117 }
1118 }
1119
1120 fn run_static_unique_test<T, L, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool>(
1121 allocated_obj_count: &'static AtomicUsize,
1122 allocator: &'static SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>,
1123 max_slabs: usize,
1124 test_allocs: usize,
1125 ) where
1126 T: Recyclable
1127 + StaticSlabAllocated<L, SLAB_SIZE, TRACK_OBJECT_COUNT>
1128 + TestConstructors
1129 + 'static,
1130 L: RawLock + 'static,
1131 SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>: MaybeTracked,
1132 {
1133 allocated_obj_count.store(0, Ordering::SeqCst);
1134
1135 if TRACK_OBJECT_COUNT {
1136 allocator.maybe_reset_max_obj_count();
1137 assert_eq!(allocator.maybe_obj_count(), 0);
1138 assert_eq!(allocator.maybe_max_obj_count(), 0);
1139 }
1140
1141 let max_allocs =
1142 SlabAllocator::<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>::ALLOCS_PER_SLAB * max_slabs;
1143 let mut ref_list = Vec::new();
1144
1145 for i in 0..test_allocs {
1146 let expected_count = min(i, max_allocs);
1147 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), expected_count);
1148
1149 if TRACK_OBJECT_COUNT {
1150 assert_eq!(allocator.maybe_obj_count(), expected_count);
1151 assert_eq!(allocator.maybe_max_obj_count(), expected_count);
1152 }
1153
1154 let val = match i % 4 {
1155 0 => T::new_default(),
1156 1 => T::new_lvalue(i),
1157 2 => T::new_rvalue(i),
1158 _ => T::new_l_then_r(i, i),
1159 };
1160
1161 let ptr = UniquePtr::try_new(val);
1162
1163 if i < max_allocs {
1164 let obj = ptr.expect("Allocation failed when it should not have!");
1165 ref_list.push(obj);
1166 } else {
1167 assert!(ptr.is_err(), "Allocation succeeded when it should not have!");
1168 }
1169
1170 let expected_count_after = min(i + 1, max_allocs);
1171 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), expected_count_after);
1172
1173 if TRACK_OBJECT_COUNT {
1174 assert_eq!(allocator.maybe_obj_count(), expected_count_after);
1175 assert_eq!(allocator.maybe_max_obj_count(), expected_count_after);
1176 }
1177 }
1178
1179 let mut max_obj_count = allocated_obj_count.load(Ordering::SeqCst);
1180 let total_allocated = ref_list.len();
1181
1182 for (i, obj) in ref_list.into_iter().enumerate() {
1183 let current_expected = total_allocated - i;
1184 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), current_expected);
1185
1186 if TRACK_OBJECT_COUNT {
1187 assert_eq!(allocator.maybe_obj_count(), current_expected);
1188 assert_eq!(allocator.maybe_max_obj_count(), max_obj_count);
1189 }
1190
1191 match i % 4 {
1192 0 => assert_eq!(obj.ctype(), ConstructType::Default),
1193 1 => assert_eq!(obj.ctype(), ConstructType::LvalueRef),
1194 2 => assert_eq!(obj.ctype(), ConstructType::RvalueRef),
1195 _ => assert_eq!(obj.ctype(), ConstructType::LThenRRef),
1196 }
1197
1198 drop(obj); if TRACK_OBJECT_COUNT {
1201 if i % 2 == 1 {
1202 allocator.maybe_reset_max_obj_count();
1203 max_obj_count = allocated_obj_count.load(Ordering::SeqCst);
1204 }
1205 assert_eq!(allocator.maybe_max_obj_count(), max_obj_count);
1206 }
1207 }
1208
1209 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), 0);
1210 if TRACK_OBJECT_COUNT {
1211 assert_eq!(allocator.maybe_obj_count(), 0);
1212 assert_eq!(allocator.maybe_max_obj_count(), total_allocated % 2);
1213 allocator.maybe_reset_max_obj_count();
1214 assert_eq!(allocator.maybe_max_obj_count(), 0);
1215 }
1216 }
1217
1218 fn run_static_ref_test<T, L, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool>(
1219 allocated_obj_count: &'static AtomicUsize,
1220 allocator: &'static SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>,
1221 max_slabs: usize,
1222 test_allocs: usize,
1223 ) where
1224 T: HasRefCount
1225 + Recyclable
1226 + StaticSlabAllocated<L, SLAB_SIZE, TRACK_OBJECT_COUNT>
1227 + TestConstructors
1228 + 'static,
1229 L: RawLock + 'static,
1230 SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>: MaybeTracked,
1231 {
1232 allocated_obj_count.store(0, Ordering::SeqCst);
1233
1234 if TRACK_OBJECT_COUNT {
1235 allocator.maybe_reset_max_obj_count();
1236 assert_eq!(allocator.maybe_obj_count(), 0);
1237 assert_eq!(allocator.maybe_max_obj_count(), 0);
1238 }
1239
1240 let max_allocs =
1241 SlabAllocator::<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>::ALLOCS_PER_SLAB * max_slabs;
1242 let mut ref_list = Vec::new();
1243
1244 for i in 0..test_allocs {
1245 let expected_count = min(i, max_allocs);
1246 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), expected_count);
1247
1248 if TRACK_OBJECT_COUNT {
1249 assert_eq!(allocator.maybe_obj_count(), expected_count);
1250 assert_eq!(allocator.maybe_max_obj_count(), expected_count);
1251 }
1252
1253 let val = match i % 4 {
1254 0 => T::new_default(),
1255 1 => T::new_lvalue(i),
1256 2 => T::new_rvalue(i),
1257 _ => T::new_l_then_r(i, i),
1258 };
1259
1260 let ptr = unsafe { RefPtr::try_new(val) };
1262
1263 if i < max_allocs {
1264 let obj = ptr.expect("Allocation failed when it should not have!");
1265 ref_list.push(obj);
1266 } else {
1267 assert!(ptr.is_err(), "Allocation succeeded when it should not have!");
1268 }
1269
1270 let expected_count_after = min(i + 1, max_allocs);
1271 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), expected_count_after);
1272
1273 if TRACK_OBJECT_COUNT {
1274 assert_eq!(allocator.maybe_obj_count(), expected_count_after);
1275 assert_eq!(allocator.maybe_max_obj_count(), expected_count_after);
1276 }
1277 }
1278
1279 let mut max_obj_count = allocated_obj_count.load(Ordering::SeqCst);
1280 let total_allocated = ref_list.len();
1281
1282 for (i, obj) in ref_list.into_iter().enumerate() {
1283 let current_expected = total_allocated - i;
1284 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), current_expected);
1285
1286 if TRACK_OBJECT_COUNT {
1287 assert_eq!(allocator.maybe_obj_count(), current_expected);
1288 assert_eq!(allocator.maybe_max_obj_count(), max_obj_count);
1289 }
1290
1291 match i % 4 {
1292 0 => assert_eq!(obj.ctype(), ConstructType::Default),
1293 1 => assert_eq!(obj.ctype(), ConstructType::LvalueRef),
1294 2 => assert_eq!(obj.ctype(), ConstructType::RvalueRef),
1295 _ => assert_eq!(obj.ctype(), ConstructType::LThenRRef),
1296 }
1297
1298 {
1300 let _clone = obj.clone();
1301 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), current_expected);
1302 }
1303
1304 drop(obj); if TRACK_OBJECT_COUNT {
1307 if i % 2 == 1 {
1308 allocator.maybe_reset_max_obj_count();
1309 max_obj_count = allocated_obj_count.load(Ordering::SeqCst);
1310 }
1311 assert_eq!(allocator.maybe_max_obj_count(), max_obj_count);
1312 }
1313 }
1314
1315 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), 0);
1316 if TRACK_OBJECT_COUNT {
1317 assert_eq!(allocator.maybe_obj_count(), 0);
1318 assert_eq!(allocator.maybe_max_obj_count(), total_allocated % 2);
1319 allocator.maybe_reset_max_obj_count();
1320 assert_eq!(allocator.maybe_max_obj_count(), 0);
1321 }
1322 }
1323
1324 fn run_static_unmanaged_test<T, L, const SLAB_SIZE: usize, const TRACK_OBJECT_COUNT: bool>(
1325 allocated_obj_count: &'static AtomicUsize,
1326 allocator: &'static SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>,
1327 max_slabs: usize,
1328 test_allocs: usize,
1329 ) where
1330 T: TestConstructors + 'static,
1331 L: RawLock + 'static,
1332 SlabAllocator<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>: MaybeTracked,
1333 {
1334 allocated_obj_count.store(0, Ordering::SeqCst);
1335
1336 if TRACK_OBJECT_COUNT {
1337 allocator.maybe_reset_max_obj_count();
1338 assert_eq!(allocator.maybe_obj_count(), 0);
1339 assert_eq!(allocator.maybe_max_obj_count(), 0);
1340 }
1341
1342 let max_allocs =
1343 SlabAllocator::<T, L, SLAB_SIZE, TRACK_OBJECT_COUNT>::ALLOCS_PER_SLAB * max_slabs;
1344 let mut ref_list = Vec::new();
1345
1346 for i in 0..test_allocs {
1347 let expected_count = min(i, max_allocs);
1348 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), expected_count);
1349
1350 if TRACK_OBJECT_COUNT {
1351 assert_eq!(allocator.maybe_obj_count(), expected_count);
1352 assert_eq!(allocator.maybe_max_obj_count(), expected_count);
1353 }
1354
1355 let ptr = allocator.alloc_raw();
1356
1357 if i < max_allocs {
1358 let p = ptr.expect("Allocation failed when it should not have!");
1359 unsafe {
1361 let val = match i % 4 {
1362 0 => T::new_default(),
1363 1 => T::new_lvalue(i),
1364 2 => T::new_rvalue(i),
1365 _ => T::new_l_then_r(i, i),
1366 };
1367 write(p.as_ptr(), val);
1368 }
1369 ref_list.push(p);
1370 } else {
1371 assert!(ptr.is_err(), "Allocation succeeded when it should not have!");
1372 }
1373
1374 let expected_count_after = min(i + 1, max_allocs);
1375 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), expected_count_after);
1376
1377 if TRACK_OBJECT_COUNT {
1378 assert_eq!(allocator.maybe_obj_count(), expected_count_after);
1379 assert_eq!(allocator.maybe_max_obj_count(), expected_count_after);
1380 }
1381 }
1382
1383 let mut max_obj_count = allocated_obj_count.load(Ordering::SeqCst);
1384 let total_allocated = ref_list.len();
1385
1386 for (i, p) in ref_list.into_iter().enumerate() {
1387 let current_expected = total_allocated - i;
1388 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), current_expected);
1389
1390 if TRACK_OBJECT_COUNT {
1391 assert_eq!(allocator.maybe_obj_count(), current_expected);
1392 assert_eq!(allocator.maybe_max_obj_count(), max_obj_count);
1393 }
1394
1395 unsafe {
1397 match i % 4 {
1398 0 => assert_eq!(p.as_ref().ctype(), ConstructType::Default),
1399 1 => assert_eq!(p.as_ref().ctype(), ConstructType::LvalueRef),
1400 2 => assert_eq!(p.as_ref().ctype(), ConstructType::RvalueRef),
1401 _ => assert_eq!(p.as_ref().ctype(), ConstructType::LThenRRef),
1402 }
1403
1404 allocator.delete(p);
1405 }
1406
1407 if TRACK_OBJECT_COUNT {
1408 if i % 2 == 1 {
1409 allocator.maybe_reset_max_obj_count();
1410 max_obj_count = allocated_obj_count.load(Ordering::SeqCst);
1411 }
1412 assert_eq!(allocator.maybe_max_obj_count(), max_obj_count);
1413 }
1414 }
1415
1416 assert_eq!(allocated_obj_count.load(Ordering::SeqCst), 0);
1417 if TRACK_OBJECT_COUNT {
1418 assert_eq!(allocator.maybe_obj_count(), 0);
1419 assert_eq!(allocator.maybe_max_obj_count(), total_allocated % 2);
1420 allocator.maybe_reset_max_obj_count();
1421 assert_eq!(allocator.maybe_max_obj_count(), 0);
1422 }
1423 }
1424
1425 macro_rules! define_instanced_unique_test {
1426 ($name:ident, $lock:ty, $lock_init:expr, $slab_size:expr, $track_obj_count:expr, $max_slabs:expr) => {
1427 #[test]
1428 fn $name() {
1429 static ALLOCATED_OBJ_COUNT: AtomicUsize = AtomicUsize::new(0);
1430 struct Obj {
1431 ctype: ConstructType,
1432 slab_origin: SlabOrigin<Obj, $lock, $slab_size, $track_obj_count>,
1433 _payload: [u8; 13],
1434 }
1435 impl TestConstructors for Obj {
1436 fn new_default() -> Self {
1437 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1438 Self {
1439 ctype: ConstructType::Default,
1440 slab_origin: SlabOrigin::new(),
1441 _payload: [0; 13],
1442 }
1443 }
1444 fn new_lvalue(_val: usize) -> Self {
1445 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1446 Self {
1447 ctype: ConstructType::LvalueRef,
1448 slab_origin: SlabOrigin::new(),
1449 _payload: [0; 13],
1450 }
1451 }
1452 fn new_rvalue(_val: usize) -> Self {
1453 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1454 Self {
1455 ctype: ConstructType::RvalueRef,
1456 slab_origin: SlabOrigin::new(),
1457 _payload: [0; 13],
1458 }
1459 }
1460 fn new_l_then_r(_a: usize, _b: usize) -> Self {
1461 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1462 Self {
1463 ctype: ConstructType::LThenRRef,
1464 slab_origin: SlabOrigin::new(),
1465 _payload: [0; 13],
1466 }
1467 }
1468 fn ctype(&self) -> ConstructType {
1469 self.ctype
1470 }
1471 }
1472 impl Drop for Obj {
1473 fn drop(&mut self) {
1474 ALLOCATED_OBJ_COUNT.fetch_sub(1, Ordering::SeqCst);
1475 }
1476 }
1477 crate::impl_instanced_slab_allocatable!(Obj, $lock, $slab_size, $track_obj_count);
1478
1479 let max_allocs =
1480 SlabAllocator::<Obj, $lock, $slab_size, $track_obj_count>::ALLOCS_PER_SLAB
1481 * $max_slabs;
1482
1483 run_instanced_unique_test::<Obj, $lock, $slab_size, $track_obj_count>(
1484 &ALLOCATED_OBJ_COUNT,
1485 $max_slabs,
1486 1,
1487 );
1488 run_instanced_unique_test::<Obj, $lock, $slab_size, $track_obj_count>(
1489 &ALLOCATED_OBJ_COUNT,
1490 $max_slabs,
1491 max_allocs / 2,
1492 );
1493 run_instanced_unique_test::<Obj, $lock, $slab_size, $track_obj_count>(
1494 &ALLOCATED_OBJ_COUNT,
1495 $max_slabs,
1496 max_allocs + 4,
1497 );
1498 }
1499 };
1500 }
1501
1502 macro_rules! define_instanced_ref_test {
1503 ($name:ident, $lock:ty, $lock_init:expr, $slab_size:expr, $track_obj_count:expr, $max_slabs:expr) => {
1504 #[test]
1505 fn $name() {
1506 static ALLOCATED_OBJ_COUNT: AtomicUsize = AtomicUsize::new(0);
1507 #[crate::ref_counted]
1508 #[repr(C)]
1509 struct Obj {
1510 ctype: ConstructType,
1511 slab_origin: SlabOrigin<Obj, $lock, $slab_size, $track_obj_count>,
1512 _payload: [u8; 13],
1513 }
1514 impl TestConstructors for Obj {
1515 fn new_default() -> Self {
1516 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1517 Self {
1518 ref_count: RefCounted::new(),
1519 __fbl_ref_counted_guard: (),
1520 ctype: ConstructType::Default,
1521 slab_origin: SlabOrigin::new(),
1522 _payload: [0; 13],
1523 }
1524 }
1525 fn new_lvalue(_val: usize) -> Self {
1526 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1527 Self {
1528 ref_count: RefCounted::new(),
1529 __fbl_ref_counted_guard: (),
1530 ctype: ConstructType::LvalueRef,
1531 slab_origin: SlabOrigin::new(),
1532 _payload: [0; 13],
1533 }
1534 }
1535 fn new_rvalue(_val: usize) -> Self {
1536 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1537 Self {
1538 ref_count: RefCounted::new(),
1539 __fbl_ref_counted_guard: (),
1540 ctype: ConstructType::RvalueRef,
1541 slab_origin: SlabOrigin::new(),
1542 _payload: [0; 13],
1543 }
1544 }
1545 fn new_l_then_r(_a: usize, _b: usize) -> Self {
1546 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1547 Self {
1548 ref_count: RefCounted::new(),
1549 __fbl_ref_counted_guard: (),
1550 ctype: ConstructType::LThenRRef,
1551 slab_origin: SlabOrigin::new(),
1552 _payload: [0; 13],
1553 }
1554 }
1555 fn ctype(&self) -> ConstructType {
1556 self.ctype
1557 }
1558 }
1559 impl Drop for Obj {
1560 fn drop(&mut self) {
1561 ALLOCATED_OBJ_COUNT.fetch_sub(1, Ordering::SeqCst);
1562 }
1563 }
1564 crate::impl_instanced_slab_allocatable!(Obj, $lock, $slab_size, $track_obj_count);
1565
1566 let max_allocs =
1567 SlabAllocator::<Obj, $lock, $slab_size, $track_obj_count>::ALLOCS_PER_SLAB
1568 * $max_slabs;
1569
1570 run_instanced_ref_test::<Obj, $lock, $slab_size, $track_obj_count>(
1571 &ALLOCATED_OBJ_COUNT,
1572 $max_slabs,
1573 1,
1574 );
1575 run_instanced_ref_test::<Obj, $lock, $slab_size, $track_obj_count>(
1576 &ALLOCATED_OBJ_COUNT,
1577 $max_slabs,
1578 max_allocs / 2,
1579 );
1580 run_instanced_ref_test::<Obj, $lock, $slab_size, $track_obj_count>(
1581 &ALLOCATED_OBJ_COUNT,
1582 $max_slabs,
1583 max_allocs + 4,
1584 );
1585 }
1586 };
1587 }
1588
1589 macro_rules! define_unmanaged_test {
1590 ($name:ident, $lock:ty, $lock_init:expr, $slab_size:expr, $track_obj_count:expr, $max_slabs:expr) => {
1591 #[test]
1592 fn $name() {
1593 static ALLOCATED_OBJ_COUNT: AtomicUsize = AtomicUsize::new(0);
1594 struct Obj {
1595 ctype: ConstructType,
1596 _payload: [u8; 13],
1597 }
1598 impl TestConstructors for Obj {
1599 fn new_default() -> Self {
1600 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1601 Self { ctype: ConstructType::Default, _payload: [0; 13] }
1602 }
1603 fn new_lvalue(_val: usize) -> Self {
1604 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1605 Self { ctype: ConstructType::LvalueRef, _payload: [0; 13] }
1606 }
1607 fn new_rvalue(_val: usize) -> Self {
1608 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1609 Self { ctype: ConstructType::RvalueRef, _payload: [0; 13] }
1610 }
1611 fn new_l_then_r(_a: usize, _b: usize) -> Self {
1612 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1613 Self { ctype: ConstructType::LThenRRef, _payload: [0; 13] }
1614 }
1615 fn ctype(&self) -> ConstructType {
1616 self.ctype
1617 }
1618 }
1619 impl Drop for Obj {
1620 fn drop(&mut self) {
1621 ALLOCATED_OBJ_COUNT.fetch_sub(1, Ordering::SeqCst);
1622 }
1623 }
1624
1625 let max_allocs =
1626 SlabAllocator::<Obj, $lock, $slab_size, $track_obj_count>::ALLOCS_PER_SLAB
1627 * $max_slabs;
1628
1629 run_unmanaged_test::<Obj, $lock, $slab_size, $track_obj_count>(
1630 &ALLOCATED_OBJ_COUNT,
1631 $max_slabs,
1632 1,
1633 );
1634 run_unmanaged_test::<Obj, $lock, $slab_size, $track_obj_count>(
1635 &ALLOCATED_OBJ_COUNT,
1636 $max_slabs,
1637 max_allocs / 2,
1638 );
1639 run_unmanaged_test::<Obj, $lock, $slab_size, $track_obj_count>(
1640 &ALLOCATED_OBJ_COUNT,
1641 $max_slabs,
1642 max_allocs + 4,
1643 );
1644 }
1645 };
1646 }
1647
1648 macro_rules! define_static_unique_test {
1649 ($name:ident, $lock:ty, $lock_init:expr, $slab_size:expr, $track_obj_count:expr, $max_slabs:expr) => {
1650 #[test]
1651 fn $name() {
1652 static ALLOCATED_OBJ_COUNT: AtomicUsize = AtomicUsize::new(0);
1653 struct Obj {
1654 ctype: ConstructType,
1655 _payload: [u8; 13],
1656 }
1657 impl TestConstructors for Obj {
1658 fn new_default() -> Self {
1659 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1660 Self { ctype: ConstructType::Default, _payload: [0; 13] }
1661 }
1662 fn new_lvalue(_val: usize) -> Self {
1663 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1664 Self { ctype: ConstructType::LvalueRef, _payload: [0; 13] }
1665 }
1666 fn new_rvalue(_val: usize) -> Self {
1667 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1668 Self { ctype: ConstructType::RvalueRef, _payload: [0; 13] }
1669 }
1670 fn new_l_then_r(_a: usize, _b: usize) -> Self {
1671 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1672 Self { ctype: ConstructType::LThenRRef, _payload: [0; 13] }
1673 }
1674 fn ctype(&self) -> ConstructType {
1675 self.ctype
1676 }
1677 }
1678 impl Drop for Obj {
1679 fn drop(&mut self) {
1680 ALLOCATED_OBJ_COUNT.fetch_sub(1, Ordering::SeqCst);
1681 }
1682 }
1683
1684 static ALLOCATOR: SlabAllocator<Obj, $lock, $slab_size, $track_obj_count> =
1685 SlabAllocator::<Obj, $lock, $slab_size, $track_obj_count>::const_new(
1686 $max_slabs, $lock_init,
1687 );
1688
1689 crate::impl_static_slab_allocatable!(
1690 Obj,
1691 $lock,
1692 $slab_size,
1693 ALLOCATOR,
1694 $track_obj_count
1695 );
1696
1697 let max_allocs =
1698 SlabAllocator::<Obj, $lock, $slab_size, $track_obj_count>::ALLOCS_PER_SLAB
1699 * $max_slabs;
1700
1701 run_static_unique_test::<Obj, $lock, $slab_size, $track_obj_count>(
1702 &ALLOCATED_OBJ_COUNT,
1703 &ALLOCATOR,
1704 $max_slabs,
1705 1,
1706 );
1707 run_static_unique_test::<Obj, $lock, $slab_size, $track_obj_count>(
1708 &ALLOCATED_OBJ_COUNT,
1709 &ALLOCATOR,
1710 $max_slabs,
1711 max_allocs / 2,
1712 );
1713 run_static_unique_test::<Obj, $lock, $slab_size, $track_obj_count>(
1714 &ALLOCATED_OBJ_COUNT,
1715 &ALLOCATOR,
1716 $max_slabs,
1717 max_allocs + 4,
1718 );
1719 }
1720 };
1721 }
1722
1723 macro_rules! define_static_ref_test {
1724 ($name:ident, $lock:ty, $lock_init:expr, $slab_size:expr, $track_obj_count:expr, $max_slabs:expr) => {
1725 #[test]
1726 fn $name() {
1727 static ALLOCATED_OBJ_COUNT: AtomicUsize = AtomicUsize::new(0);
1728 #[crate::ref_counted]
1729 #[repr(C)]
1730 struct Obj {
1731 ctype: ConstructType,
1732 _payload: [u8; 13],
1733 }
1734 impl TestConstructors for Obj {
1735 fn new_default() -> Self {
1736 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1737 Self {
1738 ref_count: RefCounted::new(),
1739 __fbl_ref_counted_guard: (),
1740 ctype: ConstructType::Default,
1741 _payload: [0; 13],
1742 }
1743 }
1744 fn new_lvalue(_val: usize) -> Self {
1745 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1746 Self {
1747 ref_count: RefCounted::new(),
1748 __fbl_ref_counted_guard: (),
1749 ctype: ConstructType::LvalueRef,
1750 _payload: [0; 13],
1751 }
1752 }
1753 fn new_rvalue(_val: usize) -> Self {
1754 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1755 Self {
1756 ref_count: RefCounted::new(),
1757 __fbl_ref_counted_guard: (),
1758 ctype: ConstructType::RvalueRef,
1759 _payload: [0; 13],
1760 }
1761 }
1762 fn new_l_then_r(_a: usize, _b: usize) -> Self {
1763 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1764 Self {
1765 ref_count: RefCounted::new(),
1766 __fbl_ref_counted_guard: (),
1767 ctype: ConstructType::LThenRRef,
1768 _payload: [0; 13],
1769 }
1770 }
1771 fn ctype(&self) -> ConstructType {
1772 self.ctype
1773 }
1774 }
1775 impl Drop for Obj {
1776 fn drop(&mut self) {
1777 ALLOCATED_OBJ_COUNT.fetch_sub(1, Ordering::SeqCst);
1778 }
1779 }
1780
1781 static ALLOCATOR: SlabAllocator<Obj, $lock, $slab_size, $track_obj_count> =
1782 SlabAllocator::<Obj, $lock, $slab_size, $track_obj_count>::const_new(
1783 $max_slabs, $lock_init,
1784 );
1785
1786 crate::impl_static_slab_allocatable!(
1787 Obj,
1788 $lock,
1789 $slab_size,
1790 ALLOCATOR,
1791 $track_obj_count
1792 );
1793
1794 let max_allocs =
1795 SlabAllocator::<Obj, $lock, $slab_size, $track_obj_count>::ALLOCS_PER_SLAB
1796 * $max_slabs;
1797
1798 {
1800 ALLOCATED_OBJ_COUNT.store(1, Ordering::SeqCst);
1801 let obj = crate::make_ref_counted!(Obj {
1802 ctype: ConstructType::Default,
1803 _payload: [0; 13],
1804 })
1805 .unwrap();
1806 assert_eq!(ALLOCATED_OBJ_COUNT.load(Ordering::SeqCst), 1);
1807 drop(obj);
1808 assert_eq!(ALLOCATED_OBJ_COUNT.load(Ordering::SeqCst), 0);
1809 }
1810
1811 run_static_ref_test::<Obj, $lock, $slab_size, $track_obj_count>(
1812 &ALLOCATED_OBJ_COUNT,
1813 &ALLOCATOR,
1814 $max_slabs,
1815 1,
1816 );
1817 run_static_ref_test::<Obj, $lock, $slab_size, $track_obj_count>(
1818 &ALLOCATED_OBJ_COUNT,
1819 &ALLOCATOR,
1820 $max_slabs,
1821 max_allocs / 2,
1822 );
1823 run_static_ref_test::<Obj, $lock, $slab_size, $track_obj_count>(
1824 &ALLOCATED_OBJ_COUNT,
1825 &ALLOCATOR,
1826 $max_slabs,
1827 max_allocs + 4,
1828 );
1829 }
1830 };
1831 }
1832
1833 macro_rules! define_static_unmanaged_test {
1834 ($name:ident, $lock:ty, $lock_init:expr, $slab_size:expr, $track_obj_count:expr, $max_slabs:expr) => {
1835 #[test]
1836 fn $name() {
1837 static ALLOCATED_OBJ_COUNT: AtomicUsize = AtomicUsize::new(0);
1838 struct Obj {
1839 ctype: ConstructType,
1840 _payload: [u8; 13],
1841 }
1842 impl TestConstructors for Obj {
1843 fn new_default() -> Self {
1844 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1845 Self { ctype: ConstructType::Default, _payload: [0; 13] }
1846 }
1847 fn new_lvalue(_val: usize) -> Self {
1848 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1849 Self { ctype: ConstructType::LvalueRef, _payload: [0; 13] }
1850 }
1851 fn new_rvalue(_val: usize) -> Self {
1852 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1853 Self { ctype: ConstructType::RvalueRef, _payload: [0; 13] }
1854 }
1855 fn new_l_then_r(_a: usize, _b: usize) -> Self {
1856 ALLOCATED_OBJ_COUNT.fetch_add(1, Ordering::SeqCst);
1857 Self { ctype: ConstructType::LThenRRef, _payload: [0; 13] }
1858 }
1859 fn ctype(&self) -> ConstructType {
1860 self.ctype
1861 }
1862 }
1863 impl Drop for Obj {
1864 fn drop(&mut self) {
1865 ALLOCATED_OBJ_COUNT.fetch_sub(1, Ordering::SeqCst);
1866 }
1867 }
1868
1869 static ALLOCATOR: SlabAllocator<Obj, $lock, $slab_size, $track_obj_count> =
1870 SlabAllocator::<Obj, $lock, $slab_size, $track_obj_count>::const_new(
1871 $max_slabs, $lock_init,
1872 );
1873
1874 let max_allocs =
1875 SlabAllocator::<Obj, $lock, $slab_size, $track_obj_count>::ALLOCS_PER_SLAB
1876 * $max_slabs;
1877
1878 run_static_unmanaged_test::<Obj, $lock, $slab_size, $track_obj_count>(
1879 &ALLOCATED_OBJ_COUNT,
1880 &ALLOCATOR,
1881 $max_slabs,
1882 1,
1883 );
1884 run_static_unmanaged_test::<Obj, $lock, $slab_size, $track_obj_count>(
1885 &ALLOCATED_OBJ_COUNT,
1886 &ALLOCATOR,
1887 $max_slabs,
1888 max_allocs / 2,
1889 );
1890 run_static_unmanaged_test::<Obj, $lock, $slab_size, $track_obj_count>(
1891 &ALLOCATED_OBJ_COUNT,
1892 &ALLOCATOR,
1893 $max_slabs,
1894 max_allocs + 4,
1895 );
1896 }
1897 };
1898 }
1899
1900 define_unmanaged_test!(unmanaged_single_slab_mutex, RawMutex, RawMutex::INIT, 1024, false, 1);
1901 define_unmanaged_test!(unmanaged_multi_slab_mutex, RawMutex, RawMutex::INIT, 1024, false, 4);
1902 define_instanced_unique_test!(
1903 unique_ptr_single_slab_mutex,
1904 RawMutex,
1905 RawMutex::INIT,
1906 1024,
1907 false,
1908 1
1909 );
1910 define_instanced_unique_test!(
1911 unique_ptr_multi_slab_mutex,
1912 RawMutex,
1913 RawMutex::INIT,
1914 1024,
1915 false,
1916 4
1917 );
1918 define_instanced_ref_test!(ref_ptr_single_slab_mutex, RawMutex, RawMutex::INIT, 1024, false, 1);
1919 define_instanced_ref_test!(ref_ptr_multi_slab_mutex, RawMutex, RawMutex::INIT, 1024, false, 4);
1920
1921 define_unmanaged_test!(
1923 counted_unmanaged_single_slab_mutex,
1924 RawMutex,
1925 RawMutex::INIT,
1926 1024,
1927 true,
1928 1
1929 );
1930 define_unmanaged_test!(
1931 counted_unmanaged_multi_slab_mutex,
1932 RawMutex,
1933 RawMutex::INIT,
1934 1024,
1935 true,
1936 4
1937 );
1938 define_instanced_unique_test!(
1939 counted_unique_ptr_single_slab_mutex,
1940 RawMutex,
1941 RawMutex::INIT,
1942 1024,
1943 true,
1944 1
1945 );
1946 define_instanced_unique_test!(
1947 counted_unique_ptr_multi_slab_mutex,
1948 RawMutex,
1949 RawMutex::INIT,
1950 1024,
1951 true,
1952 4
1953 );
1954 define_instanced_ref_test!(
1955 counted_ref_ptr_single_slab_mutex,
1956 RawMutex,
1957 RawMutex::INIT,
1958 1024,
1959 true,
1960 1
1961 );
1962 define_instanced_ref_test!(
1963 counted_ref_ptr_multi_slab_mutex,
1964 RawMutex,
1965 RawMutex::INIT,
1966 1024,
1967 true,
1968 4
1969 );
1970
1971 define_static_unmanaged_test!(static_unmanaged_mutex, RawMutex, RawMutex::INIT, 1024, false, 4);
1973 define_static_unique_test!(static_unique_ptr_mutex, RawMutex, RawMutex::INIT, 1024, false, 4);
1974 define_static_ref_test!(static_ref_ptr_mutex, RawMutex, RawMutex::INIT, 1024, false, 4);
1975
1976 define_static_unmanaged_test!(
1978 counted_static_unmanaged_mutex,
1979 RawMutex,
1980 RawMutex::INIT,
1981 1024,
1982 true,
1983 4
1984 );
1985 define_static_unique_test!(
1986 counted_static_unique_ptr_mutex,
1987 RawMutex,
1988 RawMutex::INIT,
1989 1024,
1990 true,
1991 4
1992 );
1993 define_static_ref_test!(counted_static_ref_ptr_mutex, RawMutex, RawMutex::INIT, 1024, true, 4);
1994
1995 #[test]
1996 fn test_constructor_statistics_fix() {
1997 let init = SlabAllocator::<TestObjectDummy, RawMutex, 1024, true>::init(1);
2000 stack_pin_init!(let allocator = init);
2001 allocator.preallocate().unwrap();
2002 assert_eq!(allocator.obj_count(), 0);
2003 assert_eq!(allocator.max_obj_count(), 0);
2004 }
2005
2006 #[cfg(debug_assertions)]
2007 #[test]
2008 #[should_panic(expected = "SlabAllocator destroyed with outstanding allocations!")]
2009 fn test_leak_detector_counted() {
2010 let init = SlabAllocator::<u32, RawMutex, 1024, true>::init(1);
2011 stack_pin_init!(let allocator = init);
2012 let _ptr = allocator.alloc_raw().unwrap();
2013 }
2014
2015 #[cfg(debug_assertions)]
2016 #[test]
2017 #[should_panic(expected = "SlabAllocator destroyed with outstanding allocations!")]
2018 fn test_leak_detector_uncounted() {
2019 let init = SlabAllocator::<u32, RawMutex, 1024, false>::init(1);
2020 stack_pin_init!(let allocator = init);
2021 let _ptr = allocator.alloc_raw().unwrap();
2022 }
2023
2024 struct TestObjectDummy {
2025 _val: u32,
2026 slab_origin: SlabOrigin<TestObjectDummy, RawMutex, 1024, true>,
2027 }
2028 crate::impl_instanced_slab_allocatable!(TestObjectDummy, RawMutex, 1024, true);
2029}