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