infinity_pool/handles/
raw.rs

1use std::any::type_name;
2use std::fmt;
3use std::pin::Pin;
4use std::ptr::NonNull;
5
6use crate::{RawPooledMut, SlabHandle};
7
8/// A shared handle to an object in an object pool.
9#[doc = include_str!("../../doc/snippets/raw_handle_implications.md")]
10#[doc = include_str!("../../doc/snippets/shared_handle_implications.md")]
11#[doc = include_str!("../../doc/snippets/shared_raw_handle_implications.md")]
12#[doc = include_str!("../../doc/snippets/nonlocal_handle_thread_safety.md")]
13pub struct RawPooled<T>
14where
15    // We support casting to trait objects, hence `?Sized`.
16    T: ?Sized,
17{
18    /// Index of the slab in the pool. Slabs are guaranteed to stay at the same index unless
19    /// the pool is shrunk (which can only happen when the affected slabs are empty, in which
20    /// case all existing handles are already invalidated).
21    slab_index: usize,
22
23    /// Handle to the object in the slab. This grants us access to the object's pointer
24    /// and allows us to operate on the object (e.g. to remove it or create a reference).
25    ///
26    /// We inherit the thread-safety properties of the slab handle (Send from T, Sync always).
27    slab_handle: SlabHandle<T>,
28}
29
30impl<T: ?Sized> RawPooled<T> {
31    #[must_use]
32    pub(crate) fn new(slab_index: usize, slab_handle: SlabHandle<T>) -> Self {
33        Self {
34            slab_index,
35            slab_handle,
36        }
37    }
38
39    /// Get the index of the slab in the pool.
40    ///
41    /// This is used by the pool itself to identify the slab in which the object resides.
42    #[must_use]
43    pub(crate) fn slab_index(&self) -> usize {
44        self.slab_index
45    }
46
47    /// Get the slab handle for this pool handle.
48    ///
49    /// This is used by the pool itself to perform operations on the object in the slab.
50    #[must_use]
51    pub(crate) fn slab_handle(&self) -> SlabHandle<T> {
52        self.slab_handle
53    }
54
55    #[doc = include_str!("../../doc/snippets/handle_ptr.md")]
56    #[must_use]
57    #[inline]
58    #[cfg_attr(test, mutants::skip)] // cargo-mutants tries many unviable mutations, wasting precious build minutes.
59    pub fn ptr(&self) -> NonNull<T> {
60        self.slab_handle.ptr()
61    }
62
63    /// Erase the type information from this handle for internal use by remover logic.
64    ///
65    /// # Safety
66    ///
67    /// This method is intended for internal use only and does not enforce trait bounds
68    /// on the type being erased. The caller must guarantee that the type-erased handle
69    /// will not be used in a way that would take advantage of auto traits of `()` that
70    /// the original type `T` did not have (such as `Send`).
71    ///
72    /// For public use, see [`erase()`](Self::erase), which has simplified safety rules
73    /// because it can rely on public API constraints being enforced on all accesses.
74    #[must_use]
75    #[inline]
76    #[cfg_attr(test, mutants::skip)] // cargo-mutants tries many unviable mutations, wasting precious build minutes.
77    pub(crate) unsafe fn erase_raw(self) -> RawPooled<()> {
78        RawPooled {
79            slab_index: self.slab_index,
80            // SAFETY: Forwarding safety guarantees from the caller.
81            slab_handle: unsafe { self.slab_handle.erase() },
82        }
83    }
84
85    #[doc = include_str!("../../doc/snippets/raw_as_pin.md")]
86    #[must_use]
87    #[inline]
88    #[cfg_attr(test, mutants::skip)] // cargo-mutants tries many unviable mutations, wasting precious build minutes.
89    pub unsafe fn as_pin(&self) -> Pin<&T> {
90        // SAFETY: Forwarding safety guarantees from the caller.
91        let as_ref = unsafe { self.as_ref() };
92
93        // SAFETY: Pooled items are always pinned.
94        unsafe { Pin::new_unchecked(as_ref) }
95    }
96
97    #[doc = include_str!("../../doc/snippets/raw_as_ref.md")]
98    #[must_use]
99    #[inline]
100    #[cfg_attr(test, mutants::skip)] // cargo-mutants tries many unviable mutations, wasting precious build minutes.
101    pub unsafe fn as_ref(&self) -> &T {
102        // SAFETY: This is a shared handle, so we cannot guarantee borrow safety. What we
103        // do instead is that we only allow shared references to be created from shared handles,
104        // so the user must explicitly invoke (some other) unsafe code to create an exclusive
105        // reference, at which point they become responsible for deconflicting the references.
106        // Pointer validity requires pool to be alive for the duration of the reference.
107        unsafe { self.ptr().as_ref() }
108    }
109
110    /// Casts this handle to reference the target as a trait object.
111    ///
112    /// This method is only intended for use by the [`define_pooled_dyn_cast!`] macro
113    /// for type-safe casting operations.
114    ///
115    /// # Safety
116    ///
117    /// The caller must guarantee that the provided closure's input and output references
118    /// point to the same object.
119    ///
120    /// The caller must guarantee that the pool will remain alive for the duration the returned
121    /// reference is used.
122    #[doc(hidden)]
123    #[must_use]
124    #[inline]
125    pub unsafe fn __private_cast_dyn_with_fn<U: ?Sized, F>(self, cast_fn: F) -> RawPooled<U>
126    where
127        F: FnOnce(&T) -> &U,
128    {
129        // SAFETY: Forwarding callback safety guarantees from the caller.
130        // We are a shared handle, so we always have the right to create
131        // shared references to the target of the handle, satisfying that requirement.
132        let new_handle = unsafe { self.slab_handle.cast_with(cast_fn) };
133
134        RawPooled {
135            slab_index: self.slab_index,
136            slab_handle: new_handle,
137        }
138    }
139
140    /// Erase the type information from this handle, converting it to `RawPooled<()>`.
141    ///
142    /// This is useful for extending the lifetime of an object in the pool without retaining
143    /// type information. The type-erased handle prevents access to the object but provides
144    /// the capability to type-agnostically remove the object at a later point in time.
145    #[must_use]
146    #[inline]
147    #[cfg_attr(test, mutants::skip)] // All mutations unviable - save some time.
148    pub fn erase(self) -> RawPooled<()> {
149        // SAFETY: The risk is that if `T: !Send` then `Self<()>` would be `Send`. Calling
150        // `pool.remove()` with this handle would move the object across threads to drop it.
151        // However, this cannot actually happen because for this call to be possible, `pool`
152        // would need to at minimum be `Send` (either to move it to a new thread or to wrap in
153        // a mutex and gain `Sync` with an exclusive reference), which is only possible if
154        // `T: Send` to begin with because that is a condition set by the pool itself.
155        unsafe { self.erase_raw() }
156    }
157}
158
159impl<T: ?Sized> Clone for RawPooled<T> {
160    #[inline]
161    fn clone(&self) -> Self {
162        *self
163    }
164}
165
166impl<T: ?Sized> Copy for RawPooled<T> {}
167
168impl<T: ?Sized> fmt::Debug for RawPooled<T> {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        f.debug_struct(type_name::<Self>())
171            .field("slab_index", &self.slab_index)
172            .field("slab_handle", &self.slab_handle)
173            .finish()
174    }
175}
176
177impl<T: ?Sized> From<RawPooledMut<T>> for RawPooled<T> {
178    #[inline]
179    fn from(value: RawPooledMut<T>) -> Self {
180        value.into_shared()
181    }
182}
183
184#[cfg(test)]
185#[allow(clippy::undocumented_unsafe_blocks, reason = "test code, be concise")]
186mod tests {
187    use static_assertions::{assert_impl_all, assert_not_impl_any};
188
189    use super::*;
190    use crate::{NotSendNotSync, NotSendSync, SendAndSync, SendNotSync};
191
192    assert_impl_all!(RawPooled<SendAndSync>: Send, Sync);
193    assert_impl_all!(RawPooled<SendNotSync>: Send, Sync);
194    assert_impl_all!(RawPooled<NotSendNotSync>: Sync);
195    assert_impl_all!(RawPooled<NotSendSync>:  Sync);
196
197    assert_not_impl_any!(RawPooled<NotSendNotSync>: Send);
198    assert_not_impl_any!(RawPooled<NotSendSync>: Send);
199
200    // Shared raw handles are just fancy pointers, value objects.
201    assert_impl_all!(RawPooled<SendAndSync>: Copy);
202    assert_not_impl_any!(RawPooled<SendAndSync>: Drop);
203
204    #[test]
205    fn erase_raw_and_public_erase() {
206        use crate::RawPinnedPool;
207
208        let mut pool = RawPinnedPool::<u32>::new();
209        let handle = pool.insert(42);
210        let shared = handle.into_shared();
211
212        // Both erase_raw() and erase() produce the same result.
213        let erased_raw = unsafe { shared.erase_raw() };
214        let erased_public = shared.erase();
215
216        // Both are RawPooled<()> and behave identically.
217        assert_eq!(erased_raw.slab_index(), erased_public.slab_index());
218
219        // SAFETY: Handles are valid and pool is alive.
220        unsafe {
221            pool.remove(erased_raw);
222        }
223
224        assert_eq!(pool.len(), 0);
225    }
226}