generational_box/
sync.rs

1use parking_lot::{
2    MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard,
3};
4use std::{
5    any::Any,
6    fmt::Debug,
7    num::NonZeroU64,
8    sync::{Arc, OnceLock},
9};
10
11use crate::{
12    entry::{MemoryLocationBorrowInfo, RcStorageEntry, StorageEntry},
13    error::{self, ValueDroppedError},
14    references::{GenerationalRef, GenerationalRefMut},
15    AnyStorage, BorrowError, BorrowMutError, BorrowMutResult, BorrowResult, GenerationalLocation,
16    GenerationalPointer, Storage,
17};
18
19type RwLockStorageEntryRef = RwLockReadGuard<'static, StorageEntry<RwLockStorageEntryData>>;
20type RwLockStorageEntryMut = RwLockWriteGuard<'static, StorageEntry<RwLockStorageEntryData>>;
21
22type AnyRef = MappedRwLockReadGuard<'static, Box<dyn Any + Send + Sync + 'static>>;
23type AnyRefMut = MappedRwLockWriteGuard<'static, Box<dyn Any + Send + Sync + 'static>>;
24
25#[derive(Default)]
26pub(crate) enum RwLockStorageEntryData {
27    Reference(GenerationalPointer<SyncStorage>),
28    Rc(RcStorageEntry<Box<dyn Any + Send + Sync>>),
29    Data(Box<dyn Any + Send + Sync>),
30    #[default]
31    Empty,
32}
33
34impl Debug for RwLockStorageEntryData {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        match self {
37            Self::Reference(location) => write!(f, "Reference({location:?})"),
38            Self::Rc(_) => write!(f, "Rc"),
39            Self::Data(_) => write!(f, "Data"),
40            Self::Empty => write!(f, "Empty"),
41        }
42    }
43}
44
45impl RwLockStorageEntryData {
46    pub const fn new_full(data: Box<dyn Any + Send + Sync>) -> Self {
47        Self::Data(data)
48    }
49}
50
51/// A thread safe storage. This is slower than the unsync storage, but allows you to share the value between threads.
52#[derive(Default)]
53pub struct SyncStorage {
54    borrow_info: MemoryLocationBorrowInfo,
55    data: RwLock<StorageEntry<RwLockStorageEntryData>>,
56}
57
58impl SyncStorage {
59    pub(crate) fn read(
60        pointer: GenerationalPointer<Self>,
61    ) -> BorrowResult<(AnyRef, GenerationalPointer<Self>)> {
62        Self::get_split_ref(pointer).map(|(resolved, guard)| {
63            (
64                RwLockReadGuard::map(guard, |data| match &data.data {
65                    RwLockStorageEntryData::Data(data) => data,
66                    RwLockStorageEntryData::Rc(data) => &data.data,
67                    _ => unreachable!(),
68                }),
69                resolved,
70            )
71        })
72    }
73
74    pub(crate) fn get_split_ref(
75        mut pointer: GenerationalPointer<Self>,
76    ) -> BorrowResult<(GenerationalPointer<Self>, RwLockStorageEntryRef)> {
77        loop {
78            let borrow = pointer.storage.data.read();
79            if !borrow.valid(&pointer.location) {
80                return Err(BorrowError::Dropped(ValueDroppedError::new_for_location(
81                    pointer.location,
82                )));
83            }
84            match &borrow.data {
85                // If this is a reference, keep traversing the pointers
86                RwLockStorageEntryData::Reference(data) => {
87                    pointer = *data;
88                }
89                // Otherwise return the value
90                RwLockStorageEntryData::Data(_) | RwLockStorageEntryData::Rc(_) => {
91                    return Ok((pointer, borrow));
92                }
93                RwLockStorageEntryData::Empty => {
94                    return Err(BorrowError::Dropped(ValueDroppedError::new_for_location(
95                        pointer.location,
96                    )));
97                }
98            }
99        }
100    }
101
102    pub(crate) fn write(
103        pointer: GenerationalPointer<Self>,
104    ) -> BorrowMutResult<(AnyRefMut, GenerationalPointer<Self>)> {
105        Self::get_split_mut(pointer).map(|(resolved, guard)| {
106            (
107                RwLockWriteGuard::map(guard, |data| match &mut data.data {
108                    RwLockStorageEntryData::Data(data) => data,
109                    RwLockStorageEntryData::Rc(data) => &mut data.data,
110                    _ => unreachable!(),
111                }),
112                resolved,
113            )
114        })
115    }
116
117    pub(crate) fn get_split_mut(
118        mut pointer: GenerationalPointer<Self>,
119    ) -> BorrowMutResult<(GenerationalPointer<Self>, RwLockStorageEntryMut)> {
120        loop {
121            let borrow = pointer.storage.data.write();
122            if !borrow.valid(&pointer.location) {
123                return Err(BorrowMutError::Dropped(
124                    ValueDroppedError::new_for_location(pointer.location),
125                ));
126            }
127            match &borrow.data {
128                // If this is a reference, keep traversing the pointers
129                RwLockStorageEntryData::Reference(data) => {
130                    pointer = *data;
131                }
132                // Otherwise return the value
133                RwLockStorageEntryData::Data(_) | RwLockStorageEntryData::Rc(_) => {
134                    return Ok((pointer, borrow));
135                }
136                RwLockStorageEntryData::Empty => {
137                    return Err(BorrowMutError::Dropped(
138                        ValueDroppedError::new_for_location(pointer.location),
139                    ));
140                }
141            }
142        }
143    }
144
145    fn create_new(
146        value: RwLockStorageEntryData,
147        #[allow(unused)] caller: &'static std::panic::Location<'static>,
148    ) -> GenerationalPointer<Self> {
149        match sync_runtime().lock().pop() {
150            Some(storage) => {
151                let mut write = storage.data.write();
152                let location = GenerationalLocation {
153                    generation: write.generation(),
154                    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
155                    created_at: caller,
156                };
157                write.data = value;
158                GenerationalPointer { storage, location }
159            }
160            None => {
161                let storage: &'static Self = &*Box::leak(Box::new(Self {
162                    borrow_info: Default::default(),
163                    data: RwLock::new(StorageEntry::new(value)),
164                }));
165
166                let location = GenerationalLocation {
167                    generation: NonZeroU64::MIN,
168                    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
169                    created_at: caller,
170                };
171
172                GenerationalPointer { storage, location }
173            }
174        }
175    }
176}
177
178static SYNC_RUNTIME: OnceLock<Arc<Mutex<Vec<&'static SyncStorage>>>> = OnceLock::new();
179
180fn sync_runtime() -> &'static Arc<Mutex<Vec<&'static SyncStorage>>> {
181    SYNC_RUNTIME.get_or_init(|| Arc::new(Mutex::new(Vec::new())))
182}
183
184impl AnyStorage for SyncStorage {
185    type Ref<'a, R: ?Sized + 'a> = GenerationalRef<MappedRwLockReadGuard<'a, R>>;
186    type Mut<'a, W: ?Sized + 'a> = GenerationalRefMut<MappedRwLockWriteGuard<'a, W>>;
187
188    fn downcast_lifetime_ref<'a: 'b, 'b, T: ?Sized + 'b>(
189        ref_: Self::Ref<'a, T>,
190    ) -> Self::Ref<'b, T> {
191        ref_
192    }
193
194    fn downcast_lifetime_mut<'a: 'b, 'b, T: ?Sized + 'a>(
195        mut_: Self::Mut<'a, T>,
196    ) -> Self::Mut<'b, T> {
197        mut_
198    }
199
200    fn map<T: ?Sized, U: ?Sized>(
201        ref_: Self::Ref<'_, T>,
202        f: impl FnOnce(&T) -> &U,
203    ) -> Self::Ref<'_, U> {
204        ref_.map(|inner| MappedRwLockReadGuard::map(inner, f))
205    }
206
207    fn map_mut<T: ?Sized, U: ?Sized>(
208        mut_ref: Self::Mut<'_, T>,
209        f: impl FnOnce(&mut T) -> &mut U,
210    ) -> Self::Mut<'_, U> {
211        mut_ref.map(|inner| MappedRwLockWriteGuard::map(inner, f))
212    }
213
214    fn try_map<I: ?Sized, U: ?Sized>(
215        ref_: Self::Ref<'_, I>,
216        f: impl FnOnce(&I) -> Option<&U>,
217    ) -> Option<Self::Ref<'_, U>> {
218        ref_.try_map(|inner| MappedRwLockReadGuard::try_map(inner, f).ok())
219    }
220
221    fn try_map_mut<I: ?Sized, U: ?Sized>(
222        mut_ref: Self::Mut<'_, I>,
223        f: impl FnOnce(&mut I) -> Option<&mut U>,
224    ) -> Option<Self::Mut<'_, U>> {
225        mut_ref.try_map(|inner| MappedRwLockWriteGuard::try_map(inner, f).ok())
226    }
227
228    fn data_ptr(&self) -> *const () {
229        self.data.data_ptr() as *const ()
230    }
231
232    fn recycle(pointer: GenerationalPointer<Self>) {
233        let mut borrow_mut = pointer.storage.data.write();
234
235        // First check if the generation is still valid
236        if !borrow_mut.valid(&pointer.location) {
237            return;
238        }
239
240        borrow_mut.increment_generation();
241
242        // Then decrement the reference count or drop the value if it's the last reference
243        match &mut borrow_mut.data {
244            // If this is the original reference, drop the value
245            RwLockStorageEntryData::Data(_) => borrow_mut.data = RwLockStorageEntryData::Empty,
246            // If this is a rc, just ignore the drop
247            RwLockStorageEntryData::Rc(_) => {}
248            // If this is a reference, decrement the reference count
249            RwLockStorageEntryData::Reference(reference) => {
250                drop_ref(*reference);
251            }
252            RwLockStorageEntryData::Empty => {}
253        }
254
255        sync_runtime().lock().push(pointer.storage);
256    }
257}
258
259fn drop_ref(pointer: GenerationalPointer<SyncStorage>) {
260    let mut borrow_mut = pointer.storage.data.write();
261
262    // First check if the generation is still valid
263    if !borrow_mut.valid(&pointer.location) {
264        return;
265    }
266
267    if let RwLockStorageEntryData::Rc(entry) = &mut borrow_mut.data {
268        // Decrement the reference count
269        if entry.drop_ref() {
270            // If the reference count is now zero, drop the value
271            borrow_mut.data = RwLockStorageEntryData::Empty;
272            sync_runtime().lock().push(pointer.storage);
273        }
274    } else {
275        unreachable!("References should always point to a data entry directly");
276    }
277}
278
279impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
280    #[track_caller]
281    fn try_read(
282        pointer: GenerationalPointer<Self>,
283    ) -> Result<Self::Ref<'static, T>, error::BorrowError> {
284        let (read, pointer) = Self::read(pointer)?;
285
286        let read = MappedRwLockReadGuard::try_map(read, |any| {
287            // Then try to downcast
288            any.downcast_ref()
289        });
290        match read {
291            Ok(guard) => Ok(GenerationalRef::new(
292                guard,
293                pointer.storage.borrow_info.borrow_guard(),
294            )),
295            Err(_) => Err(error::BorrowError::Dropped(
296                ValueDroppedError::new_for_location(pointer.location),
297            )),
298        }
299    }
300
301    #[track_caller]
302    fn try_write(
303        pointer: GenerationalPointer<Self>,
304    ) -> Result<Self::Mut<'static, T>, error::BorrowMutError> {
305        let (write, pointer) = Self::write(pointer)?;
306
307        let write = MappedRwLockWriteGuard::try_map(write, |any| {
308            // Then try to downcast
309            any.downcast_mut()
310        });
311        match write {
312            Ok(guard) => Ok(GenerationalRefMut::new(
313                guard,
314                pointer.storage.borrow_info.borrow_mut_guard(),
315            )),
316            Err(_) => Err(error::BorrowMutError::Dropped(
317                ValueDroppedError::new_for_location(pointer.location),
318            )),
319        }
320    }
321
322    fn new(value: T, caller: &'static std::panic::Location<'static>) -> GenerationalPointer<Self> {
323        Self::create_new(RwLockStorageEntryData::new_full(Box::new(value)), caller)
324    }
325
326    fn new_rc(
327        value: T,
328        caller: &'static std::panic::Location<'static>,
329    ) -> GenerationalPointer<Self> {
330        // Create the data that the rc points to
331        let data = Self::create_new(
332            RwLockStorageEntryData::Rc(RcStorageEntry::new(Box::new(value))),
333            caller,
334        );
335        Self::create_new(RwLockStorageEntryData::Reference(data), caller)
336    }
337
338    fn new_reference(
339        location: GenerationalPointer<Self>,
340    ) -> BorrowResult<GenerationalPointer<Self>> {
341        // Chase the reference to get the final location
342        let (location, value) = Self::get_split_ref(location)?;
343        if let RwLockStorageEntryData::Rc(data) = &value.data {
344            data.add_ref();
345        } else {
346            unreachable!()
347        }
348        Ok(Self::create_new(
349            RwLockStorageEntryData::Reference(location),
350            location
351                .location
352                .created_at()
353                .unwrap_or(std::panic::Location::caller()),
354        ))
355    }
356
357    fn change_reference(
358        location: GenerationalPointer<Self>,
359        other: GenerationalPointer<Self>,
360    ) -> BorrowResult {
361        if location == other {
362            return Ok(());
363        }
364
365        let (other_final, other_write) = Self::get_split_ref(other)?;
366
367        let mut write = location.storage.data.write();
368        // First check if the generation is still valid
369        if !write.valid(&location.location) {
370            return Err(BorrowError::Dropped(ValueDroppedError::new_for_location(
371                location.location,
372            )));
373        }
374
375        if let (RwLockStorageEntryData::Reference(reference), RwLockStorageEntryData::Rc(data)) =
376            (&mut write.data, &other_write.data)
377        {
378            if reference == &other_final {
379                return Ok(());
380            }
381            drop_ref(*reference);
382            *reference = other_final;
383            data.add_ref();
384        } else {
385            tracing::trace!(
386                "References should always point to a data entry directly found {:?} instead",
387                other_write.data
388            );
389            return Err(BorrowError::Dropped(ValueDroppedError::new_for_location(
390                other_final.location,
391            )));
392        }
393
394        Ok(())
395    }
396}