left_right_cell/
lib.rs

1//! left-right-cell is a lockfree, eventually consistent cell created using the `left-right` crate.
2//! It allows readers to read from the cell without ever blocking while the writer might block when writing.
3//! This is achived by storing to copies of the data one for the readers and one for the writer.
4#![deny(missing_docs)]
5use std::ops::Deref;
6
7use left_right::Absorb;
8
9struct SetOp<T>(T);
10
11impl<T> Absorb<SetOp<T>> for Inner<T>
12where
13    T: Clone,
14{
15    fn absorb_first(&mut self, operation: &mut SetOp<T>, _: &Self) {
16        self.0 = operation.0.clone();
17    }
18
19    fn absorb_second(&mut self, operation: SetOp<T>, _: &Self) {
20        self.0 = operation.0;
21    }
22
23    fn drop_first(self: Box<Self>) {}
24
25    fn sync_with(&mut self, first: &Self) {
26        self.0 = first.0.clone()
27    }
28}
29
30#[derive(Clone)]
31struct Inner<T>(T);
32
33/// A handle to the read half of the cell. Getting a value from the read handle will never block.
34pub struct ReadHandle<T>(left_right::ReadHandle<Inner<T>>);
35impl<T> ReadHandle<T> {
36    /// Gets the value from the cell. Returns [`None`] if the [`WriteHandle`] as been dropped.
37    pub fn get(&self) -> Option<ReadGuard<T>> {
38        self.0.enter().map(|guard| ReadGuard(guard))
39    }
40
41    /// # Safety
42    /// The user of this function must be sure that the [`WriteHandle`] has not been dropped.
43    pub unsafe fn get_unchecked(&self) -> ReadGuard<T> {
44        self.0
45            .enter()
46            .map(|guard| ReadGuard(guard))
47            .unwrap_unchecked()
48    }
49}
50
51/// A reference guard to the read half of the cell. [`WriteHandle::publish`] will block until this is dropped.
52pub struct ReadGuard<'a, T>(left_right::ReadGuard<'a, Inner<T>>);
53
54impl<T> Deref for ReadGuard<'_, T> {
55    type Target = T;
56
57    fn deref(&self) -> &Self::Target {
58        &self.0.as_ref().0
59    }
60}
61
62impl<T> Clone for ReadHandle<T>
63where
64    T: Clone,
65{
66    fn clone(&self) -> Self {
67        ReadHandle(self.0.clone())
68    }
69}
70
71/// A handle to the write half of the cell.
72/// When this handle is dropped the backing data is also dropped.
73pub struct WriteHandle<T: Clone>(left_right::WriteHandle<Inner<T>, SetOp<T>>);
74
75impl<T> WriteHandle<T>
76where
77    T: Clone,
78{
79    /// Set the value of the cell.
80    pub fn set(&mut self, value: T) {
81        self.0.append(SetOp(value));
82    }
83
84    /// Make the changes the to cell since the last set visible to the readers.
85    pub fn publish(&mut self) {
86        self.0.publish();
87    }
88}
89
90/// Creates a new left-right-cell and returns the read and write handle.
91pub fn new<T: Clone>(value: T) -> (WriteHandle<T>, ReadHandle<T>) {
92    let (w, r) = left_right::new_from_empty::<Inner<T>, SetOp<T>>(Inner(value));
93    (WriteHandle(w), ReadHandle(r))
94}
95
96#[cfg(test)]
97mod tests {
98    #[test]
99    fn it_works() {
100        let (mut w, r) = super::new(false);
101
102        let t = std::thread::spawn(move || loop {
103            let value = r.get().unwrap();
104            if *value {
105                break;
106            }
107        });
108
109        w.set(true);
110        w.publish();
111        t.join().unwrap();
112        assert!(true);
113    }
114}