1use std::fmt;
2use std::iter::FusedIterator;
3use std::marker::PhantomData;
4use std::mem::MaybeUninit;
5use std::ptr::NonNull;
6use std::sync::{Arc, Mutex};
7
8use crate::{
9 ERR_POISONED_LOCK, PooledMut, RawOpaquePool, RawOpaquePoolIterator, RawOpaquePoolSend,
10};
11
12#[doc = include_str!("../../doc/snippets/managed_pool_lifetimes.md")]
18pub struct PinnedPool<T: Send + 'static> {
54 inner: Arc<Mutex<RawOpaquePoolSend>>,
67
68 _phantom: PhantomData<T>,
69}
70
71impl<T> PinnedPool<T>
72where
73 T: Send + 'static,
74{
75 #[must_use]
77 pub fn new() -> Self {
78 let inner = RawOpaquePool::with_layout_of::<T>();
79
80 let inner = unsafe { RawOpaquePoolSend::new(inner) };
82
83 Self {
84 inner: Arc::new(Mutex::new(inner)),
85 _phantom: PhantomData,
86 }
87 }
88
89 #[doc = include_str!("../../doc/snippets/pool_len.md")]
90 #[must_use]
91 #[inline]
92 pub fn len(&self) -> usize {
93 self.inner.lock().expect(ERR_POISONED_LOCK).len()
94 }
95
96 #[doc = include_str!("../../doc/snippets/pool_capacity.md")]
97 #[must_use]
98 #[inline]
99 pub fn capacity(&self) -> usize {
100 self.inner.lock().expect(ERR_POISONED_LOCK).capacity()
101 }
102
103 #[doc = include_str!("../../doc/snippets/pool_is_empty.md")]
104 #[must_use]
105 #[inline]
106 pub fn is_empty(&self) -> bool {
107 self.inner.lock().expect(ERR_POISONED_LOCK).is_empty()
108 }
109
110 #[doc = include_str!("../../doc/snippets/pool_reserve.md")]
111 #[inline]
112 pub fn reserve(&mut self, additional: usize) {
113 self.inner
114 .lock()
115 .expect(ERR_POISONED_LOCK)
116 .reserve(additional);
117 }
118
119 #[doc = include_str!("../../doc/snippets/pool_shrink_to_fit.md")]
120 #[inline]
121 pub fn shrink_to_fit(&mut self) {
122 self.inner.lock().expect(ERR_POISONED_LOCK).shrink_to_fit();
123 }
124
125 #[doc = include_str!("../../doc/snippets/pool_insert.md")]
126 #[inline]
153 #[must_use]
154 pub fn insert(&mut self, value: T) -> PooledMut<T> {
155 let inner = self.inner.lock().expect(ERR_POISONED_LOCK).insert(value);
156
157 PooledMut::new(inner, Arc::clone(&self.inner))
158 }
159
160 #[doc = include_str!("../../doc/snippets/pool_insert_with.md")]
161 #[doc = include_str!("../../doc/snippets/safety_closure_must_initialize_object.md")]
195 #[inline]
196 #[must_use]
197 pub unsafe fn insert_with<F>(&mut self, f: F) -> PooledMut<T>
198 where
199 F: FnOnce(&mut MaybeUninit<T>),
200 {
201 let inner = unsafe { self.inner.lock().expect(ERR_POISONED_LOCK).insert_with(f) };
203
204 PooledMut::new(inner, Arc::clone(&self.inner))
205 }
206
207 pub fn with_iter<F, R>(&self, f: F) -> R
245 where
246 F: FnOnce(PinnedPoolIterator<'_, T>) -> R,
247 {
248 let guard = self.inner.lock().expect(ERR_POISONED_LOCK);
249 let iter = PinnedPoolIterator::new(&guard);
250 f(iter)
251 }
252}
253
254impl<T> Clone for PinnedPool<T>
255where
256 T: Send,
257{
258 #[inline]
259 fn clone(&self) -> Self {
260 Self {
261 inner: Arc::clone(&self.inner),
262 _phantom: PhantomData,
263 }
264 }
265}
266
267impl<T> Default for PinnedPool<T>
268where
269 T: Send,
270{
271 fn default() -> Self {
272 Self::new()
273 }
274}
275
276impl<T> fmt::Debug for PinnedPool<T>
277where
278 T: Send,
279{
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 f.debug_struct("PinnedPool")
282 .field("inner", &self.inner)
283 .finish()
284 }
285}
286
287#[derive(Debug)]
297pub struct PinnedPoolIterator<'p, T: 'static> {
298 raw_iter: RawOpaquePoolIterator<'p>,
299 _phantom: PhantomData<T>,
300}
301
302impl<'p, T> PinnedPoolIterator<'p, T> {
303 fn new(pool: &'p RawOpaquePoolSend) -> Self {
304 Self {
305 raw_iter: pool.iter(),
306 _phantom: PhantomData,
307 }
308 }
309}
310
311impl<T> Iterator for PinnedPoolIterator<'_, T> {
312 type Item = NonNull<T>;
313
314 fn next(&mut self) -> Option<Self::Item> {
315 self.raw_iter.next().map(NonNull::cast::<T>)
316 }
317
318 fn size_hint(&self) -> (usize, Option<usize>) {
319 self.raw_iter.size_hint()
320 }
321}
322
323impl<T> DoubleEndedIterator for PinnedPoolIterator<'_, T> {
324 fn next_back(&mut self) -> Option<Self::Item> {
325 self.raw_iter.next_back().map(NonNull::cast::<T>)
326 }
327}
328
329impl<T> ExactSizeIterator for PinnedPoolIterator<'_, T> {
330 fn len(&self) -> usize {
331 self.raw_iter.len()
332 }
333}
334
335impl<T> FusedIterator for PinnedPoolIterator<'_, T> {}
336
337#[cfg(test)]
338mod tests {
339 use std::mem::MaybeUninit;
340
341 use super::*;
342
343 #[test]
344 fn new_pool_is_empty() {
345 let pool = PinnedPool::<u32>::new();
346
347 assert_eq!(pool.len(), 0);
348 assert!(pool.is_empty());
349 assert_eq!(pool.capacity(), 0);
350 }
351
352 #[test]
353 fn default_pool_is_empty() {
354 let pool: PinnedPool<String> = PinnedPool::default();
355
356 assert_eq!(pool.len(), 0);
357 assert!(pool.is_empty());
358 assert_eq!(pool.capacity(), 0);
359 }
360
361 #[test]
362 fn insert_and_length() {
363 let mut pool = PinnedPool::<u64>::new();
364
365 let _h1 = pool.insert(10);
366 assert_eq!(pool.len(), 1);
367 assert!(!pool.is_empty());
368
369 let _h2 = pool.insert(20);
370 assert_eq!(pool.len(), 2);
371 }
372
373 #[test]
374 fn capacity_grows_when_needed() {
375 let mut pool = PinnedPool::<u64>::new();
376
377 assert_eq!(pool.capacity(), 0);
378
379 let _handle = pool.insert(123_u64);
380
381 assert!(pool.capacity() > 0);
383 let initial_capacity = pool.capacity();
384
385 #[expect(
387 clippy::collection_is_never_read,
388 reason = "handles are used for ownership"
389 )]
390 let mut handles = Vec::new();
391 for i in 1..initial_capacity {
392 handles.push(pool.insert(i as u64));
393 }
394
395 let _handle = pool.insert(999_u64);
397
398 assert!(pool.capacity() >= initial_capacity);
399 }
400
401 #[test]
402 fn reserve_creates_capacity() {
403 let mut pool = PinnedPool::<u8>::new();
404
405 pool.reserve(100);
406 assert!(pool.capacity() >= 100);
407
408 let initial_capacity = pool.capacity();
409 pool.reserve(50); assert_eq!(pool.capacity(), initial_capacity);
411
412 pool.reserve(200); assert!(pool.capacity() >= 200);
414 }
415
416 #[test]
417 fn shrink_to_fit_removes_unused_capacity() {
418 let mut pool = PinnedPool::<u8>::new();
419
420 pool.reserve(100);
422
423 let _handle1 = pool.insert(1_u8);
425 let _handle2 = pool.insert(2_u8);
426
427 pool.shrink_to_fit();
429
430 assert_eq!(pool.len(), 2);
432 let _handle3 = pool.insert(3_u8);
433 assert_eq!(pool.len(), 3);
434 }
435
436 #[test]
437 fn shrink_to_fit_with_zero_items_shrinks_to_zero_capacity() {
438 let mut pool = PinnedPool::<u8>::new();
439
440 let handle1 = pool.insert(1_u8);
442 let handle2 = pool.insert(2_u8);
443 let handle3 = pool.insert(3_u8);
444
445 assert!(pool.capacity() > 0);
447
448 drop(handle1);
450 drop(handle2);
451 drop(handle3);
452
453 assert!(pool.is_empty());
454
455 pool.shrink_to_fit();
456
457 assert_eq!(pool.capacity(), 0);
461 }
462
463 #[test]
464 fn handle_provides_access_to_object() {
465 let mut pool = PinnedPool::<u64>::new();
466
467 let handle = pool.insert(12345_u64);
468
469 assert_eq!(*handle, 12345);
470 }
471
472 #[test]
473 fn multiple_handles_to_same_type() {
474 let mut pool = PinnedPool::<String>::new();
475
476 let handle1 = pool.insert("hello".to_string());
477 let handle2 = pool.insert("world".to_string());
478
479 assert_eq!(pool.len(), 2);
480
481 assert_eq!(&*handle1, "hello");
482 assert_eq!(&*handle2, "world");
483
484 drop(handle1);
486 assert_eq!(pool.len(), 1);
487
488 drop(handle2);
489 assert_eq!(pool.len(), 0);
490 assert!(pool.is_empty());
491 }
492
493 #[test]
494 fn handle_drop_removes_objects_both_exclusive_and_shared() {
495 let mut pool = PinnedPool::<String>::new();
496
497 let exclusive_handle = pool.insert("exclusive".to_string());
499 assert_eq!(pool.len(), 1);
500 drop(exclusive_handle);
501 assert_eq!(pool.len(), 0);
502
503 let mut_handle = pool.insert("shared".to_string());
505 let shared_handle = mut_handle.into_shared();
506 assert_eq!(pool.len(), 1);
507
508 assert_eq!(&*shared_handle, "shared");
510
511 drop(shared_handle);
513 assert_eq!(pool.len(), 0);
514 assert!(pool.is_empty());
515 }
516
517 #[test]
518 fn insert_with_closure() {
519 let mut pool = PinnedPool::<u64>::new();
520
521 let handle = unsafe {
523 pool.insert_with(|u: &mut MaybeUninit<u64>| {
524 u.write(42);
525 })
526 };
527
528 assert_eq!(pool.len(), 1);
529 assert_eq!(*handle, 42);
530 }
531
532 #[test]
533 fn clone_behavior() {
534 let mut p1 = PinnedPool::<u32>::new();
535 let mut p2 = p1.clone();
536
537 let _h1 = p1.insert(100);
538 assert_eq!(p2.len(), 1);
539
540 let _h2 = p2.insert(200);
541 assert_eq!(p1.len(), 2);
542
543 let mut values1: Vec<u32> = p1.with_iter(|iter| {
545 iter.map(|ptr| {
546 unsafe { *ptr.as_ref() }
548 })
549 .collect()
550 });
551 let mut values2: Vec<u32> = p2.with_iter(|iter| {
552 iter.map(|ptr| {
553 unsafe { *ptr.as_ref() }
555 })
556 .collect()
557 });
558 values1.sort_unstable();
559 values2.sort_unstable();
560 assert_eq!(values1, vec![100, 200]);
561 assert_eq!(values2, vec![100, 200]);
562 }
563
564 #[test]
565 fn lifecycle_handles_keep_pool_alive() {
566 let handle = {
567 let mut pool = PinnedPool::<String>::new();
568 pool.insert("persist".to_string())
569 }; assert_eq!(&*handle, "persist");
572
573 drop(handle);
575 }
576
577 #[test]
578 fn lifecycle_pool_clone_keeps_inner_alive() {
579 let mut pool = PinnedPool::<String>::new();
580
581 let handle = pool.insert("data".to_string());
583 let pool_clone = pool.clone();
584
585 drop(pool); assert_eq!(&*handle, "data");
590 assert_eq!(pool_clone.len(), 1);
591
592 drop(handle);
594 assert_eq!(pool_clone.len(), 0);
595 }
596
597 #[test]
598 fn pooled_mut_mutation_reflected() {
599 let mut pool = PinnedPool::<String>::new();
600
601 let mut handle = pool.insert("hello".to_string());
602 handle.push_str(" world");
603
604 assert_eq!(&*handle, "hello world");
605 assert_eq!(pool.len(), 1);
606
607 pool.with_iter(|iter| {
609 let vals: Vec<String> = iter
610 .map(|p| {
611 unsafe { p.as_ref().clone() }
613 })
614 .collect();
615 assert_eq!(vals, vec!["hello world".to_string()]);
616 });
617 }
618
619 #[test]
620 fn reserve_and_shrink_to_fit_shared() {
621 let mut p1 = PinnedPool::<u8>::new();
622 let mut p2 = p1.clone();
623
624 p1.reserve(50);
626 assert!(p1.capacity() >= 50);
627 assert_eq!(p1.capacity(), p2.capacity());
628
629 let h = p2.insert(99);
631 assert_eq!(p1.len(), 1);
632 drop(h);
633 assert_eq!(p2.len(), 0);
634
635 p1.shrink_to_fit();
637 assert_eq!(p1.capacity(), p2.capacity());
638 }
639
640 #[test]
641 fn with_iter_empty_pool() {
642 let pool = PinnedPool::<u32>::new();
643 let count = pool.with_iter(|iter| iter.count());
644 assert_eq!(count, 0);
645 }
646
647 #[test]
648 fn with_iter_collect_values() {
649 let mut pool = PinnedPool::<u32>::new();
650
651 let _handles: Vec<_> = [10, 20, 30].into_iter().map(|v| pool.insert(v)).collect();
652
653 let mut collected: Vec<u32> = pool.with_iter(|iter| {
654 iter.map(|p| {
655 unsafe { *p.as_ref() }
657 })
658 .collect()
659 });
660
661 collected.sort_unstable();
662 assert_eq!(collected, vec![10, 20, 30]);
663 }
664
665 #[test]
666 fn with_iter_double_ended() {
667 let mut pool = PinnedPool::<i32>::new();
668 let _handles: Vec<_> = [1, 2, 3].into_iter().map(|v| pool.insert(v)).collect();
669
670 pool.with_iter(|mut iter| {
671 assert_eq!(iter.len(), 3);
672
673 let _front = iter.next();
674 assert_eq!(iter.len(), 2);
675
676 let _back = iter.next_back();
677 assert_eq!(iter.len(), 1);
678
679 let _remaining = iter.next();
680 assert!(iter.next().is_none());
681 assert!(iter.next_back().is_none());
682 });
683 }
684
685 #[test]
686 fn with_iter_fused_behavior() {
687 let pool = PinnedPool::<u32>::new();
688 pool.with_iter(|mut iter| {
689 assert!(iter.next().is_none());
690 assert!(iter.next().is_none());
691 assert!(iter.next_back().is_none());
692 });
693 }
694
695 #[test]
696 fn iter_size_hint_and_exact_size() {
697 let mut pool = PinnedPool::<u32>::new();
698
699 pool.with_iter(|iter| {
701 assert_eq!(iter.size_hint(), (0, Some(0)));
702 assert_eq!(iter.len(), 0);
703 });
704
705 let _handle1 = pool.insert(100_u32);
707 let _handle2 = pool.insert(200_u32);
708
709 pool.with_iter(|mut iter| {
710 assert_eq!(iter.size_hint(), (2, Some(2)));
711 assert_eq!(iter.len(), 2);
712
713 let first_item = iter.next();
715 assert!(first_item.is_some());
716 assert_eq!(iter.size_hint(), (1, Some(1)));
717 assert_eq!(iter.len(), 1);
718
719 let second_item = iter.next();
721 assert!(second_item.is_some());
722 assert_eq!(iter.size_hint(), (0, Some(0)));
723 assert_eq!(iter.len(), 0);
724
725 assert_eq!(iter.next(), None);
727 assert_eq!(iter.size_hint(), (0, Some(0)));
728 assert_eq!(iter.len(), 0);
729 });
730 }
731
732 #[test]
733 fn thread_safety() {
734 use std::sync::Arc;
735 use std::thread;
736
737 let mut pool = PinnedPool::<String>::new();
738
739 let handle1 = pool.insert("Thread test 1".to_string());
741 let handle2 = pool.insert("Thread test 2".to_string());
742
743 let pool = Arc::new(pool);
744 let pool_clone = Arc::clone(&pool);
745
746 let thread_handle = thread::spawn(move || {
748 assert!(pool_clone.len() >= 2);
750
751 assert!(pool_clone.capacity() >= 2);
753
754 assert!(!pool_clone.is_empty());
756 });
757
758 thread_handle.join().unwrap();
760
761 assert_eq!(&*handle1, "Thread test 1");
763 assert_eq!(&*handle2, "Thread test 2");
764 }
765}