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