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