1 use crate::sync::batch_semaphore::Semaphore;
2 use std::marker::PhantomData;
3 use std::{fmt, mem, ops};
4 
5 /// RAII structure used to release the shared read access of a lock when
6 /// dropped.
7 ///
8 /// This structure is created by the [`read`] method on
9 /// [`RwLock`].
10 ///
11 /// [`read`]: method@crate::sync::RwLock::read
12 /// [`RwLock`]: struct@crate::sync::RwLock
13 #[clippy::has_significant_drop]
14 #[must_use = "if unused the RwLock will immediately unlock"]
15 pub struct RwLockReadGuard<'a, T: ?Sized> {
16     // When changing the fields in this struct, make sure to update the
17     // `skip_drop` method.
18     #[cfg(all(tokio_unstable, feature = "tracing"))]
19     pub(super) resource_span: tracing::Span,
20     pub(super) s: &'a Semaphore,
21     pub(super) data: *const T,
22     pub(super) marker: PhantomData<&'a T>,
23 }
24 
25 #[allow(dead_code)] // Unused fields are still used in Drop.
26 struct Inner<'a, T: ?Sized> {
27     #[cfg(all(tokio_unstable, feature = "tracing"))]
28     resource_span: tracing::Span,
29     s: &'a Semaphore,
30     data: *const T,
31 }
32 
33 impl<'a, T: ?Sized> RwLockReadGuard<'a, T> {
skip_drop(self) -> Inner<'a, T>34     fn skip_drop(self) -> Inner<'a, T> {
35         let me = mem::ManuallyDrop::new(self);
36         // SAFETY: This duplicates the values in every field of the guard, then
37         // forgets the originals, so in the end no value is duplicated.
38         Inner {
39             #[cfg(all(tokio_unstable, feature = "tracing"))]
40             resource_span: unsafe { std::ptr::read(&me.resource_span) },
41             s: me.s,
42             data: me.data,
43         }
44     }
45 
46     /// Makes a new `RwLockReadGuard` for a component of the locked data.
47     ///
48     /// This operation cannot fail as the `RwLockReadGuard` passed in already
49     /// locked the data.
50     ///
51     /// This is an associated function that needs to be
52     /// used as `RwLockReadGuard::map(...)`. A method would interfere with
53     /// methods of the same name on the contents of the locked data.
54     ///
55     /// This is an asynchronous version of [`RwLockReadGuard::map`] from the
56     /// [`parking_lot` crate].
57     ///
58     /// [`RwLockReadGuard::map`]: https://docs.rs/lock_api/latest/lock_api/struct.RwLockReadGuard.html#method.map
59     /// [`parking_lot` crate]: https://crates.io/crates/parking_lot
60     ///
61     /// # Examples
62     ///
63     /// ```
64     /// use tokio::sync::{RwLock, RwLockReadGuard};
65     ///
66     /// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
67     /// struct Foo(u32);
68     ///
69     /// # #[tokio::main]
70     /// # async fn main() {
71     /// let lock = RwLock::new(Foo(1));
72     ///
73     /// let guard = lock.read().await;
74     /// let guard = RwLockReadGuard::map(guard, |f| &f.0);
75     ///
76     /// assert_eq!(1, *guard);
77     /// # }
78     /// ```
79     #[inline]
map<F, U: ?Sized>(this: Self, f: F) -> RwLockReadGuard<'a, U> where F: FnOnce(&T) -> &U,80     pub fn map<F, U: ?Sized>(this: Self, f: F) -> RwLockReadGuard<'a, U>
81     where
82         F: FnOnce(&T) -> &U,
83     {
84         let data = f(&*this) as *const U;
85         let this = this.skip_drop();
86 
87         RwLockReadGuard {
88             s: this.s,
89             data,
90             marker: PhantomData,
91             #[cfg(all(tokio_unstable, feature = "tracing"))]
92             resource_span: this.resource_span,
93         }
94     }
95 
96     /// Attempts to make a new [`RwLockReadGuard`] for a component of the
97     /// locked data. The original guard is returned if the closure returns
98     /// `None`.
99     ///
100     /// This operation cannot fail as the `RwLockReadGuard` passed in already
101     /// locked the data.
102     ///
103     /// This is an associated function that needs to be used as
104     /// `RwLockReadGuard::try_map(..)`. A method would interfere with methods of the
105     /// same name on the contents of the locked data.
106     ///
107     /// This is an asynchronous version of [`RwLockReadGuard::try_map`] from the
108     /// [`parking_lot` crate].
109     ///
110     /// [`RwLockReadGuard::try_map`]: https://docs.rs/lock_api/latest/lock_api/struct.RwLockReadGuard.html#method.try_map
111     /// [`parking_lot` crate]: https://crates.io/crates/parking_lot
112     ///
113     /// # Examples
114     ///
115     /// ```
116     /// use tokio::sync::{RwLock, RwLockReadGuard};
117     ///
118     /// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
119     /// struct Foo(u32);
120     ///
121     /// # #[tokio::main]
122     /// # async fn main() {
123     /// let lock = RwLock::new(Foo(1));
124     ///
125     /// let guard = lock.read().await;
126     /// let guard = RwLockReadGuard::try_map(guard, |f| Some(&f.0)).expect("should not fail");
127     ///
128     /// assert_eq!(1, *guard);
129     /// # }
130     /// ```
131     #[inline]
try_map<F, U: ?Sized>(this: Self, f: F) -> Result<RwLockReadGuard<'a, U>, Self> where F: FnOnce(&T) -> Option<&U>,132     pub fn try_map<F, U: ?Sized>(this: Self, f: F) -> Result<RwLockReadGuard<'a, U>, Self>
133     where
134         F: FnOnce(&T) -> Option<&U>,
135     {
136         let data = match f(&*this) {
137             Some(data) => data as *const U,
138             None => return Err(this),
139         };
140         let this = this.skip_drop();
141 
142         Ok(RwLockReadGuard {
143             s: this.s,
144             data,
145             marker: PhantomData,
146             #[cfg(all(tokio_unstable, feature = "tracing"))]
147             resource_span: this.resource_span,
148         })
149     }
150 }
151 
152 impl<T: ?Sized> ops::Deref for RwLockReadGuard<'_, T> {
153     type Target = T;
154 
deref(&self) -> &T155     fn deref(&self) -> &T {
156         unsafe { &*self.data }
157     }
158 }
159 
160 impl<'a, T: ?Sized> fmt::Debug for RwLockReadGuard<'a, T>
161 where
162     T: fmt::Debug,
163 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result164     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165         fmt::Debug::fmt(&**self, f)
166     }
167 }
168 
169 impl<'a, T: ?Sized> fmt::Display for RwLockReadGuard<'a, T>
170 where
171     T: fmt::Display,
172 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result173     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174         fmt::Display::fmt(&**self, f)
175     }
176 }
177 
178 impl<'a, T: ?Sized> Drop for RwLockReadGuard<'a, T> {
drop(&mut self)179     fn drop(&mut self) {
180         self.s.release(1);
181 
182         #[cfg(all(tokio_unstable, feature = "tracing"))]
183         self.resource_span.in_scope(|| {
184             tracing::trace!(
185             target: "runtime::resource::state_update",
186             current_readers = 1,
187             current_readers.op = "sub",
188             )
189         });
190     }
191 }
192