thin_cell/lib.rs
1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3#![deny(rustdoc::broken_intra_doc_links)]
4
5use std::{
6 cell::{Cell, UnsafeCell},
7 marker::PhantomData,
8 mem::{ManuallyDrop, size_of},
9 ops::{Deref, DerefMut},
10 ptr::NonNull,
11};
12
13mod state;
14use state::State;
15
16mod fat_ptr;
17use fat_ptr::FatPtr;
18
19/// The inner allocation of `ThinCell`
20///
21/// This should not be used except in unsize coercion solely as a type.
22#[repr(C)]
23pub struct Inner<T: ?Sized> {
24 // metadata MUST be at offset 0 so that `*mut Inner<T>` is also a valid `*mut usize` points
25 // to the metadata
26 metadata: usize,
27 state: Cell<State>,
28 data: UnsafeCell<T>,
29}
30
31/// A compact (`1-usize`), single-threaded smart pointer combining `Rc`
32/// and `borrow_mut`-only `RefCell`
33pub struct ThinCell<T: ?Sized> {
34 ptr: NonNull<()>,
35 _marker: PhantomData<Inner<T>>,
36}
37
38/// A mutable guard returned by [`ThinCell::borrow`]
39pub struct Ref<'a, T: ?Sized> {
40 value: &'a mut T,
41 state_cell: &'a Cell<State>,
42}
43
44impl<T> ThinCell<T> {
45 /// Creates a new `ThinCell` wrapping the given data.
46 pub fn new(data: T) -> Self {
47 let inner = Inner {
48 metadata: 0,
49 state: Cell::new(State::new()),
50 data: UnsafeCell::new(data),
51 };
52
53 let ptr = Box::into_raw(Box::new(inner));
54
55 ThinCell {
56 ptr: unsafe { NonNull::new_unchecked(ptr as _) },
57 _marker: PhantomData,
58 }
59 }
60
61 /// Consumes the `ThinCell`, returning the inner value if there are no
62 /// other owners and it is not currently borrowed.
63 pub fn try_unwrap(self) -> Result<T, Self> {
64 let inner = self.inner();
65 let s = inner.state.get();
66
67 if s.count() != 1 || s.is_borrowed() {
68 return Err(self);
69 }
70
71 // SAFETY: As tested above, there are no other owners and it is not borrowed
72 Ok(unsafe { self.unwrap_unchecked() })
73 }
74
75 /// Consumes the `ThinCell`, returning the inner value.
76 ///
77 /// # Safety
78 /// The caller must guarantee that there are no other owners and it is not
79 /// currently borrowed.
80 pub unsafe fn unwrap_unchecked(self) -> T {
81 let this = ManuallyDrop::new(self);
82 // SAFETY: guaranteed by caller to have unique ownership and is not borrowed
83 let inner = unsafe { Box::from_raw(this.inner_ptr() as *mut Inner<T>) };
84
85 inner.data.into_inner()
86 }
87}
88
89impl<T: ?Sized> ThinCell<T> {
90 const SIZED: bool = size_of::<*const Inner<T>>() == size_of::<usize>();
91
92 /// Creates a new `ThinCell<U>` from `data: U` and coerces it to
93 /// `ThinCell<T>`.
94 ///
95 /// # Safety
96 /// `coerce` function must ensure the returned pointer is:
97 /// - a valid unsizing of `T`, e.g., some `dyn Trait` with concrete type `U`
98 /// - with same address (bare data pointer without metadata) as input
99 pub unsafe fn new_unsize<U>(
100 data: U,
101 coerce: impl Fn(*const Inner<U>) -> *const Inner<T>,
102 ) -> Self {
103 let this = ThinCell::new(data);
104 // SAFETY: We're holding unique ownership and is not borrowed.
105 unsafe { this.unsize_unchecked(coerce) }
106 }
107
108 /// Reconstructs the raw pointer to the inner allocation.
109 fn inner_ptr(&self) -> *const Inner<T> {
110 unsafe {
111 let ptr = self.ptr.as_ptr();
112
113 if Self::SIZED {
114 // SIZED CASE: Cast pointer-to-pointer
115 // Doing this trick to workaround Rust not allowing `ptr as *const Inner<T>`
116 // due to `T` being `?Sized` directly even when we know it's `Sized`
117 let ptr_ref = &ptr as *const *mut () as *const *const Inner<T>;
118 *ptr_ref
119 } else {
120 // UNSIZED CASE: Read metadata
121 let metadata = *(ptr as *const usize);
122
123 // Miri will complain about this:
124 // - https://github.com/thepowersgang/stack_dst-rs/issues/14
125 // - https://github.com/uazu/stakker/blob/5821c30409c19ca9167808b669c928c94bc5f177/src/queue/flat.rs#L14-L17
126 // But this should be sound as per Rust's fat pointer and metadata construction
127 FatPtr { ptr, metadata }.into_ptr()
128 }
129 }
130 }
131
132 /// Returns a reference to the inner allocation.
133 fn inner(&self) -> &Inner<T> {
134 unsafe { &*self.inner_ptr() }
135 }
136
137 /// Deallocates the inner allocation.
138 ///
139 /// # Safety
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.inner().state.get().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.
177 ///
178 /// # Panics
179 ///
180 /// Panics if the value is already borrowed. For a non-panicking variant,
181 /// use [`try_borrow`](ThinCell::try_borrow).
182 ///
183 /// # Examples
184 ///
185 /// ```
186 /// # use thin_cell::ThinCell;
187 /// let cell = ThinCell::new(5);
188 ///
189 /// {
190 /// let mut borrowed = cell.borrow();
191 /// *borrowed = 10;
192 /// } // borrow is released here
193 ///
194 /// assert_eq!(*cell.borrow(), 10);
195 /// ```
196 pub fn borrow(&self) -> Ref<'_, T> {
197 let inner = self.inner();
198 inner.state.update(State::borrow);
199
200 // SAFETY: We have exclusive access via borrow flag
201 let value = unsafe { &mut *inner.data.get() };
202
203 Ref {
204 value,
205 state_cell: &inner.state,
206 }
207 }
208
209 /// Attempts to borrow the value mutably.
210 ///
211 /// Returns `Some(Ref)` if the value is not currently borrowed, or `None` if
212 /// it is already borrowed.
213 ///
214 /// This is the non-panicking variant of [`borrow`](ThinCell::borrow).
215 ///
216 /// # Examples
217 ///
218 /// ```
219 /// # use thin_cell::ThinCell;
220 /// let cell = ThinCell::new(5);
221 ///
222 /// let borrow1 = cell.borrow();
223 /// assert!(cell.try_borrow().is_none()); // Already borrowed
224 /// drop(borrow1);
225 /// assert!(cell.try_borrow().is_some()); // Now available
226 /// ```
227 pub fn try_borrow(&self) -> Option<Ref<'_, T>> {
228 let inner = self.inner();
229 let state = inner.state.get().try_borrow()?;
230 inner.state.set(state);
231
232 // SAFETY: We have exclusive access via borrow flag
233 let value = unsafe { &mut *inner.data.get() };
234
235 Some(Ref {
236 value,
237 state_cell: &inner.state,
238 })
239 }
240
241 /// Manually coerce to unsize without checks
242 ///
243 /// The returned `U` must be a valid unsizing of `T`, i.e., some `dyn
244 /// Trait` with concrete type `T`. `&U` must be a fat pointer. If not, this
245 /// will fail to compile.
246 ///
247 /// # Safety
248 ///
249 /// The `ThinCell` must:
250 /// - have unique ownership (count == 1)
251 /// - not be borrowed
252 ///
253 /// In particular, this is the exact state after [`ThinCell::new`].
254 ///
255 /// `coerce` function must ensure the returned pointer:
256 /// - is a valid unsizing of `T`, e.g., some `dyn Trait` with concrete type
257 /// `U`
258 /// - consists the same address (bare data pointer without metadata) as the
259 /// input
260 pub unsafe fn unsize_unchecked<U: ?Sized>(
261 self,
262 coerce: impl Fn(*const Inner<T>) -> *const Inner<U>,
263 ) -> ThinCell<U> {
264 let this = ManuallyDrop::new(self);
265
266 let old_ptr = this.inner_ptr();
267 let fat_ptr = coerce(old_ptr);
268
269 let FatPtr { ptr, metadata } = FatPtr::from_ptr::<Inner<U>>(fat_ptr);
270
271 // SAFETY: `Inner` is `repr(C)` and has `metadata` at offset 0
272 unsafe { *(old_ptr as *mut usize) = metadata };
273
274 ThinCell {
275 // SAFETY: `ptr` is valid as it comes from `self`
276 ptr: unsafe { NonNull::new_unchecked(ptr) },
277 _marker: PhantomData,
278 }
279 }
280
281 /// Manually coerce to unsize with some checks.
282 ///
283 /// # Safety
284 /// `coerce` function must ensure the returned pointer is:
285 /// - a valid unsizing of `T`, e.g., some `dyn Trait` with concrete type `U`
286 /// - with same address (bare data pointer without metadata) as input
287 ///
288 /// See [`ThinCell::unsize_unchecked`] for details.
289 pub unsafe fn unsize<U: ?Sized>(
290 self,
291 coerce: impl Fn(*const Inner<T>) -> *const Inner<U>,
292 ) -> ThinCell<U> {
293 let inner = self.inner();
294 let s = inner.state.get();
295
296 assert!(!s.is_shared(), "Cannot coerce shared `ThinCell`");
297 assert!(!s.is_borrowed(), "Cannot coerce borrowed `ThinCell`");
298
299 // SAFETY: As tested above, the `ThinCell` is:
300 // - not shared, and
301 // - not borrowed
302 // - validity of `coerce` is guaranteed by caller
303 unsafe { self.unsize_unchecked(coerce) }
304 }
305}
306
307impl<'a, T: ?Sized> Drop for Ref<'a, T> {
308 fn drop(&mut self) {
309 let current = self.state_cell.get();
310 self.state_cell.set(current.unborrow());
311 }
312}
313
314impl<'a, T: ?Sized> Deref for Ref<'a, T> {
315 type Target = T;
316
317 fn deref(&self) -> &T {
318 self.value
319 }
320}
321
322impl<'a, T: ?Sized> DerefMut for Ref<'a, T> {
323 fn deref_mut(&mut self) -> &mut T {
324 self.value
325 }
326}
327
328impl<T: ?Sized> Clone for ThinCell<T> {
329 fn clone(&self) -> Self {
330 unsafe {
331 let inner = &*self.inner_ptr();
332 let current = inner.state.get();
333
334 match current.inc() {
335 Some(new_state) => inner.state.set(new_state),
336 None => panic!("Reference count overflow"),
337 }
338
339 ThinCell {
340 ptr: self.ptr,
341 _marker: PhantomData,
342 }
343 }
344 }
345}
346
347impl<T: ?Sized> Drop for ThinCell<T> {
348 fn drop(&mut self) {
349 unsafe {
350 let ptr = self.inner_ptr();
351 // SAFETY: pointer returned by `inner_ptr` is valid
352 let inner = &*ptr;
353 let current = inner.state.get();
354
355 // If count is 1, we are the last owner
356 if current.count() == 1 {
357 debug_assert!(!current.is_borrowed(), "Dropping while borrowed");
358 self.drop_in_place();
359 } else {
360 // Not last owner, decrement
361 inner.state.set(current.dec());
362 }
363 }
364 }
365}