relm4/shared_state/state.rs
1use std::{
2 ops::{Deref, DerefMut},
3 sync::{RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockError},
4};
5
6use once_cell::sync::Lazy;
7
8use crate::Sender;
9
10use super::SubscriberFn;
11
12/// A type that allows you to share information across your
13/// application easily.
14/// Get immutable and mutable access to the data and subscribe to changes.
15///
16/// # Panics
17///
18/// [`SharedState`] uses a [`RwLock`] internally.
19/// If you use [`Self::read()`] and [`Self::write()`] in the same scope
20/// your code might be stuck in a deadlock or panic.
21pub struct SharedState<Data> {
22 data: Lazy<RwLock<Data>>,
23 subscribers: Lazy<RwLock<Vec<SubscriberFn<Data>>>>,
24}
25
26impl<Data: std::fmt::Debug> std::fmt::Debug for SharedState<Data> {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 f.debug_struct("SharedState")
29 .field("data", &self.data)
30 .field("subscribers", &self.subscribers.try_read().map(|s| s.len()))
31 .finish()
32 }
33}
34
35impl<Data> Default for SharedState<Data>
36where
37 Data: Default,
38{
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44impl<Data> SharedState<Data>
45where
46 Data: Default,
47{
48 /// Create a new [`SharedState`] variable.
49 ///
50 /// The data will be initialized lazily on the first access.
51 ///
52 /// # Example
53 ///
54 /// ```
55 /// # #[derive(Default)]
56 /// # struct MyData;
57 /// use relm4::SharedState;
58 /// static STATE: SharedState<MyData> = SharedState::new();
59 /// ```
60 #[must_use]
61 pub const fn new() -> Self {
62 Self {
63 data: Lazy::new(RwLock::default),
64 subscribers: Lazy::new(RwLock::default),
65 }
66 }
67
68 /// Subscribe to a shared state type.
69 /// Any subscriber will be notified with a message every time
70 /// you modify the shared state using [`Self::get_mut()`].
71 ///
72 /// ```
73 /// use relm4::SharedState;
74 /// static STATE: SharedState<u8> = SharedState::new();
75 ///
76 /// let (sender, receiver) = relm4::channel();
77 ///
78 /// // Every time we modify the data, we will receive
79 /// // the updated value as a message.
80 /// STATE.subscribe(&sender, |data| *data);
81 ///
82 /// {
83 /// let mut data = STATE.write();
84 /// *data += 1;
85 /// }
86 ///
87 /// assert_eq!(receiver.recv_sync().unwrap(), 1);
88 /// ```
89 pub fn subscribe<Msg, F>(&self, sender: &Sender<Msg>, f: F)
90 where
91 F: Fn(&Data) -> Msg + 'static + Send + Sync,
92 Msg: Send + 'static,
93 {
94 let sender = sender.clone();
95 self.subscribers
96 .write()
97 .unwrap()
98 .push(Box::new(move |data: &Data| {
99 let msg = f(data);
100 sender.send(msg).is_ok()
101 }));
102 }
103
104 /// An alternative version of [`subscribe()`](Self::subscribe()) that only send a message if
105 /// the closure returns [`Some`].
106 pub fn subscribe_optional<Msg, F>(&self, sender: &Sender<Msg>, f: F)
107 where
108 F: Fn(&Data) -> Option<Msg> + 'static + Send + Sync,
109 Msg: Send + 'static,
110 {
111 let sender = sender.clone();
112 self.subscribers
113 .write()
114 .unwrap()
115 .push(Box::new(move |data: &Data| {
116 if let Some(msg) = f(data) {
117 sender.send(msg).is_ok()
118 } else {
119 true
120 }
121 }));
122 }
123
124 /// Get immutable access to the shared data.
125 ///
126 /// Returns a RAII guard which will release this thread’s shared access
127 /// once it is dropped.
128 ///
129 /// The calling thread will be blocked until there are no more writers
130 /// which hold the lock (see [`RwLock`]).
131 ///
132 /// # Panics
133 ///
134 /// This function will panic if the internal [`RwLock`] is poisoned.
135 /// A [`RwLock`] is poisoned whenever a writer panics while holding an exclusive lock.
136 /// The failure will occur immediately after the lock has been acquired.
137 ///
138 /// Also, this function might panic when called if the lock is already
139 /// held by the current thread.
140 pub fn read(&self) -> SharedStateReadGuard<'_, Data> {
141 SharedStateReadGuard {
142 inner: self.data.read().unwrap(),
143 }
144 }
145
146 /// Get immutable access to the shared data.
147 ///
148 /// Similar to [`read`](Self::read), but doesn't block so this function simply
149 /// returns an [`Err`] if the data is already locked (or poisoned).
150 pub fn try_read(
151 &self,
152 ) -> Result<SharedStateReadGuard<'_, Data>, TryLockError<RwLockReadGuard<'_, Data>>> {
153 Ok(SharedStateReadGuard {
154 inner: self.data.try_read()?,
155 })
156 }
157
158 /// Get mutable access to the shared data.
159 ///
160 /// Returns a RAII guard which will **notify all subscribers** and
161 /// release this thread’s shared access once it is dropped.
162 ///
163 /// This function will not return while other writers or other readers
164 /// currently have access to the internal lock (see [`RwLock`]).
165 ///
166 /// # Panics
167 ///
168 /// This function will panic if the internal [`RwLock`] is poisoned.
169 /// A [`RwLock`] is poisoned whenever a writer panics while holding an exclusive lock.
170 /// The failure will occur immediately after the lock has been acquired.
171 ///
172 /// Also, this function might panic when called if the lock is already
173 /// held by the current thread.
174 ///
175 /// # Example
176 ///
177 /// ```
178 /// # use relm4::SharedState;
179 /// static STATE: SharedState<u8> = SharedState::new();
180 ///
181 /// // Overwrite the current value with 1
182 /// *STATE.write() = 1;
183 /// ```
184 ///
185 /// # Panic example
186 ///
187 /// ```no_run
188 /// # use relm4::SharedState;
189 /// static STATE: SharedState<u8> = SharedState::new();
190 ///
191 /// let read_guard = STATE.read();
192 ///
193 /// // This is fine
194 /// let another_read_guard = STATE.read();
195 ///
196 /// // This might panic or result in a dead lock
197 /// // because you cannot read and write at the same time.
198 /// // To solve this, drop all read guards on this thread first.
199 /// let another_write_guard = STATE.write();
200 /// ```
201 pub fn write(&self) -> SharedStateWriteGuard<'_, Data> {
202 let subscribers = self.subscribers.write().unwrap();
203 let data = self.data.write().unwrap();
204
205 SharedStateWriteGuard { data, subscribers }
206 }
207
208 /// Get mutable access to the shared data.
209 ///
210 /// Similar to [`write`](Self::write), but doesn't block so this function simply
211 /// returns an [`Err`] if the data is already locked (or poisoned).
212 pub fn try_write(
213 &self,
214 ) -> Result<SharedStateWriteGuard<'_, Data>, TryLockError<RwLockWriteGuard<'_, Data>>> {
215 let data = self.data.try_write()?;
216 let subscribers = self.subscribers.write().unwrap();
217
218 Ok(SharedStateWriteGuard { data, subscribers })
219 }
220
221 /// Get mutable access to the shared data.
222 /// Since this call borrows the [`SharedState`] mutably,
223 /// no actual locking needs to take place, but the mutable
224 /// borrow statically guarantees no locks exist.
225 ///
226 /// **This method will not notify any subscribers!**
227 ///
228 /// # Panics
229 ///
230 /// This function will panic if the internal [`RwLock`] is poisoned.
231 /// A [`RwLock`] is poisoned whenever a writer panics while holding an exclusive lock.
232 /// The failure will occur immediately after the lock has been acquired.
233 pub fn get_mut(&mut self) -> &mut Data {
234 self.data.get_mut().unwrap()
235 }
236
237 /// Get immutable access to the shared data.
238 ///
239 /// **This method will not notify any subscribers!**
240 ///
241 /// # Panics
242 ///
243 /// This function will panic if the internal [`RwLock`] is poisoned.
244 /// A [`RwLock`] is poisoned whenever a writer panics while holding an exclusive lock.
245 /// The failure will occur immediately after the lock has been acquired.
246 ///
247 /// Also, this function might panic when called if the lock is already
248 /// held by the current thread.
249 pub fn read_inner(&self) -> RwLockReadGuard<'_, Data> {
250 self.data.read().unwrap()
251 }
252
253 /// Get mutable access to the shared data.
254 ///
255 /// **This method will not notify any subscribers!**
256 ///
257 /// # Panics
258 ///
259 /// This function will panic if the internal [`RwLock`] is poisoned.
260 /// A [`RwLock`] is poisoned whenever a writer panics while holding an exclusive lock.
261 /// The failure will occur immediately after the lock has been acquired.
262 ///
263 /// Also, this function might panic when called if the lock is already
264 /// held by the current thread.
265 pub fn write_inner(&self) -> RwLockWriteGuard<'_, Data> {
266 self.data.write().unwrap()
267 }
268}
269
270#[derive(Debug)]
271/// A guard that immutably dereferences `Data`.
272pub struct SharedStateReadGuard<'a, Data> {
273 inner: RwLockReadGuard<'a, Data>,
274}
275
276impl<Data> Deref for SharedStateReadGuard<'_, Data> {
277 type Target = Data;
278
279 fn deref(&self) -> &Self::Target {
280 &self.inner
281 }
282}
283
284/// A guard that mutably dereferences `Data`.
285/// Once dropped all subscribers of the [`SharedState`] will be notified.
286pub struct SharedStateWriteGuard<'a, Data> {
287 data: RwLockWriteGuard<'a, Data>,
288 subscribers: RwLockWriteGuard<'a, Vec<SubscriberFn<Data>>>,
289}
290
291impl<Data: std::fmt::Debug> std::fmt::Debug for SharedStateWriteGuard<'_, Data> {
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293 f.debug_struct("SharedStateWriteGuard")
294 .field("data", &self.data)
295 .field("subscribers", &self.subscribers.len())
296 .finish()
297 }
298}
299
300impl<Data> Deref for SharedStateWriteGuard<'_, Data> {
301 type Target = Data;
302
303 fn deref(&self) -> &Self::Target {
304 &self.data
305 }
306}
307
308impl<Data> DerefMut for SharedStateWriteGuard<'_, Data> {
309 fn deref_mut(&mut self) -> &mut Self::Target {
310 &mut self.data
311 }
312}
313
314impl<Data> Drop for SharedStateWriteGuard<'_, Data> {
315 // Notify subscribers
316 fn drop(&mut self) {
317 let data = &*self.data;
318 // Remove all elements which had their senders dropped.
319 self.subscribers.retain(|subscriber| subscriber(data));
320 }
321}
322
323#[cfg(test)]
324mod test {
325 use super::SharedState;
326
327 static STATE: SharedState<u8> = SharedState::new();
328
329 #[test]
330 fn shared_state() {
331 assert_eq!(*STATE.read(), 0);
332
333 {
334 let mut data = STATE.write();
335 *data += 1;
336 }
337
338 assert_eq!(*STATE.read(), 1);
339
340 let (sender, receiver) = crate::channel();
341
342 STATE.subscribe(&sender, |data| *data);
343
344 {
345 let mut data = STATE.write();
346 *data += 1;
347 }
348
349 assert_eq!(receiver.recv_sync().unwrap(), 2);
350 assert_eq!(*STATE.read(), 2);
351 }
352}