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