left_right/read/
guard.rs

1use crate::sync::{AtomicUsize, Ordering};
2use std::cell::Cell;
3use std::mem;
4
5#[derive(Debug, Copy, Clone)]
6pub(super) struct ReadHandleState<'rh> {
7    pub(super) epoch: &'rh AtomicUsize,
8    pub(super) enters: &'rh Cell<usize>,
9}
10
11impl<'rh, T> From<&'rh super::ReadHandle<T>> for ReadHandleState<'rh> {
12    fn from(rh: &'rh super::ReadHandle<T>) -> Self {
13        Self {
14            epoch: &rh.epoch,
15            enters: &rh.enters,
16        }
17    }
18}
19
20/// A guard wrapping a live reference into a left-right protected `T`.
21///
22/// As long as this guard lives, the `T` being read cannot change. If a writer attempts to call
23/// [`WriteHandle::publish`](crate::WriteHandle::publish), that call will block until this guard is
24/// dropped.
25///
26/// To scope the guard to a subset of the data in `T`, use [`map`](Self::map) and
27/// [`try_map`](Self::try_map).
28#[derive(Debug)]
29pub struct ReadGuard<'rh, T: ?Sized> {
30    // NOTE: _technically_ this is more like &'self.
31    // the reference is valid until the guard is dropped.
32    pub(super) t: &'rh T,
33    pub(super) handle: ReadHandleState<'rh>,
34}
35
36impl<'rh, T: ?Sized> ReadGuard<'rh, T> {
37    /// Makes a new `ReadGuard` for a component of the borrowed data.
38    ///
39    /// This is an associated function that needs to be used as `ReadGuard::map(...)`, since
40    /// a method would interfere with methods of the same name on the contents of a `Readguard`
41    /// used through `Deref`.
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// use left_right::{ReadGuard, ReadHandle};
47    ///
48    /// fn get_str(handle: &ReadHandle<Vec<(String, i32)>>, i: usize) -> Option<ReadGuard<'_, str>> {
49    ///     handle.enter().map(|guard| {
50    ///         ReadGuard::map(guard, |t| {
51    ///             &*t[i].0
52    ///         })
53    ///     })
54    /// }
55    /// ```
56    pub fn map<F, U: ?Sized>(orig: Self, f: F) -> ReadGuard<'rh, U>
57    where
58        F: for<'a> FnOnce(&'a T) -> &'a U,
59    {
60        let rg = ReadGuard {
61            t: f(orig.t),
62            handle: orig.handle,
63        };
64        mem::forget(orig);
65        rg
66    }
67
68    /// Makes a new `ReadGuard` for a component of the borrowed data that may not exist.
69    ///
70    /// This method differs from [`map`](Self::map) in that it drops the guard if the closure maps
71    /// to `None`. This allows you to "lift" a `ReadGuard<Option<T>>` into an
72    /// `Option<ReadGuard<T>>`.
73    ///
74    /// This is an associated function that needs to be used as `ReadGuard::try_map(...)`, since
75    /// a method would interfere with methods of the same name on the contents of a `Readguard`
76    /// used through `Deref`.
77    ///
78    /// # Examples
79    ///
80    /// ```
81    /// use left_right::{ReadGuard, ReadHandle};
82    ///
83    /// fn try_get_str(handle: &ReadHandle<Vec<(String, i32)>>, i: usize) -> Option<ReadGuard<'_, str>> {
84    ///     handle.enter().and_then(|guard| {
85    ///         ReadGuard::try_map(guard, |t| {
86    ///             t.get(i).map(|v| &*v.0)
87    ///         })
88    ///     })
89    /// }
90    /// ```
91    pub fn try_map<F, U: ?Sized>(orig: Self, f: F) -> Option<ReadGuard<'rh, U>>
92    where
93        F: for<'a> FnOnce(&'a T) -> Option<&'a U>,
94    {
95        let rg = ReadGuard {
96            t: f(orig.t)?,
97            handle: orig.handle,
98        };
99        mem::forget(orig);
100        Some(rg)
101    }
102}
103
104impl<'rh, T: ?Sized> AsRef<T> for ReadGuard<'rh, T> {
105    fn as_ref(&self) -> &T {
106        self.t
107    }
108}
109
110impl<'rh, T: ?Sized> std::ops::Deref for ReadGuard<'rh, T> {
111    type Target = T;
112    fn deref(&self) -> &Self::Target {
113        self.t
114    }
115}
116
117impl<'rh, T: ?Sized> Drop for ReadGuard<'rh, T> {
118    fn drop(&mut self) {
119        let enters = self.handle.enters.get() - 1;
120        self.handle.enters.set(enters);
121        if enters == 0 {
122            // We are the last guard to be dropped -- now release our epoch.
123            self.handle.epoch.fetch_add(1, Ordering::AcqRel);
124        }
125    }
126}