Skip to main content

thin_cell/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3#![deny(rustdoc::broken_intra_doc_links)]
4
5mod state;
6
7mod fat_ptr;
8
9pub mod sync;
10pub mod unsync;
11
12macro_rules! thin_cell {
13    {
14        $( #[$doc:meta] )*
15    } => {
16        use std::{
17            any::{Any, TypeId},
18            cell::UnsafeCell,
19            fmt::{self, Debug, Display},
20            marker::PhantomData,
21            mem::ManuallyDrop,
22            ops::{Deref, DerefMut},
23            ptr::NonNull,
24        };
25
26        use crate::fat_ptr::*;
27
28        /// The inner allocation of `ThinCell`
29        ///
30        /// This should not be used except in unsize coercion solely as a type.
31        #[repr(C)]
32        pub struct Inner<T: ?Sized> {
33            // metadata MUST be at offset 0 so that `*mut Inner<T>` is also a valid `*mut usize`
34            // points to the metadata
35            metadata: usize,
36            state: State,
37            data: UnsafeCell<T>,
38        }
39
40        $( #[$doc] )*
41        pub struct ThinCell<T: ?Sized> {
42            ptr: NonNull<()>,
43            _marker: PhantomData<Inner<T>>,
44        }
45
46        /// A mutable guard returned by [`ThinCell::borrow`]
47        pub struct Ref<'a, T: ?Sized> {
48            value: &'a mut T,
49            state: &'a State,
50        }
51
52        impl<T> ThinCell<T> {
53            /// Creates a new `ThinCell` wrapping the given data.
54            pub fn new(data: T) -> Self {
55                let alloc = Box::new(Inner {
56                    metadata: 0,
57                    state: State::new(),
58                    data: UnsafeCell::new(data),
59                });
60
61                let ptr = Box::into_raw(alloc);
62
63                ThinCell {
64                    ptr: unsafe { NonNull::new_unchecked(ptr as _) },
65                    _marker: PhantomData,
66                }
67            }
68
69            /// Consumes the `ThinCell` and try to get inner value.
70            ///
71            /// Returns the inner value in [`Ok`] if there are no other owners and it is
72            /// not currently borrowed, return `Err(self)` otherwise.
73            pub fn try_unwrap(self) -> Result<T, Self> {
74                if !self.inner().state.try_unwrap() {
75                    return Err(self);
76                }
77
78                // SAFETY: As tested above, there are no other owners and it is not borrowed
79                Ok(unsafe { self.unwrap_unchecked() })
80            }
81
82            /// Consumes the `ThinCell`, returning the inner value.
83            ///
84            /// # Safety
85            /// The caller must guarantee that there are no other owners and it is not
86            /// currently borrowed.
87            pub unsafe fn unwrap_unchecked(self) -> T {
88                let this = ManuallyDrop::new(self);
89                // SAFETY: guaranteed by caller to have unique ownership and is not borrowed
90                let inner = unsafe { Box::from_raw(this.inner_ptr() as *mut Inner<T>) };
91
92                inner.data.into_inner()
93            }
94        }
95
96        impl<T: ?Sized> ThinCell<T> {
97            const IS_SIZED: bool = is_sized::<T>();
98
99            /// Reconstructs the raw pointer to the inner allocation.
100            fn inner_ptr(&self) -> *const Inner<T> {
101                let ptr = self.ptr.as_ptr();
102
103                if Self::IS_SIZED {
104                    // SIZED CASE: Cast pointer-to-pointer
105                    // Doing this trick to workaround Rust not allowing `ptr as *const Inner<T>`
106                    // due to `T` being `?Sized` directly even when we know it's `Sized`
107                    let ptr_ref = &ptr as *const *mut () as *const *const Inner<T>;
108
109                    // SAFETY: `self.ptr` is a valid pointer of `Inner<T>`
110                    unsafe { *ptr_ref }
111                } else {
112                    // UNSIZED CASE: Read metadata
113                    // SAFETY: pointer returned by `self.ptr` is valid, and `metadata` is at offset
114                    // 0 of `Inner<T>`, which is guaranteed by `repr(C)` and the definition of
115                    // `Inner<T>`
116                    let metadata = unsafe { *(ptr as *const usize) };
117
118                    // Miri will complain about this:
119                    // - https://github.com/thepowersgang/stack_dst-rs/issues/14
120                    // - https://github.com/uazu/stakker/blob/5821c30409c19ca9167808b669c928c94bc5f177/src/queue/flat.rs#L14-L17
121                    // But this should be sound as per Rust's fat pointer and metadata construction
122                    FatPtr { ptr, metadata }.into_ptr()
123                }
124            }
125
126            /// Returns a reference to the inner allocation.
127            fn inner(&self) -> &Inner<T> {
128                unsafe { &*self.inner_ptr() }
129            }
130
131            /// Returns a reference to the state cell.
132            fn state(&self) -> &State {
133                &self.inner().state
134            }
135
136            /// Deallocates the inner allocation.
137            ///
138            /// # Safety
139            ///
140            /// `self` must be the last owner and it must not be used after this call.
141            unsafe fn drop_in_place(&mut self) {
142                drop(unsafe { Box::from_raw(self.inner_ptr() as *mut Inner<T>) })
143            }
144
145            /// Leaks the `ThinCell`, returning a raw pointer to the inner allocation.
146            ///
147            /// The returned pointer points to the inner allocation. To restore the
148            /// `ThinCell`, use [`ThinCell::from_raw`].
149            pub fn leak(self) -> *mut () {
150                let this = ManuallyDrop::new(self);
151                this.ptr.as_ptr()
152            }
153
154            /// Reconstructs a `ThinCell<T>` from a raw pointer.
155            ///
156            /// # Safety
157            /// The pointer must have been obtained from a previous call to
158            /// [`ThinCell::leak`], and the [`ThinCell`] must not have been dropped in
159            /// the meantime.
160            pub unsafe fn from_raw(ptr: *mut ()) -> Self {
161                ThinCell {
162                    // SAFETY: caller guarantees `ptr` is valid
163                    ptr: unsafe { NonNull::new_unchecked(ptr) },
164                    _marker: PhantomData,
165                }
166            }
167
168            /// Returns the number of owners.
169            pub fn count(&self) -> usize {
170                self.state().load().count()
171            }
172
173            /// Borrows the value mutably.
174            ///
175            /// Returns a [`Ref`] guard that provides mutable access to the inner value.
176            /// The borrow lasts until the guard is dropped. See module-level documentation
177            /// for details on borrowing behavior.
178            ///
179            /// # Examples
180            ///
181            /// ```
182            /// # use thin_cell::unsync::ThinCell;
183            /// let cell = ThinCell::new(5);
184            ///
185            /// {
186            ///     let mut borrowed = cell.borrow();
187            ///     *borrowed = 10;
188            /// } // borrow is released here
189            ///
190            /// assert_eq!(*cell.borrow(), 10);
191            /// ```
192            pub fn borrow(&self) -> Ref<'_, T> {
193                let inner = self.inner();
194                inner.state.borrow();
195
196                // SAFETY: We have exclusive access via borrow flag and block further access
197                // with `Ordering::Acquire`/`Release` pair.
198                let value = unsafe { &mut *inner.data.get() };
199
200                Ref {
201                    value,
202                    state: &inner.state,
203                }
204            }
205
206            /// Attempts to borrow the value mutably.
207            ///
208            /// Returns `Some(Ref)` if the value is not currently borrowed, or `None` if
209            /// it is already borrowed.
210            ///
211            /// This is the non-blocking variant of [`borrow`](ThinCell::borrow).
212            ///
213            /// # Examples
214            ///
215            /// ```
216            /// # use thin_cell::unsync::ThinCell;
217            /// let cell = ThinCell::new(5);
218            ///
219            /// let borrow1 = cell.borrow();
220            /// assert!(cell.try_borrow().is_none()); // Already borrowed
221            /// drop(borrow1);
222            /// assert!(cell.try_borrow().is_some()); // Now available
223            /// ```
224            pub fn try_borrow(&self) -> Option<Ref<'_, T>> {
225                let inner = self.inner();
226                if !inner.state.try_borrow() {
227                    return None;
228                }
229
230                // SAFETY: We have exclusive access via borrow flag and block further access
231                // with `Ordering::Acquire`/`Release` pair.
232                let value = unsafe { &mut *inner.data.get() };
233
234                Some(Ref {
235                    value,
236                    state: &inner.state,
237                })
238            }
239
240            /// Get a mutable reference to the inner value without any checks.
241            ///
242            /// # Safety
243            ///
244            /// The caller must guarantee that there are no other owners and it is not
245            /// borrowed now and during the entire lifetime of the returned reference.
246            pub unsafe fn borrow_unchecked(&mut self) -> &mut T {
247                let inner = self.inner();
248                unsafe { &mut *inner.data.get() }
249            }
250
251            /// Creates a new `ThinCell<U>` from `data: U` and coerces it to
252            /// `ThinCell<T>`.
253            ///
254            /// # Safety
255            ///
256            /// `coerce` function must ensure the returned pointer is:
257            ///
258            /// - a valid unsizing of `Inner<T>`, e.g., some `Inner<dyn Trait>` or
259            ///   `Inner<[_]>`
260            /// - with same address (bare data pointer without metadata) as input
261            pub unsafe fn new_unsize<U>(
262                data: U,
263                coerce: impl Fn(*const Inner<U>) -> *const Inner<T>,
264            ) -> Self {
265                let this = ThinCell::new(data);
266                // SAFETY: We're holding unique ownership and is not borrowed.
267                unsafe { this.unsize_unchecked(coerce) }
268            }
269
270            /// Manually coerce to unsize.
271            ///
272            /// # Safety
273            ///
274            /// `coerce` has the same requirements as [`ThinCell::new_unsize`].
275            ///
276            /// # Panics
277            ///
278            /// Panics if the `ThinCell` is currently shared (count > 1) or borrowed.
279            ///
280            /// See [`ThinCell::unsize_unchecked`] for details.
281            pub unsafe fn unsize<U: ?Sized>(
282                self,
283                coerce: impl Fn(*const Inner<T>) -> *const Inner<U>,
284            ) -> ThinCell<U> {
285                let inner = self.inner();
286                let s = inner.state.load();
287
288                assert!(!s.is_shared(), "Cannot coerce shared `ThinCell`");
289                assert!(!s.is_borrowed(), "Cannot coerce borrowed `ThinCell`");
290
291                // SAFETY: As tested above, the `ThinCell` is:
292                // - not shared, and
293                // - not borrowed
294                // - validity of `coerce` is guaranteed by caller
295                unsafe { self.unsize_unchecked(coerce) }
296            }
297
298            /// Manually coerce to unsize without checks.
299            ///
300            /// # Safety
301            ///
302            /// - The `ThinCell` must have unique ownership (count == 1)
303            /// - The `ThinCell` must not be borrowed
304            /// - `coerce` has the same requirements as [`ThinCell::new_unsize`].
305            ///
306            /// In particular, first two requirement is the exact state after
307            /// [`ThinCell::new`].
308            pub unsafe fn unsize_unchecked<U: ?Sized>(
309                self,
310                coerce: impl Fn(*const Inner<T>) -> *const Inner<U>,
311            ) -> ThinCell<U> {
312                let this = ManuallyDrop::new(self);
313
314                let old_ptr = this.inner_ptr();
315                let fat_ptr = coerce(old_ptr);
316
317                let FatPtr { ptr, metadata } = FatPtr::from_ptr::<Inner<U>>(fat_ptr);
318
319                // SAFETY: `Inner` is `repr(C)` and has `metadata` at offset 0
320                unsafe { *(old_ptr as *mut usize) = metadata };
321
322                ThinCell {
323                    // SAFETY: `ptr` is valid as it comes from `self`
324                    ptr: unsafe { NonNull::new_unchecked(ptr) },
325                    _marker: PhantomData,
326                }
327            }
328
329            /// Returns the raw pointer to the inner allocation.
330            pub fn as_ptr(&self) -> *const () {
331                self.ptr.as_ptr()
332            }
333
334            /// Returns `true` if the two `ThinCell`s point to the same allocation.
335            pub fn ptr_eq(&self, other: &Self) -> bool {
336                std::ptr::eq(self.as_ptr(), other.as_ptr())
337            }
338
339            /// Downcasts the `ThinCell<T>` to `ThinCell<U>`.
340            ///
341            /// # Safety
342            ///
343            /// The caller must make sure that the inner value is actually of type `U`.
344            pub unsafe fn downcast_unchecked<U>(self) -> ThinCell<U> {
345                let this = ManuallyDrop::new(self);
346
347                ThinCell {
348                    ptr: this.ptr,
349                    _marker: PhantomData,
350                }
351            }
352        }
353
354        impl<T, const N: usize> ThinCell<[T; N]> {
355            /// Coerce an array [`ThinCell`] to a slice one.
356            pub fn unsize_slice(self) -> ThinCell<[T]> {
357                // Safety: unsized coercion from array to slice is safe
358                unsafe { self.unsize(|ptr| ptr as _) }
359            }
360        }
361
362        /// Error returned by [`ThinCell::downcast`] when downcasting fails.
363        #[derive(Debug)]
364        pub enum DowncastError<T: ?Sized> {
365            /// The [`ThinCell`] is currently borrowed.
366            Borrowed(ThinCell<T>),
367
368            /// The inner value is not of the target type.
369            Type(ThinCell<T>),
370        }
371
372        impl<T: ?Sized> DowncastError<T> {
373            /// Consumes the error and returns the original `ThinCell<T>`.
374            pub fn into_inner(self) -> ThinCell<T> {
375                match self {
376                    DowncastError::Borrowed(cell) | DowncastError::Type(cell) => cell,
377                }
378            }
379        }
380
381        impl<T: Any + ?Sized> ThinCell<T> {
382            /// Attempts to downcast the `ThinCell<T>` to `ThinCell<U>`.
383            ///
384            /// # Returns
385            ///
386            /// - `Ok(ThinCell<U>)` if the inner value is of type `U` and is not
387            ///   currently borrowed
388            /// - `Err(DowncastError::Borrowed(self))` if the inner value is currently
389            ///   borrowed
390            /// - `Err(DowncastError::Type(self))` if the inner value is not of type `U`
391            pub fn downcast<U: Any>(self) -> Result<ThinCell<U>, DowncastError<T>> {
392                let inner = self.inner();
393                if !inner.state.try_borrow() {
394                    return Err(DowncastError::Borrowed(self));
395                }
396
397                // SAFETY: We have exclusive access via borrow flag.
398                let data_ref = unsafe { &*inner.data.get() };
399                let type_id = data_ref.type_id();
400                inner.state.unborrow();
401
402                if TypeId::of::<U>() == type_id {
403                    // SAFETY: We have verified that the inner value is of type `U`
404                    Ok(unsafe { self.downcast_unchecked::<U>() })
405                } else {
406                    Err(DowncastError::Type(self))
407                }
408            }
409        }
410
411        /// `ThinCell` is `Unpin` as it does not move its inner data.
412        impl<T: ?Sized> Unpin for ThinCell<T> {}
413
414        impl<'a, T: ?Sized> Drop for Ref<'a, T> {
415            fn drop(&mut self) {
416                self.state.unborrow();
417            }
418        }
419
420        impl<'a, T: ?Sized> Deref for Ref<'a, T> {
421            type Target = T;
422
423            fn deref(&self) -> &T {
424                self.value
425            }
426        }
427
428        impl<'a, T: ?Sized> DerefMut for Ref<'a, T> {
429            fn deref_mut(&mut self) -> &mut T {
430                self.value
431            }
432        }
433
434        impl<'a, T: Debug + ?Sized> Debug for Ref<'a, T> {
435            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436                Debug::fmt(&**self, f)
437            }
438        }
439
440        impl<'a, T: Display + ?Sized> Display for Ref<'a, T> {
441            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442                Display::fmt(&**self, f)
443            }
444        }
445
446        impl<T: ?Sized> Clone for ThinCell<T> {
447            fn clone(&self) -> Self {
448                self.state().inc();
449
450                ThinCell {
451                    ptr: self.ptr,
452                    _marker: PhantomData,
453                }
454            }
455        }
456
457        impl<T: ?Sized> Drop for ThinCell<T> {
458            fn drop(&mut self) {
459                let inner = self.inner();
460                if !inner.state.dec() {
461                    // Not last owner, nothing to do
462                    return;
463                }
464
465                // SAFETY: We are the last owner, so we have unique ownership, and we're not
466                // using `self` after this call.
467                unsafe {
468                    self.drop_in_place();
469                }
470            }
471        }
472
473        impl<T: Default> Default for ThinCell<T> {
474            fn default() -> Self {
475                ThinCell::new(T::default())
476            }
477        }
478
479        impl<T: Debug + ?Sized> Debug for ThinCell<T> {
480            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
481                let inner = self.inner();
482                let state = inner.state.load();
483                let mut d = f.debug_struct("ThinCell");
484                match self.try_borrow() {
485                    Some(borrowed) => d.field("value", &borrowed),
486                    None => d.field("value", &"<borrowed>"),
487                }
488                .field("state", &state)
489                .finish()
490            }
491        }
492
493        impl<T: Display + ?Sized> Display for ThinCell<T> {
494            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
495                match self.try_borrow() {
496                    Some(borrowed) => Display::fmt(&*borrowed, f),
497                    None => write!(f, "<borrowed>"),
498                }
499            }
500        }
501
502        impl<T: PartialEq + ?Sized> PartialEq<ThinCell<T>> for ThinCell<T> {
503            /// Compares the inner values for equality.
504            ///
505            /// This will block on `sync` version or panic on `unsync` version if either `ThinCell` is currently borrowed.
506            ///
507            /// If a shallow comparison is desired, use [`ptr_eq`](ThinCell::ptr_eq)
508            /// instead.
509            fn eq(&self, other: &Self) -> bool {
510                self.borrow().eq(&other.borrow())
511            }
512        }
513
514        impl<T: Eq + ?Sized> Eq for ThinCell<T> {}
515
516        #[allow(clippy::non_canonical_partial_ord_impl)]
517        impl<T: Ord + ?Sized> PartialOrd<ThinCell<T>> for ThinCell<T> {
518            /// Compares the inner values.
519            ///
520            /// This will block on `sync` version or panic on `unsync` version if either `ThinCell` is currently borrowed.
521            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
522                self.borrow().partial_cmp(&other.borrow())
523            }
524        }
525
526        impl<T: Ord + ?Sized> Ord for ThinCell<T> {
527            /// Compares the inner values.
528            ///
529            /// This will block on `sync` version or panic on `unsync` version if either `ThinCell` is currently borrowed.
530            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
531                self.borrow().cmp(&other.borrow())
532            }
533        }
534    }
535}
536
537use thin_cell;