generational_box/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3
4use parking_lot::Mutex;
5use std::{
6    fmt::Debug,
7    marker::PhantomData,
8    num::NonZeroU64,
9    ops::{Deref, DerefMut},
10    sync::Arc,
11};
12
13pub use error::*;
14pub use references::*;
15pub use sync::SyncStorage;
16pub use unsync::UnsyncStorage;
17
18mod entry;
19mod error;
20mod references;
21mod sync;
22mod unsync;
23
24/// The type erased id of a generational box.
25#[derive(Clone, Copy, PartialEq, Eq, Hash)]
26pub struct GenerationalBoxId {
27    data_ptr: *const (),
28    generation: NonZeroU64,
29}
30
31// Safety: GenerationalBoxId is Send and Sync because there is no way to access the pointer.
32unsafe impl Send for GenerationalBoxId {}
33unsafe impl Sync for GenerationalBoxId {}
34
35impl Debug for GenerationalBoxId {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        f.write_fmt(format_args!("{:?}@{:?}", self.data_ptr, self.generation))?;
38        Ok(())
39    }
40}
41
42/// The core Copy state type. The generational box will be dropped when the [Owner] is dropped.
43pub struct GenerationalBox<T, S: 'static = UnsyncStorage> {
44    raw: GenerationalPointer<S>,
45    _marker: PhantomData<T>,
46}
47
48impl<T, S: AnyStorage> Debug for GenerationalBox<T, S> {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        self.raw.fmt(f)
51    }
52}
53
54impl<T, S: Storage<T>> GenerationalBox<T, S> {
55    /// Create a new generational box by leaking a value into the storage. This is useful for creating
56    /// a box that needs to be manually dropped with no owners.
57    #[track_caller]
58    pub fn leak(value: T, location: &'static std::panic::Location<'static>) -> Self {
59        let location = S::new(value, location);
60        Self {
61            raw: location,
62            _marker: PhantomData,
63        }
64    }
65
66    /// Create a new reference counted generational box by leaking a value into the storage. This is useful for creating
67    /// a box that needs to be manually dropped with no owners.
68    #[track_caller]
69    pub fn leak_rc(value: T, location: &'static std::panic::Location<'static>) -> Self {
70        let location = S::new_rc(value, location);
71        Self {
72            raw: location,
73            _marker: PhantomData,
74        }
75    }
76
77    /// Get the raw pointer to the value.
78    pub fn raw_ptr(&self) -> *const () {
79        self.raw.storage.data_ptr()
80    }
81
82    /// Get the id of the generational box.
83    pub fn id(&self) -> GenerationalBoxId {
84        self.raw.id()
85    }
86
87    /// Try to read the value. Returns an error if the value is no longer valid.
88    #[track_caller]
89    pub fn try_read(&self) -> Result<S::Ref<'static, T>, BorrowError> {
90        self.raw.try_read()
91    }
92
93    /// Read the value. Panics if the value is no longer valid.
94    #[track_caller]
95    pub fn read(&self) -> S::Ref<'static, T> {
96        self.try_read().unwrap()
97    }
98
99    /// Try to write the value. Returns None if the value is no longer valid.
100    #[track_caller]
101    pub fn try_write(&self) -> Result<S::Mut<'static, T>, BorrowMutError> {
102        self.raw.try_write()
103    }
104
105    /// Write the value. Panics if the value is no longer valid.
106    #[track_caller]
107    pub fn write(&self) -> S::Mut<'static, T> {
108        self.try_write().unwrap()
109    }
110
111    /// Set the value. Panics if the value is no longer valid.
112    #[track_caller]
113    pub fn set(&self, value: T)
114    where
115        T: 'static,
116    {
117        *self.write() = value;
118    }
119
120    /// Returns true if the pointer is equal to the other pointer.
121    pub fn ptr_eq(&self, other: &Self) -> bool {
122        self.raw == other.raw
123    }
124
125    /// Drop the value out of the generational box and invalidate the generational box.
126    pub fn manually_drop(&self)
127    where
128        T: 'static,
129    {
130        self.raw.recycle();
131    }
132
133    /// Try to get the location the generational box was created at. In release mode this will always return None.
134    pub fn created_at(&self) -> Option<&'static std::panic::Location<'static>> {
135        self.raw.location.created_at()
136    }
137
138    /// Get a reference to the value
139    #[track_caller]
140    pub fn leak_reference(&self) -> BorrowResult<GenerationalBox<T, S>> {
141        Ok(Self {
142            raw: S::new_reference(self.raw)?,
143            _marker: std::marker::PhantomData,
144        })
145    }
146
147    /// Change this box to point to another generational box
148    pub fn point_to(&self, other: GenerationalBox<T, S>) -> BorrowResult {
149        S::change_reference(self.raw, other.raw)
150    }
151}
152
153impl<T, S> Copy for GenerationalBox<T, S> {}
154
155impl<T, S> Clone for GenerationalBox<T, S> {
156    fn clone(&self) -> Self {
157        *self
158    }
159}
160
161/// A trait for a storage backing type. (RefCell, RwLock, etc.)
162pub trait Storage<Data = ()>: AnyStorage + 'static {
163    /// Try to read the value. Returns None if the value is no longer valid.
164    fn try_read(pointer: GenerationalPointer<Self>) -> BorrowResult<Self::Ref<'static, Data>>;
165
166    /// Try to write the value. Returns None if the value is no longer valid.
167    fn try_write(pointer: GenerationalPointer<Self>) -> BorrowMutResult<Self::Mut<'static, Data>>;
168
169    /// Create a new memory location. This will either create a new memory location or recycle an old one.
170    fn new(
171        value: Data,
172        caller: &'static std::panic::Location<'static>,
173    ) -> GenerationalPointer<Self>;
174
175    /// Create a new reference counted memory location. This will either create a new memory location or recycle an old one.
176    fn new_rc(
177        value: Data,
178        caller: &'static std::panic::Location<'static>,
179    ) -> GenerationalPointer<Self>;
180
181    /// Reference another location if the location is valid
182    ///
183    /// This method may return an error if the other box is no longer valid or it is already borrowed mutably.
184    fn new_reference(inner: GenerationalPointer<Self>) -> BorrowResult<GenerationalPointer<Self>>;
185
186    /// Change the reference a signal is pointing to
187    ///
188    /// This method may return an error if the other box is no longer valid or it is already borrowed mutably.
189    fn change_reference(
190        pointer: GenerationalPointer<Self>,
191        rc_pointer: GenerationalPointer<Self>,
192    ) -> BorrowResult;
193}
194
195/// A trait for any storage backing type.
196pub trait AnyStorage: Default + 'static {
197    /// The reference this storage type returns.
198    type Ref<'a, T: ?Sized + 'static>: Deref<Target = T>;
199    /// The mutable reference this storage type returns.
200    type Mut<'a, T: ?Sized + 'static>: DerefMut<Target = T>;
201
202    /// Downcast a reference in a Ref to a more specific lifetime
203    ///
204    /// This function enforces the variance of the lifetime parameter `'a` in Ref. Rust will typically infer this cast with a concrete type, but it cannot with a generic type.
205    fn downcast_lifetime_ref<'a: 'b, 'b, T: ?Sized + 'static>(
206        ref_: Self::Ref<'a, T>,
207    ) -> Self::Ref<'b, T>;
208
209    /// Downcast a mutable reference in a RefMut to a more specific lifetime
210    ///
211    /// This function enforces the variance of the lifetime parameter `'a` in Mut.  Rust will typically infer this cast with a concrete type, but it cannot with a generic type.
212    fn downcast_lifetime_mut<'a: 'b, 'b, T: ?Sized + 'static>(
213        mut_: Self::Mut<'a, T>,
214    ) -> Self::Mut<'b, T>;
215
216    /// Try to map the mutable ref.
217    fn try_map_mut<T: ?Sized + 'static, U: ?Sized + 'static>(
218        mut_ref: Self::Mut<'_, T>,
219        f: impl FnOnce(&mut T) -> Option<&mut U>,
220    ) -> Option<Self::Mut<'_, U>>;
221
222    /// Map the mutable ref.
223    fn map_mut<T: ?Sized + 'static, U: ?Sized + 'static>(
224        mut_ref: Self::Mut<'_, T>,
225        f: impl FnOnce(&mut T) -> &mut U,
226    ) -> Self::Mut<'_, U> {
227        Self::try_map_mut(mut_ref, |v| Some(f(v))).unwrap()
228    }
229
230    /// Try to map the ref.
231    fn try_map<T: ?Sized, U: ?Sized + 'static>(
232        ref_: Self::Ref<'_, T>,
233        f: impl FnOnce(&T) -> Option<&U>,
234    ) -> Option<Self::Ref<'_, U>>;
235
236    /// Map the ref.
237    fn map<T: ?Sized, U: ?Sized + 'static>(
238        ref_: Self::Ref<'_, T>,
239        f: impl FnOnce(&T) -> &U,
240    ) -> Self::Ref<'_, U> {
241        Self::try_map(ref_, |v| Some(f(v))).unwrap()
242    }
243
244    /// Get the data pointer. No guarantees are made about the data pointer. It should only be used for debugging.
245    fn data_ptr(&self) -> *const ();
246
247    /// Recycle a memory location. This will drop the memory location and return it to the runtime.
248    fn recycle(location: GenerationalPointer<Self>);
249
250    /// Create a new owner. The owner will be responsible for dropping all of the generational boxes that it creates.
251    fn owner() -> Owner<Self> {
252        Owner(Arc::new(Mutex::new(OwnerInner {
253            owned: Default::default(),
254        })))
255    }
256}
257
258#[derive(Debug, Clone, Copy)]
259pub(crate) struct GenerationalLocation {
260    /// The generation this location is associated with. Using the location after this generation is invalidated will return errors.
261    generation: NonZeroU64,
262    #[cfg(any(debug_assertions, feature = "debug_ownership"))]
263    created_at: &'static std::panic::Location<'static>,
264}
265
266impl GenerationalLocation {
267    pub(crate) fn created_at(&self) -> Option<&'static std::panic::Location<'static>> {
268        #[cfg(debug_assertions)]
269        {
270            Some(self.created_at)
271        }
272        #[cfg(not(debug_assertions))]
273        {
274            None
275        }
276    }
277}
278
279/// A pointer to a specific generational box and generation in that box.
280pub struct GenerationalPointer<S: 'static = UnsyncStorage> {
281    /// The storage that is backing this location
282    storage: &'static S,
283    /// The location of the data
284    location: GenerationalLocation,
285}
286
287impl<S: AnyStorage + 'static> PartialEq for GenerationalPointer<S> {
288    fn eq(&self, other: &Self) -> bool {
289        self.storage.data_ptr() == other.storage.data_ptr()
290            && self.location.generation == other.location.generation
291    }
292}
293
294impl<S: AnyStorage + 'static> Debug for GenerationalPointer<S> {
295    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296        f.write_fmt(format_args!(
297            "{:?}@{:?}",
298            self.storage.data_ptr(),
299            self.location.generation
300        ))
301    }
302}
303
304impl<S: 'static> Clone for GenerationalPointer<S> {
305    fn clone(&self) -> Self {
306        *self
307    }
308}
309
310impl<S: 'static> Copy for GenerationalPointer<S> {}
311
312impl<S> GenerationalPointer<S> {
313    #[track_caller]
314    fn try_read<T>(self) -> Result<S::Ref<'static, T>, BorrowError>
315    where
316        S: Storage<T>,
317    {
318        S::try_read(self)
319    }
320
321    #[track_caller]
322    fn try_write<T>(self) -> Result<S::Mut<'static, T>, BorrowMutError>
323    where
324        S: Storage<T>,
325    {
326        S::try_write(self)
327    }
328
329    fn recycle(self)
330    where
331        S: AnyStorage,
332    {
333        S::recycle(self);
334    }
335
336    fn id(&self) -> GenerationalBoxId
337    where
338        S: AnyStorage,
339    {
340        GenerationalBoxId {
341            data_ptr: self.storage.data_ptr(),
342            generation: self.location.generation,
343        }
344    }
345}
346
347struct OwnerInner<S: AnyStorage + 'static> {
348    owned: Vec<GenerationalPointer<S>>,
349}
350
351impl<S: AnyStorage> Drop for OwnerInner<S> {
352    fn drop(&mut self) {
353        for location in self.owned.drain(..) {
354            location.recycle();
355        }
356    }
357}
358
359/// Owner: Handles dropping generational boxes. The owner acts like a runtime lifetime guard. Any states that you create with an owner will be dropped when that owner is dropped.
360pub struct Owner<S: AnyStorage + 'static = UnsyncStorage>(Arc<Mutex<OwnerInner<S>>>);
361
362impl<S: AnyStorage> Default for Owner<S> {
363    fn default() -> Self {
364        S::owner()
365    }
366}
367
368impl<S: AnyStorage> Clone for Owner<S> {
369    fn clone(&self) -> Self {
370        Self(self.0.clone())
371    }
372}
373
374impl<S: AnyStorage> Owner<S> {
375    /// Insert a value into the store. The value will be dropped when the owner is dropped.
376    #[track_caller]
377    pub fn insert<T: 'static>(&self, value: T) -> GenerationalBox<T, S>
378    where
379        S: Storage<T>,
380    {
381        self.insert_with_caller(value, std::panic::Location::caller())
382    }
383
384    /// Create a new reference counted box. The box will be dropped when all references are dropped.
385    #[track_caller]
386    pub fn insert_rc<T: 'static>(&self, value: T) -> GenerationalBox<T, S>
387    where
388        S: Storage<T>,
389    {
390        self.insert_rc_with_caller(value, std::panic::Location::caller())
391    }
392
393    /// Insert a value into the store with a specific location blamed for creating the value. The value will be dropped when the owner is dropped.
394    pub fn insert_rc_with_caller<T: 'static>(
395        &self,
396        value: T,
397        caller: &'static std::panic::Location<'static>,
398    ) -> GenerationalBox<T, S>
399    where
400        S: Storage<T>,
401    {
402        let location = S::new_rc(value, caller);
403        self.0.lock().owned.push(location);
404        GenerationalBox {
405            raw: location,
406            _marker: std::marker::PhantomData,
407        }
408    }
409
410    /// Insert a value into the store with a specific location blamed for creating the value. The value will be dropped when the owner is dropped.
411    pub fn insert_with_caller<T: 'static>(
412        &self,
413        value: T,
414        caller: &'static std::panic::Location<'static>,
415    ) -> GenerationalBox<T, S>
416    where
417        S: Storage<T>,
418    {
419        let location = S::new(value, caller);
420        self.0.lock().owned.push(location);
421        GenerationalBox {
422            raw: location,
423            _marker: PhantomData,
424        }
425    }
426
427    /// Create a new reference to an existing box. The reference will be dropped when the owner is dropped.
428    ///
429    /// This method may return an error if the other box is no longer valid or it is already borrowed mutably.
430    #[track_caller]
431    pub fn insert_reference<T: 'static>(
432        &self,
433        other: GenerationalBox<T, S>,
434    ) -> BorrowResult<GenerationalBox<T, S>>
435    where
436        S: Storage<T>,
437    {
438        let location = other.leak_reference()?;
439        self.0.lock().owned.push(location.raw);
440        Ok(location)
441    }
442}