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}