• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::sync::batch_semaphore::Semaphore;
2 use std::fmt;
3 use std::marker;
4 use std::mem;
5 use std::ops;
6 
7 /// RAII structure used to release the exclusive write access of a lock when
8 /// dropped.
9 ///
10 /// This structure is created by [mapping] an [`RwLockWriteGuard`]. It is a
11 /// separate type from `RwLockWriteGuard` to disallow downgrading a mapped
12 /// guard, since doing so can cause undefined behavior.
13 ///
14 /// [mapping]: method@crate::sync::RwLockWriteGuard::map
15 /// [`RwLockWriteGuard`]: struct@crate::sync::RwLockWriteGuard
16 pub struct RwLockMappedWriteGuard<'a, T: ?Sized> {
17     #[cfg(all(tokio_unstable, feature = "tracing"))]
18     pub(super) resource_span: tracing::Span,
19     pub(super) permits_acquired: u32,
20     pub(super) s: &'a Semaphore,
21     pub(super) data: *mut T,
22     pub(super) marker: marker::PhantomData<&'a mut T>,
23 }
24 
25 impl<'a, T: ?Sized> RwLockMappedWriteGuard<'a, T> {
26     /// Makes a new `RwLockMappedWriteGuard` for a component of the locked data.
27     ///
28     /// This operation cannot fail as the `RwLockMappedWriteGuard` passed in already
29     /// locked the data.
30     ///
31     /// This is an associated function that needs to be used as
32     /// `RwLockMappedWriteGuard::map(..)`. A method would interfere with methods
33     /// of the same name on the contents of the locked data.
34     ///
35     /// This is an asynchronous version of [`RwLockWriteGuard::map`] from the
36     /// [`parking_lot` crate].
37     ///
38     /// [`RwLockWriteGuard::map`]: https://docs.rs/lock_api/latest/lock_api/struct.RwLockWriteGuard.html#method.map
39     /// [`parking_lot` crate]: https://crates.io/crates/parking_lot
40     ///
41     /// # Examples
42     ///
43     /// ```
44     /// use tokio::sync::{RwLock, RwLockWriteGuard};
45     ///
46     /// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
47     /// struct Foo(u32);
48     ///
49     /// # #[tokio::main]
50     /// # async fn main() {
51     /// let lock = RwLock::new(Foo(1));
52     ///
53     /// {
54     ///     let mut mapped = RwLockWriteGuard::map(lock.write().await, |f| &mut f.0);
55     ///     *mapped = 2;
56     /// }
57     ///
58     /// assert_eq!(Foo(2), *lock.read().await);
59     /// # }
60     /// ```
61     #[inline]
map<F, U: ?Sized>(mut this: Self, f: F) -> RwLockMappedWriteGuard<'a, U> where F: FnOnce(&mut T) -> &mut U,62     pub fn map<F, U: ?Sized>(mut this: Self, f: F) -> RwLockMappedWriteGuard<'a, U>
63     where
64         F: FnOnce(&mut T) -> &mut U,
65     {
66         let data = f(&mut *this) as *mut U;
67         let s = this.s;
68         let permits_acquired = this.permits_acquired;
69         #[cfg(all(tokio_unstable, feature = "tracing"))]
70         let resource_span = this.resource_span.clone();
71         // NB: Forget to avoid drop impl from being called.
72         mem::forget(this);
73 
74         RwLockMappedWriteGuard {
75             permits_acquired,
76             s,
77             data,
78             marker: marker::PhantomData,
79             #[cfg(all(tokio_unstable, feature = "tracing"))]
80             resource_span,
81         }
82     }
83 
84     /// Attempts to make a new [`RwLockMappedWriteGuard`] for a component of
85     /// the locked data. The original guard is returned if the closure returns
86     /// `None`.
87     ///
88     /// This operation cannot fail as the `RwLockMappedWriteGuard` passed in already
89     /// locked the data.
90     ///
91     /// This is an associated function that needs to be
92     /// used as `RwLockMappedWriteGuard::try_map(...)`. A method would interfere
93     /// with methods of the same name on the contents of the locked data.
94     ///
95     /// This is an asynchronous version of [`RwLockWriteGuard::try_map`] from
96     /// the [`parking_lot` crate].
97     ///
98     /// [`RwLockWriteGuard::try_map`]: https://docs.rs/lock_api/latest/lock_api/struct.RwLockWriteGuard.html#method.try_map
99     /// [`parking_lot` crate]: https://crates.io/crates/parking_lot
100     ///
101     /// # Examples
102     ///
103     /// ```
104     /// use tokio::sync::{RwLock, RwLockWriteGuard};
105     ///
106     /// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
107     /// struct Foo(u32);
108     ///
109     /// # #[tokio::main]
110     /// # async fn main() {
111     /// let lock = RwLock::new(Foo(1));
112     ///
113     /// {
114     ///     let guard = lock.write().await;
115     ///     let mut guard = RwLockWriteGuard::try_map(guard, |f| Some(&mut f.0)).expect("should not fail");
116     ///     *guard = 2;
117     /// }
118     ///
119     /// assert_eq!(Foo(2), *lock.read().await);
120     /// # }
121     /// ```
122     #[inline]
try_map<F, U: ?Sized>( mut this: Self, f: F, ) -> Result<RwLockMappedWriteGuard<'a, U>, Self> where F: FnOnce(&mut T) -> Option<&mut U>,123     pub fn try_map<F, U: ?Sized>(
124         mut this: Self,
125         f: F,
126     ) -> Result<RwLockMappedWriteGuard<'a, U>, Self>
127     where
128         F: FnOnce(&mut T) -> Option<&mut U>,
129     {
130         let data = match f(&mut *this) {
131             Some(data) => data as *mut U,
132             None => return Err(this),
133         };
134         let s = this.s;
135         let permits_acquired = this.permits_acquired;
136         #[cfg(all(tokio_unstable, feature = "tracing"))]
137         let resource_span = this.resource_span.clone();
138         // NB: Forget to avoid drop impl from being called.
139         mem::forget(this);
140 
141         Ok(RwLockMappedWriteGuard {
142             permits_acquired,
143             s,
144             data,
145             marker: marker::PhantomData,
146             #[cfg(all(tokio_unstable, feature = "tracing"))]
147             resource_span,
148         })
149     }
150 }
151 
152 impl<T: ?Sized> ops::Deref for RwLockMappedWriteGuard<'_, T> {
153     type Target = T;
154 
deref(&self) -> &T155     fn deref(&self) -> &T {
156         unsafe { &*self.data }
157     }
158 }
159 
160 impl<T: ?Sized> ops::DerefMut for RwLockMappedWriteGuard<'_, T> {
deref_mut(&mut self) -> &mut T161     fn deref_mut(&mut self) -> &mut T {
162         unsafe { &mut *self.data }
163     }
164 }
165 
166 impl<'a, T: ?Sized> fmt::Debug for RwLockMappedWriteGuard<'a, T>
167 where
168     T: fmt::Debug,
169 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result170     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171         fmt::Debug::fmt(&**self, f)
172     }
173 }
174 
175 impl<'a, T: ?Sized> fmt::Display for RwLockMappedWriteGuard<'a, T>
176 where
177     T: fmt::Display,
178 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result179     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180         fmt::Display::fmt(&**self, f)
181     }
182 }
183 
184 impl<'a, T: ?Sized> Drop for RwLockMappedWriteGuard<'a, T> {
drop(&mut self)185     fn drop(&mut self) {
186         self.s.release(self.permits_acquired as usize);
187 
188         #[cfg(all(tokio_unstable, feature = "tracing"))]
189         self.resource_span.in_scope(|| {
190             tracing::trace!(
191             target: "runtime::resource::state_update",
192             write_locked = false,
193             write_locked.op = "override",
194             )
195         });
196     }
197 }
198