infinity_pool/handles/
local_mut.rs

1use std::any::type_name;
2use std::borrow::{Borrow, BorrowMut};
3use std::cell::RefCell;
4use std::ops::{Deref, DerefMut};
5use std::pin::Pin;
6use std::ptr::NonNull;
7use std::rc::Rc;
8use std::{fmt, mem, ptr};
9
10use crate::{LocalPooled, RawOpaquePool, RawPooledMut};
11
12/// A unique single-threaded reference-counting handle for a pooled object.
13#[doc = include_str!("../../doc/snippets/ref_counted_handle_implications.md")]
14#[doc = include_str!("../../doc/snippets/unique_handle_implications.md")]
15///
16/// # Thread safety
17///
18/// This type is single-threaded.
19pub struct LocalPooledMut<T: ?Sized> {
20    inner: RawPooledMut<T>,
21
22    // This gives us our thread-safety characteristics (single-threaded),
23    // overriding those of `RawPooledMut<T>`. This is expected because we align
24    // with the stricter constraints of the pool itself, even if the underlying
25    // slab storage allows for more flexibility.
26    pool: Rc<RefCell<RawOpaquePool>>,
27}
28
29impl<T: ?Sized> LocalPooledMut<T> {
30    #[must_use]
31    pub(crate) fn new(inner: RawPooledMut<T>, pool: Rc<RefCell<RawOpaquePool>>) -> Self {
32        Self { inner, pool }
33    }
34
35    #[doc = include_str!("../../doc/snippets/handle_ptr.md")]
36    #[must_use]
37    #[inline]
38    #[cfg_attr(test, mutants::skip)] // cargo-mutants tries many unviable mutations, wasting precious build minutes.
39    pub fn ptr(&self) -> NonNull<T> {
40        self.inner.ptr()
41    }
42
43    #[doc = include_str!("../../doc/snippets/handle_into_shared.md")]
44    #[must_use]
45    #[inline]
46    #[cfg_attr(test, mutants::skip)] // cargo-mutants tries many unviable mutations, wasting precious build minutes.
47    pub fn into_shared(self) -> LocalPooled<T> {
48        let (inner, pool) = self.into_parts();
49
50        LocalPooled::new(inner, pool)
51    }
52
53    #[cfg_attr(test, mutants::skip)] // cargo-mutants tries many unviable mutations, wasting precious build minutes.
54    fn into_parts(self) -> (RawPooledMut<T>, Rc<RefCell<RawOpaquePool>>) {
55        // We transfer these fields to the caller, so we do not want the current handle
56        // to be dropped. Hence we perform raw reads to extract the fields directly.
57
58        // SAFETY: The target is valid for reads.
59        let pool = unsafe { ptr::read(&raw const self.pool) };
60        // SAFETY: The target is valid for reads.
61        let inner = unsafe { ptr::read(&raw const self.inner) };
62
63        // We are just "destructuring with Drop" here.
64        mem::forget(self);
65
66        (inner, pool)
67    }
68
69    #[doc = include_str!("../../doc/snippets/ref_counted_as_pin.md")]
70    #[must_use]
71    #[inline]
72    #[cfg_attr(test, mutants::skip)] // cargo-mutants tries many unviable mutations, wasting precious build minutes.
73    pub fn as_pin(&self) -> Pin<&T> {
74        // SAFETY: Pooled items are always pinned.
75        unsafe { Pin::new_unchecked(self) }
76    }
77
78    #[doc = include_str!("../../doc/snippets/ref_counted_as_pin_mut.md")]
79    #[must_use]
80    #[inline]
81    #[cfg_attr(test, mutants::skip)] // cargo-mutants tries many unviable mutations, wasting precious build minutes.
82    pub fn as_pin_mut(&mut self) -> Pin<&mut T> {
83        // SAFETY: This is a unique handle, so we guarantee borrow safety
84        // of the target object by borrowing the handle itself.
85        let as_mut = unsafe { self.ptr().as_mut() };
86
87        // SAFETY: Pooled items are always pinned.
88        unsafe { Pin::new_unchecked(as_mut) }
89    }
90
91    /// Casts this handle to reference the target as a trait object.
92    ///
93    /// This method is only intended for use by the [`define_pooled_dyn_cast!`] macro
94    /// for type-safe casting operations.
95    ///
96    /// # Safety
97    ///
98    /// The caller must guarantee that the provided closure's input and output references
99    /// point to the same object.
100    #[doc(hidden)]
101    #[must_use]
102    #[inline]
103    pub unsafe fn __private_cast_dyn_with_fn<U: ?Sized, F>(self, cast_fn: F) -> LocalPooledMut<U>
104    where
105        F: FnOnce(&mut T) -> &mut U,
106    {
107        let (inner, pool) = self.into_parts();
108
109        // SAFETY: Forwarding callback safety guarantees from the caller.
110        // We are an exclusive handle, so we always have the right to create
111        // exclusive references to the target of the handle, satisfying that requirement.
112        let new_inner = unsafe { inner.__private_cast_dyn_with_fn(cast_fn) };
113
114        LocalPooledMut {
115            inner: new_inner,
116            pool,
117        }
118    }
119
120    /// Erase the type information from this handle, converting it to `LocalPooledMut<()>`.
121    ///
122    /// This is useful for extending the lifetime of an object in the pool without retaining
123    /// type information. The type-erased handle prevents access to the object but ensures
124    /// it remains in the pool.
125    #[must_use]
126    #[inline]
127    #[cfg_attr(test, mutants::skip)] // All mutations unviable - save some time.
128    pub fn erase(self) -> LocalPooledMut<()> {
129        let (inner, pool) = self.into_parts();
130
131        // SAFETY: This handle is single-threaded, no cross-thread access even if `T: Send`.
132        let slab_handle_erased = unsafe { inner.slab_handle().erase() };
133
134        let inner_erased = RawPooledMut::new(inner.slab_index(), slab_handle_erased);
135
136        LocalPooledMut {
137            inner: inner_erased,
138            pool,
139        }
140    }
141}
142
143impl<T> LocalPooledMut<T>
144where
145    T: Unpin,
146{
147    #[doc = include_str!("../../doc/snippets/ref_counted_into_inner.md")]
148    #[must_use]
149    #[inline]
150    pub fn into_inner(self) -> T {
151        let (inner, pool) = self.into_parts();
152
153        let mut pool = RefCell::borrow_mut(&pool);
154        // SAFETY: We are a managed unique handle, so we are the only one who is allowed to remove
155        // the object from the pool - as long as we exist, the object exists in the pool. We keep
156        // the pool alive for as long as any handle to it exists, so the pool must still exist.
157        unsafe { pool.remove_unpin(inner) }
158    }
159}
160
161#[cfg_attr(coverage_nightly, coverage(off))] // No API contract to test.
162impl<T: ?Sized> fmt::Debug for LocalPooledMut<T> {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        f.debug_struct(type_name::<Self>())
165            .field("inner", &self.inner)
166            .field("pool", &self.pool)
167            .finish()
168    }
169}
170
171impl<T: ?Sized> Deref for LocalPooledMut<T> {
172    type Target = T;
173
174    #[inline]
175    #[cfg_attr(test, mutants::skip)] // Cargo-mutants does not understand this signature - every mutation is unviable waste of time.
176    fn deref(&self) -> &Self::Target {
177        // SAFETY: This is a unique handle, so we guarantee borrow safety
178        // of the target object by borrowing the handle itself.
179        // We guarantee liveness by being a reference counted handle.
180        unsafe { self.ptr().as_ref() }
181    }
182}
183
184impl<T> DerefMut for LocalPooledMut<T>
185where
186    T: ?Sized + Unpin,
187{
188    #[inline]
189    #[cfg_attr(test, mutants::skip)] // Cargo-mutants does not understand this signature - every mutation is unviable waste of time.
190    fn deref_mut(&mut self) -> &mut Self::Target {
191        // SAFETY: This is a unique handle, so we guarantee borrow safety
192        // of the target object by borrowing the handle itself.
193        // We guarantee liveness by being a reference counted handle.
194        unsafe { self.ptr().as_mut() }
195    }
196}
197
198impl<T: ?Sized> Borrow<T> for LocalPooledMut<T> {
199    #[inline]
200    #[cfg_attr(test, mutants::skip)] // Cargo-mutants does not understand this signature - every mutation is unviable waste of time.
201    fn borrow(&self) -> &T {
202        self
203    }
204}
205
206impl<T> BorrowMut<T> for LocalPooledMut<T>
207where
208    T: ?Sized + Unpin,
209{
210    #[inline]
211    #[cfg_attr(test, mutants::skip)] // Cargo-mutants does not understand this signature - every mutation is unviable waste of time.
212    fn borrow_mut(&mut self) -> &mut T {
213        self
214    }
215}
216
217impl<T: ?Sized> AsRef<T> for LocalPooledMut<T> {
218    #[inline]
219    #[cfg_attr(test, mutants::skip)] // Cargo-mutants does not understand this signature - every mutation is unviable waste of time.
220    fn as_ref(&self) -> &T {
221        self
222    }
223}
224
225impl<T> AsMut<T> for LocalPooledMut<T>
226where
227    T: ?Sized + Unpin,
228{
229    #[inline]
230    #[cfg_attr(test, mutants::skip)] // Cargo-mutants does not understand this signature - every mutation is unviable waste of time.
231    fn as_mut(&mut self) -> &mut T {
232        self
233    }
234}
235
236impl<T: ?Sized> Drop for LocalPooledMut<T> {
237    fn drop(&mut self) {
238        // While `RawPooledMut` is technically not Copy, we use our insider knowledge
239        // that actually it is in reality just a fat pointer, so we can actually copy it.
240        // The only reason it is not Copy is to ensure uniqueness, which we do not care
241        // about here because the copy in `self` is going away. We just do not want to
242        // insert an Option that we have to check in every method.
243        //
244        // SAFETY: The target is valid for reads.
245        let inner = unsafe { ptr::read(&raw const self.inner) };
246
247        let mut pool = RefCell::borrow_mut(&self.pool);
248
249        // SAFETY: We are a managed unique handle, so we are the only one who is allowed to remove
250        // the object from the pool - as long as we exist, the object exists in the pool. We keep
251        // the pool alive for as long as any handle to it exists, so the pool must still exist.
252        unsafe {
253            pool.remove(inner);
254        }
255    }
256}
257
258#[cfg(test)]
259#[cfg_attr(coverage_nightly, coverage(off))]
260mod tests {
261    use static_assertions::assert_not_impl_any;
262
263    use super::*;
264    use crate::{NotSendNotSync, NotSendSync, SendAndSync, SendNotSync};
265
266    assert_not_impl_any!(LocalPooledMut<SendAndSync>: Send, Sync);
267    assert_not_impl_any!(LocalPooledMut<SendNotSync>: Send, Sync);
268    assert_not_impl_any!(LocalPooledMut<NotSendNotSync>: Send, Sync);
269    assert_not_impl_any!(LocalPooledMut<NotSendSync>: Send, Sync);
270
271    // This is a unique handle, must not be cloneable/copyable.
272    assert_not_impl_any!(LocalPooledMut<SendAndSync>: Clone, Copy);
273
274    // We expect no destructor because we treat it as `Copy` in our own Drop::drop().
275    assert_not_impl_any!(RawPooledMut<()>: Drop);
276
277    #[test]
278    fn erase_extends_lifetime() {
279        use crate::LocalOpaquePool;
280
281        let pool = LocalOpaquePool::with_layout_of::<u32>();
282        let handle = pool.insert(42);
283
284        // Erase the unique handle.
285        let erased = handle.erase();
286
287        // Object still alive in erased handle.
288        assert_eq!(pool.len(), 1);
289
290        // Drop erased handle, object is removed.
291        drop(erased);
292        assert_eq!(pool.len(), 0);
293    }
294}