no_std_async/
swap_lock.rs

1use crate::{
2    mutex::{Guard, Mutex},
3    rwlock::{ReadGuard, RwLock},
4};
5
6/// A reader-writer lock that only syncs when the `sync` method is called.
7///
8/// This is similar to a [`RwLock`], except that writes are not instantly
9/// realized by parallel readers. Instead, writes are only realized when the
10/// `sync` method is called. This is useful for cases where you want to write to a value, but you don't
11/// want to block readers while you do so.
12///
13/// This type only implements [`Sync`] when `T` is [`Send`]. Syncing and the non-const [`new`](Self::new)
14/// method require `T` to implement [`Clone`]. Values are provided through the [`Deref`](std::ops::Deref)
15/// and [`DerefMut`](std::ops::DerefMut) implementations on the [`ReadGuard`] and [`Guard`] types.
16///
17/// # Examples
18/// ```rust
19/// use no_std_async::SwapLock;
20/// # fn main() { pollster::block_on(foo()); }
21///
22/// async fn foo() {
23///     let lock = SwapLock::new(42);
24///     let read = lock.read().await;
25///     assert_eq!(*read, 42);
26///
27///     let mut write = lock.write().await;
28///     *write += 1;
29///     assert_eq!(*write, 43); // All writers will read the new value.
30///     assert_eq!(*read, 42); // The read value is not updated until `sync` is called.
31///
32///     drop(read);
33///     drop(write);
34///
35///     lock.sync().await;
36///     let read = lock.read().await;
37///     assert_eq!(*read, 43); // The value has now been updated.
38/// }
39pub struct SwapLock<T> {
40    data: RwLock<T>,
41    write: Mutex<T>,
42}
43impl<T> SwapLock<T> {
44    /// Creates a new [`SwapLock`] with the given data.
45    pub fn new(val: T) -> Self
46    where
47        T: Clone,
48    {
49        Self {
50            data: RwLock::new(val.clone()),
51            write: Mutex::new(val),
52        }
53    }
54    /// Creates a new [`SwapLock`] with the given data and write value.
55    /// These values should be the same. In [`new`](Self::new), it is simply cloned,
56    /// but this is not possible in a const context.
57    pub const fn const_new(data: T, write: T) -> Self {
58        Self {
59            data: RwLock::new(data),
60            write: Mutex::new(write),
61        }
62    }
63
64    /// Syncs the data with the written value.
65    /// This function waits for ***all locks*** to be released.
66    /// This means that holding a read ***or*** write lock will deadlock
67    /// if you call this function.
68    pub async fn sync(&self)
69    where
70        T: Clone,
71    {
72        *self.data.write().await = self.write.lock().await.clone();
73    }
74
75    /// Acquires a read lock on the data.
76    pub async fn read(&self) -> ReadGuard<'_, T> {
77        self.data.read().await
78    }
79    /// Acquires a write lock on the data.
80    pub async fn write(&self) -> Guard<'_, T> {
81        self.write.lock().await
82    }
83}
84
85unsafe impl<T: Send> Send for SwapLock<T> {}
86unsafe impl<T: Send> Sync for SwapLock<T> {}