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> { |
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 | |
152 | impl<T: ?Sized> ops::Deref for RwLockReadGuard<'_, T> { |
153 | type Target = T; |
154 | |
155 | 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 | { |
164 | 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 | { |
173 | 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> { |
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 | |