tokio/sync/rwlock/
read_guard.rs

1use crate::sync::batch_semaphore::Semaphore;
2use std::marker::PhantomData;
3use 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"]
15pub 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.
26struct 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
33impl<'a, T: ?Sized> RwLockReadGuard<'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]
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]
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
152impl<T: ?Sized> ops::Deref for RwLockReadGuard<'_, T> {
153    type Target = T;
154
155    fn deref(&self) -> &T {
156        unsafe { &*self.data }
157    }
158}
159
160impl<'a, T: ?Sized> fmt::Debug for RwLockReadGuard<'a, T>
161where
162    T: fmt::Debug,
163{
164    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165        fmt::Debug::fmt(&**self, f)
166    }
167}
168
169impl<'a, T: ?Sized> fmt::Display for RwLockReadGuard<'a, T>
170where
171    T: fmt::Display,
172{
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        fmt::Display::fmt(&**self, f)
175    }
176}
177
178impl<'a, T: ?Sized> Drop for RwLockReadGuard<'a, T> {
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}