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;