Skip to main content

reactive_graph/owner/
storage.rs

1use super::arena::{Arena, NodeId};
2use send_wrapper::SendWrapper;
3
4/// A trait for borrowing and taking data.
5pub trait StorageAccess<T> {
6    /// Borrows the value.
7    fn as_borrowed(&self) -> &T;
8
9    /// Takes the value.
10    fn into_taken(self) -> T;
11}
12
13impl<T> StorageAccess<T> for T {
14    fn as_borrowed(&self) -> &T {
15        self
16    }
17
18    fn into_taken(self) -> T {
19        self
20    }
21}
22
23impl<T> StorageAccess<T> for SendWrapper<T> {
24    fn as_borrowed(&self) -> &T {
25        self
26    }
27
28    fn into_taken(self) -> T {
29        self.take()
30    }
31}
32
33/// A way of storing an [`ArenaItem`](super::arena_item::ArenaItem), either as itself or with a wrapper to make it threadsafe.
34///
35/// This exists because all items stored in the arena must be `Send + Sync`, but in single-threaded
36/// environments you might want or need to use thread-unsafe types.
37pub trait Storage<T>: Send + Sync + 'static {
38    /// The type being stored, once it has been wrapped.
39    type Wrapped: StorageAccess<T> + Send + Sync + 'static;
40
41    /// Adds any needed wrapper to the type.
42    fn wrap(value: T) -> Self::Wrapped;
43
44    /// Applies the given function to the stored value, if it exists and can be accessed from this
45    /// thread.
46    fn try_with<U>(node: NodeId, fun: impl FnOnce(&T) -> U) -> Option<U>;
47
48    /// Applies the given function to a mutable reference to the stored value, if it exists and can be accessed from this
49    /// thread.
50    fn try_with_mut<U>(
51        node: NodeId,
52        fun: impl FnOnce(&mut T) -> U,
53    ) -> Option<U>;
54
55    /// Sets a new value for the stored value. If it has been disposed, returns `Some(T)`.
56    fn try_set(node: NodeId, value: T) -> Option<T>;
57
58    /// Takes an item from the arena if it exists and can be accessed from this thread.
59    /// If it cannot be casted, it will still be removed from the arena.
60    fn take(node: NodeId) -> Option<T>;
61}
62
63/// A form of [`Storage`] that stores the type as itself, with no wrapper.
64#[derive(Debug, Copy, Clone)]
65pub struct SyncStorage;
66
67impl<T> Storage<T> for SyncStorage
68where
69    T: Send + Sync + 'static,
70{
71    type Wrapped = T;
72
73    #[inline(always)]
74    fn wrap(value: T) -> Self::Wrapped {
75        value
76    }
77
78    fn try_with<U>(node: NodeId, fun: impl FnOnce(&T) -> U) -> Option<U> {
79        Arena::try_with(|arena| {
80            let m = arena.get(node);
81            m.and_then(|n| n.downcast_ref::<T>()).map(fun)
82        })
83        .flatten()
84    }
85
86    fn try_with_mut<U>(
87        node: NodeId,
88        fun: impl FnOnce(&mut T) -> U,
89    ) -> Option<U> {
90        Arena::try_with_mut(|arena| {
91            let m = arena.get_mut(node);
92            m.and_then(|n| n.downcast_mut::<T>()).map(fun)
93        })
94        .flatten()
95    }
96
97    fn try_set(node: NodeId, value: T) -> Option<T> {
98        Arena::try_with_mut(|arena| {
99            let m = arena.get_mut(node);
100            match m.and_then(|n| n.downcast_mut::<T>()) {
101                Some(inner) => {
102                    *inner = value;
103                    None
104                }
105                None => Some(value),
106            }
107        })
108        .flatten()
109    }
110
111    fn take(node: NodeId) -> Option<T> {
112        Arena::with_mut(|arena| {
113            let m = arena.remove(node)?;
114            match m.downcast::<T>() {
115                Ok(inner) => Some(*inner),
116                Err(_) => None,
117            }
118        })
119    }
120}
121
122/// A form of [`Storage`] that stores the type with a wrapper that makes it `Send + Sync`, but only
123/// allows it to be accessed from the thread on which it was created.
124#[derive(Debug, Copy, Clone)]
125pub struct LocalStorage;
126
127impl<T> Storage<T> for LocalStorage
128where
129    T: 'static,
130{
131    type Wrapped = SendWrapper<T>;
132
133    fn wrap(value: T) -> Self::Wrapped {
134        SendWrapper::new(value)
135    }
136
137    fn try_with<U>(node: NodeId, fun: impl FnOnce(&T) -> U) -> Option<U> {
138        Arena::with(|arena| {
139            let n = arena.get(node)?.downcast_ref::<SendWrapper<T>>()?;
140            // Accessing the value from another thread would panic in the
141            // `SendWrapper` deref; the trait contract is to return `None`.
142            if !n.valid() {
143                return None;
144            }
145            Some(fun(n))
146        })
147    }
148
149    fn try_with_mut<U>(
150        node: NodeId,
151        fun: impl FnOnce(&mut T) -> U,
152    ) -> Option<U> {
153        Arena::with_mut(|arena| {
154            let n = arena.get_mut(node)?.downcast_mut::<SendWrapper<T>>()?;
155            if !n.valid() {
156                return None;
157            }
158            Some(fun(&mut *n))
159        })
160    }
161
162    fn try_set(node: NodeId, value: T) -> Option<T> {
163        Arena::with_mut(|arena| {
164            match arena
165                .get_mut(node)
166                .and_then(|n| n.downcast_mut::<SendWrapper<T>>())
167            {
168                // Replacing the value drops the old `SendWrapper`, which would
169                // panic off-thread; only do so when accessed from its thread.
170                Some(inner) if inner.valid() => {
171                    *inner = SendWrapper::new(value);
172                    None
173                }
174                _ => Some(value),
175            }
176        })
177    }
178
179    fn take(node: NodeId) -> Option<T> {
180        Arena::with_mut(|arena| {
181            // Check thread validity before removing: taking the value off its
182            // owning thread would panic, so leave the entry in place instead.
183            let valid = arena
184                .get(node)
185                .and_then(|n| n.downcast_ref::<SendWrapper<T>>())
186                .map(|inner| inner.valid());
187            if valid != Some(true) {
188                return None;
189            }
190            let m = arena.remove(node)?;
191            match m.downcast::<SendWrapper<T>>() {
192                Ok(inner) => Some(inner.take()),
193                Err(_) => None,
194            }
195        })
196    }
197}