infinity_pool/opaque/
pool_managed.rs

1use std::alloc::Layout;
2use std::iter::FusedIterator;
3use std::mem::MaybeUninit;
4use std::ptr::NonNull;
5use std::sync::Arc;
6
7use parking_lot::Mutex;
8
9use crate::opaque::pool_raw::RawOpaquePoolIterator;
10use crate::{PooledMut, RawOpaquePool, RawOpaquePoolSend};
11
12/// A thread-safe pool of reference-counted objects with uniform memory layout.
13///
14/// Stores objects of any `Send` type that match a [`Layout`] defined at pool creation
15/// time. All values in the pool remain pinned for their entire lifetime.
16///
17/// The pool automatically expands its capacity when needed.
18#[doc = include_str!("../../doc/snippets/managed_pool_lifetimes.md")]
19#[doc = include_str!("../../doc/snippets/managed_pool_is_thread_safe.md")]
20///
21/// # Example
22///
23/// ```rust
24/// use infinity_pool::OpaquePool;
25///
26/// fn work_with_displayable<T: std::fmt::Display + Send + 'static>(value: T) {
27///     let mut pool = OpaquePool::with_layout_of::<T>();
28///
29///     // Insert an object into the pool
30///     let handle = pool.insert(value);
31///
32///     // Access the object through the handle
33///     println!("Stored: {}", &*handle);
34///
35///     // The object is automatically removed when the handle is dropped
36/// }
37///
38/// work_with_displayable("Hello, world!");
39/// work_with_displayable(42);
40/// ```
41///
42/// # Pool clones are functionally equivalent
43///
44/// ```rust
45/// use infinity_pool::OpaquePool;
46///
47/// let mut pool1 = OpaquePool::with_layout_of::<i32>();
48/// let pool2 = pool1.clone();
49///
50/// assert_eq!(pool1.len(), pool2.len());
51/// let _handle = pool1.insert(42_i32);
52/// assert_eq!(pool1.len(), pool2.len());
53/// ```
54#[derive(Debug)]
55pub struct OpaquePool {
56    // We require 'static from any inserted values because the pool
57    // does not enforce any Rust lifetime semantics, only reference counts.
58    //
59    // The pool type itself is just a handle around the inner pool,
60    // which is reference-counted and mutex-guarded. The inner pool
61    // will only ever be dropped once all items have been removed from
62    // it and no more `OpaquePool` instances exist that point to it.
63    //
64    // This also implies that `DropPolicy` has no meaning for this
65    // pool configuration, as the pool can never be dropped if it has
66    // contents (as dropping the handles of pooled objects will remove
67    // them from the pool, while keeping the pool alive until then).
68    inner: Arc<Mutex<RawOpaquePoolSend>>,
69}
70
71impl OpaquePool {
72    /// Creates a new instance of the pool with the specified layout.
73    ///
74    /// Shorthand for a builder that keeps all other options at their default values.
75    ///
76    /// # Panics
77    ///
78    /// Panics if the layout is zero-sized.
79    #[must_use]
80    pub fn with_layout(object_layout: Layout) -> Self {
81        let inner = RawOpaquePool::with_layout(object_layout);
82
83        // SAFETY: All insertion methods require `T: Send`.
84        let inner = unsafe { RawOpaquePoolSend::new(inner) };
85
86        Self {
87            inner: Arc::new(Mutex::new(inner)),
88        }
89    }
90
91    /// Creates a new instance of the pool with the layout of `T`.
92    ///
93    /// Shorthand for a builder that keeps all other options at their default values.
94    ///
95    /// # Panics
96    ///
97    /// Panics if `T` is a zero-sized type.
98    #[must_use]
99    pub fn with_layout_of<T: Sized + Send>() -> Self {
100        Self::with_layout(Layout::new::<T>())
101    }
102
103    #[doc = include_str!("../../doc/snippets/opaque_pool_layout.md")]
104    #[must_use]
105    #[inline]
106    pub fn object_layout(&self) -> Layout {
107        self.inner.lock().object_layout()
108    }
109
110    #[doc = include_str!("../../doc/snippets/pool_len.md")]
111    #[must_use]
112    #[inline]
113    pub fn len(&self) -> usize {
114        self.inner.lock().len()
115    }
116
117    #[doc = include_str!("../../doc/snippets/pool_capacity.md")]
118    #[must_use]
119    #[inline]
120    pub fn capacity(&self) -> usize {
121        self.inner.lock().capacity()
122    }
123
124    #[doc = include_str!("../../doc/snippets/pool_is_empty.md")]
125    #[must_use]
126    #[inline]
127    pub fn is_empty(&self) -> bool {
128        self.inner.lock().is_empty()
129    }
130
131    #[doc = include_str!("../../doc/snippets/pool_reserve.md")]
132    #[inline]
133    pub fn reserve(&self, additional: usize) {
134        self.inner.lock().reserve(additional);
135    }
136
137    #[doc = include_str!("../../doc/snippets/pool_shrink_to_fit.md")]
138    #[inline]
139    pub fn shrink_to_fit(&self) {
140        self.inner.lock().shrink_to_fit();
141    }
142
143    #[doc = include_str!("../../doc/snippets/pool_insert.md")]
144    ///
145    ///  # Panics
146    #[doc = include_str!("../../doc/snippets/panic_on_pool_t_layout_mismatch.md")]
147    ///
148    /// # Example
149    ///
150    /// ```rust
151    /// use infinity_pool::OpaquePool;
152    ///
153    /// let mut pool = OpaquePool::with_layout_of::<String>();
154    ///
155    /// // Insert an object into the pool
156    /// let mut handle = pool.insert("Hello".to_string());
157    ///
158    /// // Mutate the object via the unique handle
159    /// handle.push_str(", Opaque World!");
160    /// assert_eq!(&*handle, "Hello, Opaque World!");
161    ///
162    /// // Transform the unique handle into a shared handle
163    /// let shared_handle = handle.into_shared();
164    ///
165    /// // After transformation, you can only immutably dereference the object
166    /// assert_eq!(&*shared_handle, "Hello, Opaque World!");
167    /// // shared_handle.push_str("!"); // This would not compile
168    ///
169    /// // The object is removed when the handle is dropped
170    /// drop(shared_handle); // Explicitly drop to remove from pool
171    /// assert_eq!(pool.len(), 0);
172    /// ```
173    #[inline]
174    #[must_use]
175    pub fn insert<T: Send + 'static>(&self, value: T) -> PooledMut<T> {
176        let inner = self.inner.lock().insert(value);
177
178        PooledMut::new(inner, Arc::clone(&self.inner))
179    }
180
181    #[doc = include_str!("../../doc/snippets/pool_insert.md")]
182    /// # Safety
183    #[doc = include_str!("../../doc/snippets/safety_pool_t_layout_must_match.md")]
184    #[inline]
185    #[must_use]
186    pub unsafe fn insert_unchecked<T: Send + 'static>(&self, value: T) -> PooledMut<T> {
187        // SAFETY: Forwarding safety guarantees from caller.
188        let inner = unsafe { self.inner.lock().insert_unchecked(value) };
189
190        PooledMut::new(inner, Arc::clone(&self.inner))
191    }
192
193    #[doc = include_str!("../../doc/snippets/pool_insert_with.md")]
194    ///
195    /// # Example
196    ///
197    /// ```rust
198    /// use std::mem::MaybeUninit;
199    ///
200    /// use infinity_pool::OpaquePool;
201    ///
202    /// struct DataBuffer {
203    ///     id: u32,
204    ///     data: MaybeUninit<[u8; 1024]>, // Large buffer to skip initializing
205    /// }
206    ///
207    /// let mut pool = OpaquePool::with_layout_of::<DataBuffer>();
208    ///
209    /// // Initialize only the id, leaving data uninitialized for performance
210    /// let handle = unsafe {
211    ///     pool.insert_with(|uninit: &mut MaybeUninit<DataBuffer>| {
212    ///         let ptr = uninit.as_mut_ptr();
213    ///         // SAFETY: Writing to the id field within allocated space
214    ///         unsafe {
215    ///             std::ptr::addr_of_mut!((*ptr).id).write(42);
216    ///             // data field is intentionally left uninitialized
217    ///         }
218    ///     })
219    /// };
220    ///
221    /// // ID is accessible, data remains uninitialized
222    /// let id = unsafe { std::ptr::addr_of!((*handle).id).read() };
223    /// assert_eq!(id, 42);
224    /// ```
225    ///
226    /// # Panics
227    #[doc = include_str!("../../doc/snippets/panic_on_pool_t_layout_mismatch.md")]
228    ///
229    /// # Safety
230    #[doc = include_str!("../../doc/snippets/safety_closure_must_initialize_object.md")]
231    #[inline]
232    #[must_use]
233    pub unsafe fn insert_with<T, F>(&self, f: F) -> PooledMut<T>
234    where
235        T: Send + 'static,
236        F: FnOnce(&mut MaybeUninit<T>),
237    {
238        // SAFETY: Forwarding safety guarantees from caller.
239        let inner = unsafe { self.inner.lock().insert_with(f) };
240
241        PooledMut::new(inner, Arc::clone(&self.inner))
242    }
243
244    #[doc = include_str!("../../doc/snippets/pool_insert_with.md")]
245    ///
246    /// # Example
247    ///
248    /// ```rust
249    /// use std::mem::MaybeUninit;
250    ///
251    /// use infinity_pool::OpaquePool;
252    ///
253    /// struct DataBuffer {
254    ///     id: u32,
255    ///     data: MaybeUninit<[u8; 1024]>, // Large buffer to skip initializing
256    /// }
257    ///
258    /// let mut pool = OpaquePool::with_layout_of::<DataBuffer>();
259    ///
260    /// // Initialize only the id, leaving data uninitialized for performance
261    /// let handle = unsafe {
262    ///     pool.insert_with_unchecked(|uninit: &mut MaybeUninit<DataBuffer>| {
263    ///         let ptr = uninit.as_mut_ptr();
264    ///         // SAFETY: Writing to the id field within allocated space
265    ///         unsafe {
266    ///             std::ptr::addr_of_mut!((*ptr).id).write(42);
267    ///             // data field is intentionally left uninitialized
268    ///         }
269    ///     })
270    /// };
271    ///
272    /// // ID is accessible, data remains uninitialized
273    /// let id = unsafe { std::ptr::addr_of!((*handle).id).read() };
274    /// assert_eq!(id, 42);
275    /// ```
276    ///
277    /// # Safety
278    #[doc = include_str!("../../doc/snippets/safety_pool_t_layout_must_match.md")]
279    #[doc = include_str!("../../doc/snippets/safety_closure_must_initialize_object.md")]
280    #[inline]
281    #[must_use]
282    pub unsafe fn insert_with_unchecked<T, F>(&self, f: F) -> PooledMut<T>
283    where
284        T: Send + 'static,
285        F: FnOnce(&mut MaybeUninit<T>),
286    {
287        // SAFETY: Forwarding safety guarantees from caller.
288        let inner = unsafe { self.inner.lock().insert_with_unchecked(f) };
289
290        PooledMut::new(inner, Arc::clone(&self.inner))
291    }
292
293    /// Calls a closure with an iterator over all objects in the pool.
294    ///
295    /// The iterator yields untyped pointers (`NonNull<()>`) to the objects stored in the pool.
296    /// It is the caller's responsibility to cast these pointers to the appropriate type.
297    ///
298    /// The pool is locked for the entire duration of the closure, ensuring that objects
299    /// cannot be removed while iteration is in progress. This guarantees that all pointers
300    /// yielded by the iterator remain valid for the duration of the closure.
301    ///
302    /// # Examples
303    ///
304    /// ```
305    /// # use infinity_pool::OpaquePool;
306    /// let mut pool = OpaquePool::with_layout_of::<u32>();
307    /// let _handle1 = pool.insert(42u32);
308    /// let _handle2 = pool.insert(100u32);
309    ///
310    /// // Safe iteration with guaranteed pointer validity
311    /// pool.with_iter(|iter| {
312    ///     for ptr in iter {
313    ///         // SAFETY: We know these are u32 pointers from this pool
314    ///         let value = unsafe { *ptr.cast::<u32>().as_ref() };
315    ///         println!("Value: {}", value);
316    ///     }
317    /// });
318    ///
319    /// // Collect values safely
320    /// let values: Vec<u32> = pool.with_iter(|iter| {
321    ///     iter.map(|ptr| unsafe { *ptr.cast::<u32>().as_ref() })
322    ///         .collect()
323    /// });
324    /// ```
325    pub fn with_iter<F, R>(&self, f: F) -> R
326    where
327        F: FnOnce(OpaquePoolIterator<'_>) -> R,
328    {
329        let guard = self.inner.lock();
330        let iter = OpaquePoolIterator::new(&guard);
331        f(iter)
332    }
333}
334
335impl Clone for OpaquePool {
336    #[inline]
337    fn clone(&self) -> Self {
338        Self {
339            inner: Arc::clone(&self.inner),
340        }
341    }
342}
343
344/// Iterator over all objects in an opaque pool.
345///
346/// This iterator yields untyped pointers to objects stored in the pool.
347/// Since the pool can contain objects of different types (as long as they have the same layout),
348/// the iterator returns `NonNull<()>` and leaves type casting to the caller.
349///
350/// The iterator holds a reference to the locked pool, ensuring that objects cannot be
351/// removed while iteration is in progress and that all yielded pointers remain valid.
352///
353/// # Safety
354///
355/// While the iterator ensures objects cannot be removed from the pool during iteration,
356/// it is still the caller's responsibility to cast the `NonNull<()>` pointers to the
357/// correct type that matches the pool's layout.
358#[derive(Debug)]
359pub struct OpaquePoolIterator<'p> {
360    raw_iter: RawOpaquePoolIterator<'p>,
361}
362
363impl<'p> OpaquePoolIterator<'p> {
364    fn new(pool: &'p RawOpaquePoolSend) -> Self {
365        Self {
366            raw_iter: pool.iter(),
367        }
368    }
369}
370
371impl Iterator for OpaquePoolIterator<'_> {
372    type Item = NonNull<()>;
373
374    fn next(&mut self) -> Option<Self::Item> {
375        self.raw_iter.next()
376    }
377
378    fn size_hint(&self) -> (usize, Option<usize>) {
379        self.raw_iter.size_hint()
380    }
381}
382
383impl DoubleEndedIterator for OpaquePoolIterator<'_> {
384    fn next_back(&mut self) -> Option<Self::Item> {
385        self.raw_iter.next_back()
386    }
387}
388
389impl ExactSizeIterator for OpaquePoolIterator<'_> {
390    fn len(&self) -> usize {
391        self.raw_iter.len()
392    }
393}
394
395impl FusedIterator for OpaquePoolIterator<'_> {}
396
397#[cfg(test)]
398mod tests {
399    use super::*;
400
401    #[test]
402    fn new_pool_with_layout_of_is_empty() {
403        let pool = OpaquePool::with_layout_of::<u64>();
404
405        assert_eq!(pool.len(), 0);
406        assert!(pool.is_empty());
407        assert_eq!(pool.capacity(), 0);
408        assert_eq!(pool.object_layout(), Layout::new::<u64>());
409    }
410
411    #[test]
412    fn new_pool_with_layout_is_empty() {
413        let layout = Layout::new::<i64>();
414        let pool = OpaquePool::with_layout(layout);
415
416        assert_eq!(pool.object_layout(), layout);
417        assert_eq!(pool.len(), 0);
418        assert!(pool.is_empty());
419        assert_eq!(pool.capacity(), 0);
420    }
421
422    #[test]
423    fn insert_and_length() {
424        let pool = OpaquePool::with_layout_of::<u32>();
425
426        let _handle1 = pool.insert(42_u32);
427        assert_eq!(pool.len(), 1);
428        assert!(!pool.is_empty());
429
430        let _handle2 = pool.insert(100_u32);
431        assert_eq!(pool.len(), 2);
432    }
433
434    #[test]
435    fn capacity_grows_when_needed() {
436        let pool = OpaquePool::with_layout_of::<u64>();
437
438        assert_eq!(pool.capacity(), 0);
439
440        let _handle = pool.insert(123_u64);
441
442        // Should have some capacity now
443        assert!(pool.capacity() > 0);
444        let initial_capacity = pool.capacity();
445
446        // Fill up the pool to force capacity expansion
447        #[expect(
448            clippy::collection_is_never_read,
449            reason = "handles are used for ownership"
450        )]
451        let mut handles = Vec::new();
452        for i in 1..initial_capacity {
453            handles.push(pool.insert(i as u64));
454        }
455
456        // One more insert should expand capacity
457        let _handle = pool.insert(999_u64);
458
459        assert!(pool.capacity() >= initial_capacity);
460    }
461
462    #[test]
463    fn reserve_creates_capacity() {
464        let pool = OpaquePool::with_layout_of::<u8>();
465
466        pool.reserve(100);
467        assert!(pool.capacity() >= 100);
468
469        let initial_capacity = pool.capacity();
470        pool.reserve(50); // Should not increase capacity
471        assert_eq!(pool.capacity(), initial_capacity);
472
473        pool.reserve(200); // Should increase capacity
474        assert!(pool.capacity() >= 200);
475    }
476
477    #[test]
478    fn insert_with_closure() {
479        let pool = OpaquePool::with_layout_of::<u64>();
480
481        // SAFETY: we correctly initialize the slot.
482        let handle = unsafe {
483            pool.insert_with(|uninit: &mut MaybeUninit<u64>| {
484                uninit.write(42);
485            })
486        };
487
488        assert_eq!(pool.len(), 1);
489        assert_eq!(*handle, 42);
490    }
491
492    #[test]
493    fn shrink_to_fit_removes_unused_capacity() {
494        let pool = OpaquePool::with_layout_of::<u8>();
495
496        // Reserve more than we need
497        pool.reserve(100);
498
499        // Insert only a few items
500        let _handle1 = pool.insert(1_u8);
501        let _handle2 = pool.insert(2_u8);
502
503        // Shrink should not panic
504        pool.shrink_to_fit();
505
506        // Pool should still work normally
507        assert_eq!(pool.len(), 2);
508        let _handle3 = pool.insert(3_u8);
509        assert_eq!(pool.len(), 3);
510    }
511
512    #[test]
513    fn shrink_to_fit_with_zero_items_shrinks_to_zero_capacity() {
514        let pool = OpaquePool::with_layout_of::<u8>();
515
516        // Add some items to create capacity
517        let handle1 = pool.insert(1_u8);
518        let handle2 = pool.insert(2_u8);
519        let handle3 = pool.insert(3_u8);
520
521        // Verify we have capacity
522        assert!(pool.capacity() > 0);
523
524        // Remove all items by dropping handles
525        drop(handle1);
526        drop(handle2);
527        drop(handle3);
528
529        assert!(pool.is_empty());
530
531        pool.shrink_to_fit();
532
533        // Testing implementation detail: empty pool should shrink capacity to zero
534        // This may become untrue with future algorithm changes, at which point
535        // we will need to adjust the tests.
536        assert_eq!(pool.capacity(), 0);
537    }
538
539    #[test]
540    fn handle_provides_access_to_object() {
541        let pool = OpaquePool::with_layout_of::<u64>();
542
543        let handle = pool.insert(12345_u64);
544
545        assert_eq!(*handle, 12345);
546    }
547
548    #[test]
549    fn multiple_handles_to_same_type() {
550        let pool = OpaquePool::with_layout_of::<String>();
551
552        let handle1 = pool.insert("hello".to_string());
553        let handle2 = pool.insert("world".to_string());
554
555        assert_eq!(pool.len(), 2);
556
557        assert_eq!(&*handle1, "hello");
558        assert_eq!(&*handle2, "world");
559
560        // Dropping handles should remove items from pool
561        drop(handle1);
562        assert_eq!(pool.len(), 1);
563
564        drop(handle2);
565        assert_eq!(pool.len(), 0);
566        assert!(pool.is_empty());
567    }
568
569    #[test]
570    fn handle_drop_removes_objects_both_exclusive_and_shared() {
571        let pool = OpaquePool::with_layout_of::<String>();
572
573        // Test exclusive handle drop
574        let exclusive_handle = pool.insert("exclusive".to_string());
575        assert_eq!(pool.len(), 1);
576        drop(exclusive_handle);
577        assert_eq!(pool.len(), 0);
578
579        // Test shared handle drop
580        let mut_handle = pool.insert("shared".to_string());
581        let shared_handle = mut_handle.into_shared();
582        assert_eq!(pool.len(), 1);
583
584        // Verify shared handle works
585        assert_eq!(&*shared_handle, "shared");
586
587        // Drop the shared handle should remove from pool
588        drop(shared_handle);
589        assert_eq!(pool.len(), 0);
590        assert!(pool.is_empty());
591    }
592
593    #[test]
594    fn iter_empty_pool() {
595        let pool = OpaquePool::with_layout_of::<u32>();
596
597        pool.with_iter(|mut iter| {
598            assert_eq!(iter.size_hint(), (0, Some(0)));
599            assert_eq!(iter.len(), 0);
600
601            assert_eq!(iter.next(), None);
602            assert_eq!(iter.size_hint(), (0, Some(0)));
603            assert_eq!(iter.len(), 0);
604        });
605    }
606
607    #[test]
608    fn iter_single_item() {
609        let pool = OpaquePool::with_layout_of::<u32>();
610
611        let _handle = pool.insert(42_u32);
612
613        pool.with_iter(|mut iter| {
614            assert_eq!(iter.len(), 1);
615
616            // First item should be the object we inserted
617            let ptr = iter.next().expect("should have one item");
618
619            // SAFETY: We know this points to a u32 we just inserted
620            let value = unsafe { ptr.cast::<u32>().as_ref() };
621            assert_eq!(*value, 42);
622
623            // No more items
624            assert_eq!(iter.next(), None);
625            assert_eq!(iter.len(), 0);
626        });
627    }
628
629    #[test]
630    fn iter_multiple_items() {
631        let pool = OpaquePool::with_layout_of::<u32>();
632
633        let _handle1 = pool.insert(100_u32);
634        let _handle2 = pool.insert(200_u32);
635        let _handle3 = pool.insert(300_u32);
636
637        pool.with_iter(|iter| {
638            let values: Vec<u32> = iter
639                .map(|ptr| {
640                    // SAFETY: We know these point to u32s we inserted
641                    unsafe { *ptr.cast::<u32>().as_ref() }
642                })
643                .collect();
644
645            assert_eq!(values, vec![100, 200, 300]);
646        });
647    }
648
649    #[test]
650    fn iter_double_ended_basic() {
651        let pool = OpaquePool::with_layout_of::<u32>();
652
653        let _handle1 = pool.insert(100_u32);
654        let _handle2 = pool.insert(200_u32);
655        let _handle3 = pool.insert(300_u32);
656
657        pool.with_iter(|mut iter| {
658            // Iterate from the back
659            let last_ptr = iter.next_back().expect("should have last item");
660            // SAFETY: We know this points to a u32 we inserted
661            let last_value = unsafe { *last_ptr.cast::<u32>().as_ref() };
662            assert_eq!(last_value, 300);
663
664            let middle_ptr = iter.next_back().expect("should have middle item");
665            // SAFETY: We know this points to a u32 we inserted
666            let middle_value = unsafe { *middle_ptr.cast::<u32>().as_ref() };
667            assert_eq!(middle_value, 200);
668
669            let first_ptr = iter.next().expect("should have first item");
670            // SAFETY: We know this points to a u32 we inserted
671            let first_value = unsafe { *first_ptr.cast::<u32>().as_ref() };
672            assert_eq!(first_value, 100);
673
674            // Should be exhausted now
675            assert_eq!(iter.next(), None);
676            assert_eq!(iter.next_back(), None);
677        });
678    }
679
680    #[test]
681    fn with_iter_scoped_access() {
682        let pool = OpaquePool::with_layout_of::<u32>();
683
684        let _handle1 = pool.insert(100_u32);
685        let _handle2 = pool.insert(200_u32);
686        let _handle3 = pool.insert(300_u32);
687
688        // Test that we can use the iterator in a scoped manner
689        let result = pool.with_iter(|iter| {
690            let mut values = Vec::new();
691            for ptr in iter {
692                // SAFETY: We know these point to u32s we just inserted
693                let value = unsafe { *ptr.cast::<u32>().as_ref() };
694                values.push(value);
695            }
696            values
697        });
698
699        assert_eq!(result, vec![100, 200, 300]);
700    }
701
702    #[test]
703    fn with_iter_holds_lock() {
704        let pool = OpaquePool::with_layout_of::<u32>();
705
706        let _handle1 = pool.insert(100_u32);
707        let _handle2 = pool.insert(200_u32);
708
709        // Test that iteration sees a consistent view
710        pool.with_iter(|iter| {
711            assert_eq!(iter.len(), 2);
712
713            let values: Vec<u32> = iter
714                .map(|ptr| {
715                    // SAFETY: We know these point to u32s we inserted
716                    unsafe { *ptr.cast::<u32>().as_ref() }
717                })
718                .collect();
719
720            assert_eq!(values, vec![100, 200]);
721        });
722
723        // After the scope, we can modify the pool again
724        let _handle3 = pool.insert(300_u32);
725
726        // A new with_iter call should see all 3 items
727        pool.with_iter(|iter| {
728            assert_eq!(iter.len(), 3);
729        });
730    }
731
732    #[test]
733    fn iter_size_hint_and_exact_size() {
734        let pool = OpaquePool::with_layout_of::<u32>();
735
736        // Empty pool
737        pool.with_iter(|iter| {
738            assert_eq!(iter.size_hint(), (0, Some(0)));
739            assert_eq!(iter.len(), 0);
740        });
741
742        // Add some items
743        let _handle1 = pool.insert(100_u32);
744        let _handle2 = pool.insert(200_u32);
745
746        pool.with_iter(|mut iter| {
747            assert_eq!(iter.size_hint(), (2, Some(2)));
748            assert_eq!(iter.len(), 2);
749
750            // Consume one item
751            let first_item = iter.next();
752            assert!(first_item.is_some());
753            assert_eq!(iter.size_hint(), (1, Some(1)));
754            assert_eq!(iter.len(), 1);
755
756            // Consume another
757            let second_item = iter.next();
758            assert!(second_item.is_some());
759            assert_eq!(iter.size_hint(), (0, Some(0)));
760            assert_eq!(iter.len(), 0);
761
762            // Should be exhausted now
763            assert_eq!(iter.next(), None);
764            assert_eq!(iter.size_hint(), (0, Some(0)));
765            assert_eq!(iter.len(), 0);
766        });
767    }
768
769    #[test]
770    fn clone_behavior() {
771        let pool1 = OpaquePool::with_layout_of::<u32>();
772
773        // Clone the pool handle
774        let pool2 = pool1.clone();
775
776        // Both should have the same object layout
777        assert_eq!(pool1.object_layout(), pool2.object_layout());
778
779        // Insert via first handle
780        let _handle1 = pool1.insert(100_u32);
781
782        // Second handle should see the same pool state
783        assert_eq!(pool2.len(), 1);
784        assert!(!pool2.is_empty());
785
786        // Insert via second handle
787        let _handle2 = pool2.insert(200_u32);
788
789        // First handle should see the updated state
790        assert_eq!(pool1.len(), 2);
791
792        // Both handles should see the objects via iteration
793        pool1.with_iter(|iter| {
794            let values: Vec<u32> = iter
795                // SAFETY: We know these point to u32s we inserted
796                .map(|ptr| unsafe { *ptr.cast::<u32>().as_ref() })
797                .collect();
798            assert_eq!(values, vec![100, 200]);
799        });
800
801        pool2.with_iter(|iter| {
802            let values: Vec<u32> = iter
803                // SAFETY: We know these point to u32s we inserted
804                .map(|ptr| unsafe { *ptr.cast::<u32>().as_ref() })
805                .collect();
806            assert_eq!(values, vec![100, 200]);
807        });
808    }
809
810    #[test]
811    fn lifecycle_management_pool_keeps_inner_alive() {
812        let pool = OpaquePool::with_layout_of::<String>();
813
814        // Insert an object and get a handle
815        let handle = pool.insert("test data".to_string());
816
817        // Clone the pool before dropping the original
818        let pool_clone = pool.clone();
819
820        // Drop the original pool
821        drop(pool);
822
823        // The handle should still be valid because pool_clone keeps the inner pool alive
824        assert_eq!(&*handle, "test data");
825
826        // We should still be able to access pool operations through the clone
827        assert_eq!(pool_clone.len(), 1);
828        assert!(!pool_clone.is_empty());
829
830        // Dropping the handle should remove the object
831        drop(handle);
832
833        // Pool clone should reflect the removal
834        assert_eq!(pool_clone.len(), 0);
835        assert!(pool_clone.is_empty());
836    }
837
838    #[test]
839    fn lifecycle_management_handles_keep_pool_alive() {
840        let handle = {
841            let pool = OpaquePool::with_layout_of::<String>();
842            // Pool goes out of scope here, but handle should keep inner pool alive
843            pool.insert("persistent data".to_string())
844        };
845
846        // Handle should still be valid and accessible
847        assert_eq!(&*handle, "persistent data");
848
849        // Even though the original pool is dropped, the object remains accessible
850        // This tests that PooledMut holds a reference to the inner pool
851        drop(handle);
852        // Object is cleaned up when handle is dropped
853    }
854
855    #[test]
856    #[cfg(not(miri))] // Blocking on a parking_lot Mutex in Miri is not supported.
857    fn concurrent_access_basic() {
858        use std::sync::{Arc, Barrier};
859        use std::thread;
860
861        let pool = Arc::new(Mutex::new(OpaquePool::with_layout_of::<u32>()));
862        let barrier = Arc::new(Barrier::new(3));
863
864        let mut handles = vec![];
865
866        // Spawn threads that insert concurrently
867        for i in 0..3 {
868            let pool_clone = Arc::clone(&pool);
869            let barrier_clone = Arc::clone(&barrier);
870
871            let handle = thread::spawn(move || {
872                barrier_clone.wait();
873
874                let value = (i + 1) * 100;
875                let handle = {
876                    let pool_guard = pool_clone.lock();
877                    pool_guard.insert(value)
878                };
879
880                // Return the handle and the value we inserted
881                (handle, value)
882            });
883
884            handles.push(handle);
885        }
886
887        // Collect results and keep the pooled handles alive
888        let mut pooled_handles = vec![];
889        for handle in handles {
890            let (pooled_handle, value) = handle.join().unwrap();
891            assert_eq!(*pooled_handle, value);
892            pooled_handles.push(pooled_handle);
893        }
894
895        // Verify final pool state
896        {
897            let pool_guard = pool.lock();
898            assert_eq!(pool_guard.len(), 3);
899
900            let mut values: Vec<u32> = pool_guard.with_iter(|iter| {
901                iter
902                    // SAFETY: We know these point to u32s we inserted
903                    .map(|ptr| unsafe { *ptr.cast::<u32>().as_ref() })
904                    .collect()
905            });
906
907            values.sort_unstable();
908            assert_eq!(values, vec![100, 200, 300]);
909        }
910
911        // Clean up by dropping the handles
912        drop(pooled_handles);
913    }
914
915    #[test]
916    fn pooled_mut_integration() {
917        let pool = OpaquePool::with_layout_of::<String>();
918
919        // Test that PooledMut works correctly with OpaquePool
920        let mut handle = pool.insert("initial".to_string());
921
922        // Test deref access
923        assert_eq!(&*handle, "initial");
924
925        // Test mutable access
926        handle.push_str(" value");
927        assert_eq!(&*handle, "initial value");
928
929        // Test that the pool sees the mutation
930        assert_eq!(pool.len(), 1);
931
932        // Test that iteration sees the mutated value
933        pool.with_iter(|iter| {
934            let values: Vec<String> = iter
935                // SAFETY: We know these point to Strings we inserted
936                .map(|ptr| unsafe { ptr.cast::<String>().as_ref().clone() })
937                .collect();
938            assert_eq!(values, vec!["initial value"]);
939        });
940
941        // Test that dropping the handle removes from pool
942        drop(handle);
943        assert_eq!(pool.len(), 0);
944        assert!(pool.is_empty());
945    }
946
947    #[test]
948    fn multiple_handles_to_different_objects() {
949        let pool = OpaquePool::with_layout_of::<u32>();
950
951        // Insert multiple objects
952        let handle1 = pool.insert(100_u32);
953        let handle2 = pool.insert(200_u32);
954        let handle3 = pool.insert(300_u32);
955
956        assert_eq!(pool.len(), 3);
957
958        // All handles should be independently accessible
959        assert_eq!(*handle1, 100);
960        assert_eq!(*handle2, 200);
961        assert_eq!(*handle3, 300);
962
963        // Drop one handle
964        drop(handle2);
965        assert_eq!(pool.len(), 2);
966
967        // Remaining handles should still work
968        assert_eq!(*handle1, 100);
969        assert_eq!(*handle3, 300);
970
971        // Pool should reflect the removal
972        pool.with_iter(|iter| {
973            let mut values: Vec<u32> = iter
974                // SAFETY: We know these point to u32s we inserted
975                .map(|ptr| unsafe { *ptr.cast::<u32>().as_ref() })
976                .collect();
977            values.sort_unstable();
978            assert_eq!(values, vec![100, 300]);
979        });
980
981        drop(handle1);
982        drop(handle3);
983        assert_eq!(pool.len(), 0);
984    }
985
986    #[test]
987    fn insert_methods_with_lifecycle() {
988        let pool = OpaquePool::with_layout_of::<String>();
989
990        // Test regular insert
991        let handle1 = pool.insert("regular".to_string());
992        assert_eq!(pool.len(), 1);
993
994        // Test unsafe insert_unchecked
995        // SAFETY: String layout matches the pool's object layout
996        let handle2 = unsafe { pool.insert_unchecked("unchecked".to_string()) };
997        assert_eq!(pool.len(), 2);
998
999        // Test insert_with
1000        // SAFETY: We properly initialize the string value
1001        let handle3 = unsafe {
1002            pool.insert_with(|uninit| {
1003                uninit.write("with_closure".to_string());
1004            })
1005        };
1006        assert_eq!(pool.len(), 3);
1007
1008        // Test insert_with_unchecked
1009        // SAFETY: String layout matches the pool and we properly initialize the value
1010        let handle4 = unsafe {
1011            pool.insert_with_unchecked(|uninit| {
1012                uninit.write("with_closure_unchecked".to_string());
1013            })
1014        };
1015        assert_eq!(pool.len(), 4);
1016
1017        // Verify all values
1018        assert_eq!(&*handle1, "regular");
1019        assert_eq!(&*handle2, "unchecked");
1020        assert_eq!(&*handle3, "with_closure");
1021        assert_eq!(&*handle4, "with_closure_unchecked");
1022
1023        // Test cleanup
1024        drop(handle1);
1025        assert_eq!(pool.len(), 3);
1026
1027        drop(handle2);
1028        drop(handle3);
1029        drop(handle4);
1030        assert_eq!(pool.len(), 0);
1031    }
1032
1033    #[test]
1034    fn pool_operations_with_arc_semantics() {
1035        let pool1 = OpaquePool::with_layout_of::<u32>();
1036
1037        // Insert some initial data
1038        let _handle1 = pool1.insert(100_u32);
1039        let _handle2 = pool1.insert(200_u32);
1040
1041        // Clone the pool
1042        let pool2 = pool1.clone();
1043
1044        // Test capacity operations on both handles
1045        assert_eq!(pool1.capacity(), pool2.capacity());
1046
1047        let initial_capacity = pool1.capacity();
1048        pool1.reserve(10);
1049
1050        // Both should see the capacity change (note: reserve is a minimum, actual capacity may be higher)
1051        assert!(pool1.capacity() >= initial_capacity);
1052        assert_eq!(pool1.capacity(), pool2.capacity());
1053
1054        // Test shrink_to_fit
1055        pool2.shrink_to_fit();
1056        assert_eq!(pool1.capacity(), pool2.capacity());
1057
1058        // Both should see the same length and state
1059        assert_eq!(pool1.len(), pool2.len());
1060        assert_eq!(pool1.is_empty(), pool2.is_empty());
1061        assert_eq!(pool1.object_layout(), pool2.object_layout());
1062    }
1063
1064    #[test]
1065    fn into_inner_removes_and_returns_value() {
1066        let pool = OpaquePool::with_layout_of::<String>();
1067
1068        // Insert an item into the pool
1069        let mut handle = pool.insert("initial value".to_string());
1070        assert_eq!(pool.len(), 1);
1071        assert_eq!(&*handle, "initial value");
1072
1073        // Modify the value while it's in the pool
1074        handle.push_str(" - modified");
1075        assert_eq!(&*handle, "initial value - modified");
1076        assert_eq!(pool.len(), 1);
1077
1078        // Extract the value from the pool using into_inner()
1079        let extracted_value = handle.into_inner();
1080
1081        // Verify the extracted value is correct
1082        assert_eq!(extracted_value, "initial value - modified");
1083
1084        // Verify the pool is now empty (item was removed)
1085        assert_eq!(pool.len(), 0);
1086        assert!(pool.is_empty());
1087
1088        // The caller now owns the value and can continue using it
1089        let final_value = extracted_value + " - after extraction";
1090        assert_eq!(final_value, "initial value - modified - after extraction");
1091    }
1092
1093    #[test]
1094    fn pool_operations_work_with_shared_references() {
1095        // Test that all insertion methods work with &self (shared references)
1096        let pool = OpaquePool::with_layout_of::<String>();
1097
1098        // Test basic insert
1099        let handle1 = pool.insert("hello".to_string());
1100        assert_eq!(pool.len(), 1);
1101        assert_eq!(&*handle1, "hello");
1102
1103        // Test insert_with
1104        // SAFETY: We properly initialize the value in the closure.
1105        let handle2 = unsafe {
1106            pool.insert_with(|uninit| {
1107                uninit.write("world".to_string());
1108            })
1109        };
1110        assert_eq!(pool.len(), 2);
1111        assert_eq!(&*handle2, "world");
1112
1113        // Test insert_unchecked
1114        // SAFETY: String layout matches the expected layout for this pool.
1115        let handle3 = unsafe { pool.insert_unchecked("test".to_string()) };
1116        assert_eq!(pool.len(), 3);
1117        assert_eq!(&*handle3, "test");
1118
1119        // Test insert_with_unchecked
1120        // SAFETY: We properly initialize the value in the closure.
1121        let handle4 = unsafe {
1122            pool.insert_with_unchecked(|uninit| {
1123                uninit.write("unchecked".to_string());
1124            })
1125        };
1126        assert_eq!(pool.len(), 4);
1127        assert_eq!(&*handle4, "unchecked");
1128
1129        // Test reserve and shrink_to_fit work with shared references
1130        pool.reserve(10);
1131        pool.shrink_to_fit();
1132
1133        // Clean up
1134        drop(handle1);
1135        drop(handle2);
1136        drop(handle3);
1137        drop(handle4);
1138        assert_eq!(pool.len(), 0);
1139    }
1140}