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}