diff --git a/core/src/spinwait.rs b/core/src/spinwait.rs index ad0327a3..c7134997 100644 --- a/core/src/spinwait.rs +++ b/core/src/spinwait.rs @@ -6,11 +6,13 @@ // copied, modified, or distributed except according to those terms. use crate::thread_parker; +#[allow(deprecated)] use std::sync::atomic::spin_loop_hint; // Wastes some CPU time for the given number of iterations, // using a hint to indicate to the CPU that we are spinning. #[inline] +#[allow(deprecated)] fn cpu_relax(iterations: u32) { for _ in 0..iterations { spin_loop_hint() diff --git a/lock_api/src/lib.rs b/lock_api/src/lib.rs index c99c68bd..9e4d60a1 100644 --- a/lock_api/src/lib.rs +++ b/lock_api/src/lib.rs @@ -114,3 +114,84 @@ pub use crate::remutex::*; mod rwlock; pub use crate::rwlock::*; + +/// A "shim trait" to allow generalizing over functions which return some generic +/// type which may borrow elements of its arguments, but without specifying that +/// the return type is `&`, `&mut` or something else concrete. This allows using +/// HRTB to force a caller to supply a function which works for any lifetime, +/// and therefore avoids the caller relying on a specific lifetime for the +/// argument, which can cause UB if the inner data lives for the static lifetime. +/// +/// It also allows the output type to depend on the input type, which is important +/// when using lifetimes in HRTBs but is not possible with the stable syntax for +/// the `Fn` traits. +pub trait FnOnceShim<'a, T: 'a> { + /// Equivalent to `std::ops::FnOnce::Output`. + type Output: 'a; + + /// Equivalent to `std::ops::FnOnce::call` + fn call(self, input: T) -> Self::Output; +} + +impl<'a, F, In, Out> FnOnceShim<'a, In> for F +where + F: FnOnce(In) -> Out, + In: 'a, + Out: 'a, +{ + type Output = Out; + + fn call(self, input: In) -> Self::Output { + self(input) + } +} + +/// As `FnOnceShim`, but specialized for functions which return an `Option` (used +/// for `try_map`). +pub trait FnOnceOptionShim<'a, T: 'a> { + /// Equivalent to `std::ops::FnOnce::Output`. + type Output: 'a; + + /// Equivalent to `std::ops::FnOnce::call` + fn call(self, input: T) -> Option; +} + +impl<'a, F, In, Out> FnOnceOptionShim<'a, In> for F +where + F: FnOnce(In) -> Option, + In: 'a, + Out: 'a, +{ + type Output = Out; + + fn call(self, input: In) -> Option { + self(input) + } +} + +/// As `FnOnceShim`, but specialized for functions which return an `Result` (used +/// for `try_map`). +pub trait FnOnceResultShim<'a, T: 'a> { + /// Equivalent to `std::ops::FnOnce::Output`. + type Output: 'a; + /// Equivalent to `std::ops::FnOnce::Output`. + type Error: 'a; + + /// Equivalent to `std::ops::FnOnce::call` + fn call(self, input: T) -> Result; +} + +impl<'a, F, In, Out, Error> FnOnceResultShim<'a, In> for F +where + F: FnOnce(In) -> Result, + In: 'a, + Out: 'a, + Error: 'a, +{ + type Output = Out; + type Error = Error; + + fn call(self, input: In) -> Result { + self(input) + } +} diff --git a/lock_api/src/mutex.rs b/lock_api/src/mutex.rs index f64fc13f..e1fdccd6 100644 --- a/lock_api/src/mutex.rs +++ b/lock_api/src/mutex.rs @@ -5,18 +5,21 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use core::cell::UnsafeCell; -use core::fmt; -use core::marker::PhantomData; -use core::mem; -use core::ops::{Deref, DerefMut}; +use core::{ + cell::UnsafeCell, + fmt, + marker::PhantomData, + mem, + ops::{Deref, DerefMut}, + ptr, +}; + +use crate::{FnOnceOptionShim, FnOnceResultShim, FnOnceShim}; #[cfg(feature = "arc_lock")] use alloc::sync::Arc; #[cfg(feature = "arc_lock")] use core::mem::ManuallyDrop; -#[cfg(feature = "arc_lock")] -use core::ptr; #[cfg(feature = "owning_ref")] use owning_ref::StableAddress; @@ -508,13 +511,18 @@ impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> MutexGuard<'a, R, T> { /// used as `MutexGuard::map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn map(s: Self, f: F) -> MappedMutexGuard<'a, R, U> + pub fn map( + s: Self, + f: F, + ) -> MappedMutexGuard<'a, R, >::Output> where - F: FnOnce(&mut T) -> &mut U, + for<'any> F: FnOnceShim<'any, &'any mut T>, { let raw = &s.mutex.raw; - let data = f(unsafe { &mut *s.mutex.data.get() }); + let data = unsafe { &mut *s.mutex.data.get() }; mem::forget(s); + + let data = f.call(data); MappedMutexGuard { raw, data, @@ -532,16 +540,25 @@ impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> MutexGuard<'a, R, T> { /// used as `MutexGuard::try_map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn try_map(s: Self, f: F) -> Result, Self> + pub fn try_map( + s: Self, + f: F, + ) -> Result>::Output>, Self> where - F: FnOnce(&mut T) -> Option<&mut U>, + for<'any> F: FnOnceOptionShim<'any, &'any mut T>, { let raw = &s.mutex.raw; - let data = match f(unsafe { &mut *s.mutex.data.get() }) { + let data = unsafe { &mut *s.mutex.data.get() }; + + let data = match f.call(data) { Some(data) => data, None => return Err(s), }; + + // We use `mem::forget` instead of `ManuallyDrop` because we want to drop `self` if + // `f` panicks. This is safe, as `self` must outlive the `&'a mut T` reference. mem::forget(s); + Ok(MappedMutexGuard { raw, data, @@ -663,8 +680,8 @@ impl<'a, R: RawMutex + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display for Mutex unsafe impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> StableAddress for MutexGuard<'a, R, T> {} /// An RAII mutex guard returned by the `Arc` locking operations on `Mutex`. -/// -/// This is similar to the `MutexGuard` struct, except instead of using a reference to unlock the `Mutex` it +/// +/// This is similar to the `MutexGuard` struct, except instead of using a reference to unlock the `Mutex` it /// uses an `Arc`. This has several advantages, most notably that it has an `'static` lifetime. #[cfg(feature = "arc_lock")] #[must_use = "if unused the Mutex will immediately unlock"] @@ -713,7 +730,7 @@ impl ArcMutexGuard { // SAFETY: make sure the Arc gets it reference decremented let mut s = ManuallyDrop::new(s); - unsafe { ptr::drop_in_place(&mut s.mutex) }; + unsafe { ptr::drop_in_place(&mut s.mutex) }; } /// Temporarily unlocks the mutex to execute the given function. @@ -780,10 +797,12 @@ impl Drop for ArcMutexGuard { /// could introduce soundness issues if the locked object is modified by another /// thread. #[must_use = "if unused the Mutex will immediately unlock"] -pub struct MappedMutexGuard<'a, R: RawMutex, T: ?Sized> { +pub struct MappedMutexGuard<'a, R: RawMutex, T: ?Sized + 'a> { raw: &'a R, - data: *mut T, - marker: PhantomData<&'a mut T>, + // We use `&'a mut` to make this type invariant over `'a` + marker: PhantomData<&'a mut ()>, + // `data` at the end so we can cast `MappedMutexGuard<'_, _, [T; N]>` to `MappedMutexGuard<'_, _, [T]>`. + data: T, } unsafe impl<'a, R: RawMutex + Sync + 'a, T: ?Sized + Sync + 'a> Sync @@ -795,7 +814,7 @@ unsafe impl<'a, R: RawMutex + 'a, T: ?Sized + Send + 'a> Send for MappedMutexGua { } -impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> MappedMutexGuard<'a, R, T> { +impl<'a, R: RawMutex + 'a, T: 'a> MappedMutexGuard<'a, R, T> { /// Makes a new `MappedMutexGuard` for a component of the locked data. /// /// This operation cannot fail as the `MappedMutexGuard` passed @@ -805,13 +824,27 @@ impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> MappedMutexGuard<'a, R, T> { /// used as `MappedMutexGuard::map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn map(s: Self, f: F) -> MappedMutexGuard<'a, R, U> + pub fn map(s: Self, f: F) -> MappedMutexGuard<'a, R, >::Output> where - F: FnOnce(&mut T) -> &mut U, + for<'any> F: FnOnceShim<'any, T>, { - let raw = s.raw; - let data = f(unsafe { &mut *s.data }); - mem::forget(s); + let (data, raw) = { + let s = mem::ManuallyDrop::new(s); + (unsafe { ptr::read(&s.data) }, s.raw) + }; + + // `panic::catch_unwind` isn't available in `core`, so we use a dummy guard to unlock + // the mutex in case of unwind. + let lock_guard: MappedMutexGuard<'a, R, ()> = MappedMutexGuard { + raw, + data: (), + marker: PhantomData, + }; + + let data = f.call(data); + + mem::forget(lock_guard); + MappedMutexGuard { raw, data, @@ -829,17 +862,39 @@ impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> MappedMutexGuard<'a, R, T> { /// used as `MappedMutexGuard::try_map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn try_map(s: Self, f: F) -> Result, Self> + pub fn try_map( + s: Self, + f: F, + ) -> Result< + MappedMutexGuard<'a, R, >::Output>, + MappedMutexGuard<'a, R, >::Error>, + > where - F: FnOnce(&mut T) -> Option<&mut U>, + for<'any> F: FnOnceResultShim<'any, T>, { - let raw = s.raw; - let data = match f(unsafe { &mut *s.data }) { - Some(data) => data, - None => return Err(s), + let (data, raw) = { + let s = mem::ManuallyDrop::new(s); + (unsafe { ptr::read(&s.data) }, s.raw) }; - mem::forget(s); - Ok(MappedMutexGuard { + + // `panic::catch_unwind` isn't available in `core`, so we use a dummy guard to unlock + // the mutex in case of unwind. + let lock_guard: MappedMutexGuard<'a, R, ()> = MappedMutexGuard { + raw, + data: (), + marker: PhantomData, + }; + + let out = f.call(data); + + mem::forget(lock_guard); + + out.map(|data| MappedMutexGuard { + raw, + data, + marker: PhantomData, + }) + .map_err(|data| MappedMutexGuard { raw, data, marker: PhantomData, @@ -847,7 +902,7 @@ impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> MappedMutexGuard<'a, R, T> { } } -impl<'a, R: RawMutexFair + 'a, T: ?Sized + 'a> MappedMutexGuard<'a, R, T> { +impl<'a, R: RawMutexFair + 'a, T: 'a> MappedMutexGuard<'a, R, T> { /// Unlocks the mutex using a fair unlock protocol. /// /// By default, mutexes are unfair and allow the current thread to re-lock @@ -872,16 +927,17 @@ impl<'a, R: RawMutexFair + 'a, T: ?Sized + 'a> MappedMutexGuard<'a, R, T> { impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> Deref for MappedMutexGuard<'a, R, T> { type Target = T; + #[inline] fn deref(&self) -> &T { - unsafe { &*self.data } + &self.data } } impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> DerefMut for MappedMutexGuard<'a, R, T> { #[inline] fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.data } + &mut self.data } } @@ -895,19 +951,14 @@ impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> Drop for MappedMutexGuard<'a, R, T> { } } -impl<'a, R: RawMutex + 'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug for MappedMutexGuard<'a, R, T> { +impl<'a, R: RawMutex + 'a, T: fmt::Debug + 'a> fmt::Debug for MappedMutexGuard<'a, R, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } -impl<'a, R: RawMutex + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display - for MappedMutexGuard<'a, R, T> -{ +impl<'a, R: RawMutex + 'a, T: fmt::Display + 'a> fmt::Display for MappedMutexGuard<'a, R, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } - -#[cfg(feature = "owning_ref")] -unsafe impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> StableAddress for MappedMutexGuard<'a, R, T> {} diff --git a/lock_api/src/rwlock.rs b/lock_api/src/rwlock.rs index c404934e..630a0eec 100644 --- a/lock_api/src/rwlock.rs +++ b/lock_api/src/rwlock.rs @@ -5,18 +5,21 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use core::cell::UnsafeCell; -use core::fmt; -use core::marker::PhantomData; -use core::mem; -use core::ops::{Deref, DerefMut}; +use core::{ + cell::UnsafeCell, + fmt, + marker::PhantomData, + mem, + ops::{Deref, DerefMut}, + ptr, +}; + +use crate::{FnOnceOptionShim, FnOnceResultShim, FnOnceShim}; #[cfg(feature = "arc_lock")] use alloc::sync::Arc; #[cfg(feature = "arc_lock")] use core::mem::ManuallyDrop; -#[cfg(feature = "arc_lock")] -use core::ptr; #[cfg(feature = "owning_ref")] use owning_ref::StableAddress; @@ -590,7 +593,7 @@ impl RwLock { } /// Locks this `RwLock` with read access, through an `Arc`. - /// + /// /// This method is similar to the `read` method; however, it requires the `RwLock` to be inside of an `Arc` /// and the resulting read guard has no lifetime requirements. #[cfg(feature = "arc_lock")] @@ -602,8 +605,8 @@ impl RwLock { } /// Attempts to lock this `RwLock` with read access, through an `Arc`. - /// - /// This method is similar to the `try_read` method; however, it requires the `RwLock` to be inside of an + /// + /// This method is similar to the `try_read` method; however, it requires the `RwLock` to be inside of an /// `Arc` and the resulting read guard has no lifetime requirements. #[cfg(feature = "arc_lock")] #[inline] @@ -617,7 +620,7 @@ impl RwLock { } /// Locks this `RwLock` with write access, through an `Arc`. - /// + /// /// This method is similar to the `write` method; however, it requires the `RwLock` to be inside of an `Arc` /// and the resulting write guard has no lifetime requirements. #[cfg(feature = "arc_lock")] @@ -629,8 +632,8 @@ impl RwLock { } /// Attempts to lock this `RwLock` with writ access, through an `Arc`. - /// - /// This method is similar to the `try_write` method; however, it requires the `RwLock` to be inside of an + /// + /// This method is similar to the `try_write` method; however, it requires the `RwLock` to be inside of an /// `Arc` and the resulting write guard has no lifetime requirements. #[cfg(feature = "arc_lock")] #[inline] @@ -744,12 +747,15 @@ impl RwLock { } /// Attempts to acquire this `RwLock` with read access until a timeout is reached, through an `Arc`. - /// + /// /// This method is similar to the `try_read_for` method; however, it requires the `RwLock` to be inside of an /// `Arc` and the resulting read guard has no lifetime requirements. #[cfg(feature = "arc_lock")] #[inline] - pub fn try_read_arc_for(self: &Arc, timeout: R::Duration) -> Option> { + pub fn try_read_arc_for( + self: &Arc, + timeout: R::Duration, + ) -> Option> { if self.raw.try_lock_shared_for(timeout) { // SAFETY: locking guarantee is upheld Some(unsafe { self.read_guard_arc() }) @@ -759,12 +765,15 @@ impl RwLock { } /// Attempts to acquire this `RwLock` with read access until a timeout is reached, through an `Arc`. - /// + /// /// This method is similar to the `try_read_until` method; however, it requires the `RwLock` to be inside of /// an `Arc` and the resulting read guard has no lifetime requirements. #[cfg(feature = "arc_lock")] #[inline] - pub fn try_read_arc_until(self: &Arc, timeout: R::Instant) -> Option> { + pub fn try_read_arc_until( + self: &Arc, + timeout: R::Instant, + ) -> Option> { if self.raw.try_lock_shared_until(timeout) { // SAFETY: locking guarantee is upheld Some(unsafe { self.read_guard_arc() }) @@ -774,12 +783,15 @@ impl RwLock { } /// Attempts to acquire this `RwLock` with write access until a timeout is reached, through an `Arc`. - /// + /// /// This method is similar to the `try_write_for` method; however, it requires the `RwLock` to be inside of /// an `Arc` and the resulting write guard has no lifetime requirements. #[cfg(feature = "arc_lock")] #[inline] - pub fn try_write_arc_for(self: &Arc, timeout: R::Duration) -> Option> { + pub fn try_write_arc_for( + self: &Arc, + timeout: R::Duration, + ) -> Option> { if self.raw.try_lock_exclusive_for(timeout) { // SAFETY: locking guarantee is upheld Some(unsafe { self.write_guard_arc() }) @@ -789,12 +801,15 @@ impl RwLock { } /// Attempts to acquire this `RwLock` with read access until a timeout is reached, through an `Arc`. - /// + /// /// This method is similar to the `try_write_until` method; however, it requires the `RwLock` to be inside of /// an `Arc` and the resulting read guard has no lifetime requirements. #[cfg(feature = "arc_lock")] #[inline] - pub fn try_write_arc_until(self: &Arc, timeout: R::Instant) -> Option> { + pub fn try_write_arc_until( + self: &Arc, + timeout: R::Instant, + ) -> Option> { if self.raw.try_lock_exclusive_until(timeout) { // SAFETY: locking guarantee is upheld Some(unsafe { self.write_guard_arc() }) @@ -848,7 +863,7 @@ impl RwLock { } /// Locks this `RwLock` with shared read access, through an `Arc`. - /// + /// /// This method is similar to the `read_recursive` method; however, it requires the `RwLock` to be inside of /// an `Arc` and the resulting read guard has no lifetime requirements. #[cfg(feature = "arc_lock")] @@ -860,7 +875,7 @@ impl RwLock { } /// Attempts to lock this `RwLock` with shared read access, through an `Arc`. - /// + /// /// This method is similar to the `try_read_recursive` method; however, it requires the `RwLock` to be inside /// of an `Arc` and the resulting read guard has no lifetime requirements. #[cfg(feature = "arc_lock")] @@ -924,7 +939,10 @@ impl RwLock { /// inside of an `Arc` and the resulting read guard has no lifetime requirements. #[cfg(feature = "arc_lock")] #[inline] - pub fn try_read_arc_recursive_for(self: &Arc, timeout: R::Duration) -> Option> { + pub fn try_read_arc_recursive_for( + self: &Arc, + timeout: R::Duration, + ) -> Option> { if self.raw.try_lock_shared_recursive_for(timeout) { // SAFETY: locking guarantee is upheld Some(unsafe { self.read_guard_arc() }) @@ -939,7 +957,10 @@ impl RwLock { /// inside of an `Arc` and the resulting read guard has no lifetime requirements. #[cfg(feature = "arc_lock")] #[inline] - pub fn try_read_arc_recursive_until(self: &Arc, timeout: R::Instant) -> Option> { + pub fn try_read_arc_recursive_until( + self: &Arc, + timeout: R::Instant, + ) -> Option> { if self.raw.try_lock_shared_recursive_until(timeout) { // SAFETY: locking guarantee is upheld Some(unsafe { self.read_guard_arc() }) @@ -995,19 +1016,19 @@ impl RwLock { } /// # Safety - /// + /// /// The lock must be held when calling this method. #[cfg(feature = "arc_lock")] #[inline] unsafe fn upgradable_guard_arc(self: &Arc) -> ArcRwLockUpgradableReadGuard { ArcRwLockUpgradableReadGuard { rwlock: self.clone(), - marker: PhantomData + marker: PhantomData, } } /// Locks this `RwLock` with upgradable read access, through an `Arc`. - /// + /// /// This method is similar to the `upgradable_read` method; however, it requires the `RwLock` to be /// inside of an `Arc` and the resulting read guard has no lifetime requirements. #[cfg(feature = "arc_lock")] @@ -1019,7 +1040,7 @@ impl RwLock { } /// Attempts to lock this `RwLock` with upgradable read access, through an `Arc`. - /// + /// /// This method is similar to the `try_upgradable_read` method; however, it requires the `RwLock` to be /// inside of an `Arc` and the resulting read guard has no lifetime requirements. #[cfg(feature = "arc_lock")] @@ -1074,7 +1095,7 @@ impl RwLock { } /// Attempts to lock this `RwLock` with upgradable access until a timeout is reached, through an `Arc`. - /// + /// /// This method is similar to the `try_upgradable_read_for` method; however, it requires the `RwLock` to be /// inside of an `Arc` and the resulting read guard has no lifetime requirements. #[cfg(feature = "arc_lock")] @@ -1092,7 +1113,7 @@ impl RwLock { } /// Attempts to lock this `RwLock` with upgradable access until a timeout is reached, through an `Arc`. - /// + /// /// This method is similar to the `try_upgradable_read_until` method; however, it requires the `RwLock` to be /// inside of an `Arc` and the resulting read guard has no lifetime requirements. #[cfg(feature = "arc_lock")] @@ -1167,12 +1188,15 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> RwLockReadGuard<'a, R, T> { /// used as `RwLockReadGuard::map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn map(s: Self, f: F) -> MappedRwLockReadGuard<'a, R, U> + pub fn map( + s: Self, + f: F, + ) -> MappedRwLockReadGuard<'a, R, >::Output> where - F: FnOnce(&T) -> &U, + for<'any> F: FnOnceShim<'any, &'any T>, { let raw = &s.rwlock.raw; - let data = f(unsafe { &*s.rwlock.data.get() }); + let data = f.call(unsafe { &*s.rwlock.data.get() }); mem::forget(s); MappedRwLockReadGuard { raw, @@ -1191,16 +1215,20 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> RwLockReadGuard<'a, R, T> { /// used as `RwLockReadGuard::map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn try_map(s: Self, f: F) -> Result, Self> + pub fn try_map( + s: Self, + f: F, + ) -> Result>::Output>, Self> where - F: FnOnce(&T) -> Option<&U>, + for<'any> F: FnOnceOptionShim<'any, &'any T>, { let raw = &s.rwlock.raw; - let data = match f(unsafe { &*s.rwlock.data.get() }) { + let data = match f.call(unsafe { &*s.rwlock.data.get() }) { Some(data) => data, None => return Err(s), }; mem::forget(s); + Ok(MappedRwLockReadGuard { raw, data, @@ -1318,9 +1346,9 @@ impl<'a, R: RawRwLock + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> StableAddress for RwLockReadGuard<'a, R, T> {} -/// An RAII rwlock guard returned by the `Arc` locking operations on `RwLock`. -/// -/// This is similar to the `RwLockReadGuard` struct, except instead of using a reference to unlock the `RwLock` +/// An RAII rwlock guard returned by the `Arc` locking operations on `RwLock`. +/// +/// This is similar to the `RwLockReadGuard` struct, except instead of using a reference to unlock the `RwLock` /// it uses an `Arc`. This has several advantages, most notably that it has an `'static` lifetime. #[cfg(feature = "arc_lock")] #[must_use = "if unused the RwLock will immediately unlock"] @@ -1426,9 +1454,7 @@ impl fmt::Debug for ArcRwLockReadGuard fmt::Display - for ArcRwLockReadGuard -{ +impl fmt::Display for ArcRwLockReadGuard { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } @@ -1457,12 +1483,15 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> RwLockWriteGuard<'a, R, T> { /// used as `RwLockWriteGuard::map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn map(s: Self, f: F) -> MappedRwLockWriteGuard<'a, R, U> + pub fn map( + s: Self, + f: F, + ) -> MappedRwLockWriteGuard<'a, R, >::Output> where - F: FnOnce(&mut T) -> &mut U, + for<'any> F: FnOnceShim<'any, &'any mut T>, { let raw = &s.rwlock.raw; - let data = f(unsafe { &mut *s.rwlock.data.get() }); + let data = f.call(unsafe { &mut *s.rwlock.data.get() }); mem::forget(s); MappedRwLockWriteGuard { raw, @@ -1481,12 +1510,15 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> RwLockWriteGuard<'a, R, T> { /// used as `RwLockWriteGuard::map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn try_map(s: Self, f: F) -> Result, Self> + pub fn try_map( + s: Self, + f: F, + ) -> Result>::Output>, Self> where - F: FnOnce(&mut T) -> Option<&mut U>, + for<'any> F: FnOnceOptionShim<'any, &'any mut T>, { let raw = &s.rwlock.raw; - let data = match f(unsafe { &mut *s.rwlock.data.get() }) { + let data = match f.call(unsafe { &mut *s.rwlock.data.get() }) { Some(data) => data, None => return Err(s), }; @@ -1655,8 +1687,8 @@ impl<'a, R: RawRwLock + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> StableAddress for RwLockWriteGuard<'a, R, T> {} -/// An RAII rwlock guard returned by the `Arc` locking operations on `RwLock`. -/// This is similar to the `RwLockWriteGuard` struct, except instead of using a reference to unlock the `RwLock` +/// An RAII rwlock guard returned by the `Arc` locking operations on `RwLock`. +/// This is similar to the `RwLockWriteGuard` struct, except instead of using a reference to unlock the `RwLock` /// it uses an `Arc`. This has several advantages, most notably that it has an `'static` lifetime. #[cfg(feature = "arc_lock")] #[must_use = "if unused the RwLock will immediately unlock"] @@ -1770,7 +1802,7 @@ impl ArcRwLockWriteGuard { /// Temporarily yields the `RwLock` to a waiting thread if there is one. /// - /// This method is functionally equivalent to the `bump` method on [`RwLockWriteGuard`]. + /// This method is functionally equivalent to the `bump` method on [`RwLockWriteGuard`]. #[inline] pub fn bump(s: &mut Self) { // Safety: An RwLockWriteGuard always holds an exclusive lock. @@ -1816,9 +1848,7 @@ impl fmt::Debug for ArcRwLockWriteGuard fmt::Display - for ArcRwLockWriteGuard -{ +impl fmt::Display for ArcRwLockWriteGuard { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } @@ -2059,7 +2089,7 @@ unsafe impl<'a, R: RawRwLockUpgrade + 'a, T: ?Sized + 'a> StableAddress /// An RAII rwlock guard returned by the `Arc` locking operations on `RwLock`. /// This is similar to the `RwLockUpgradableReadGuard` struct, except instead of using a reference to unlock the -/// `RwLock` it uses an `Arc`. This has several advantages, most notably that it has an `'static` +/// `RwLock` it uses an `Arc`. This has several advantages, most notably that it has an `'static` /// lifetime. #[cfg(feature = "arc_lock")] #[must_use = "if unused the RwLock will immediately unlock"] @@ -2069,7 +2099,7 @@ pub struct ArcRwLockUpgradableReadGuard { } #[cfg(feature = "arc_lock")] -impl ArcRwLockUpgradableReadGuard { +impl ArcRwLockUpgradableReadGuard { /// Returns a reference to the rwlock, contained in its original `Arc`. pub fn rwlock(s: &Self) -> &Arc> { &s.rwlock @@ -2144,7 +2174,7 @@ impl ArcRwLockUpgradableReadGuard { // SAFETY: make sure we decrement the refcount properly let mut s = ManuallyDrop::new(s); - unsafe { ptr::drop_in_place(&mut s.rwlock) }; + unsafe { ptr::drop_in_place(&mut s.rwlock) }; } /// Temporarily unlocks the `RwLock` to execute the given function. @@ -2291,7 +2321,6 @@ impl fmt::Display } } - /// An RAII read lock guard returned by `RwLockReadGuard::map`, which can point to a /// subfield of the protected data. /// @@ -2302,8 +2331,8 @@ impl fmt::Display #[must_use = "if unused the RwLock will immediately unlock"] pub struct MappedRwLockReadGuard<'a, R: RawRwLock, T: ?Sized> { raw: &'a R, - data: *const T, marker: PhantomData<&'a T>, + data: T, } unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + Sync + 'a> Sync for MappedRwLockReadGuard<'a, R, T> {} @@ -2312,7 +2341,7 @@ unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + Sync + 'a> Send for MappedRwLockR { } -impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> MappedRwLockReadGuard<'a, R, T> { +impl<'a, R: RawRwLock + 'a, T: 'a> MappedRwLockReadGuard<'a, R, T> { /// Make a new `MappedRwLockReadGuard` for a component of the locked data. /// /// This operation cannot fail as the `MappedRwLockReadGuard` passed @@ -2322,13 +2351,27 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> MappedRwLockReadGuard<'a, R, T> { /// used as `MappedRwLockReadGuard::map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn map(s: Self, f: F) -> MappedRwLockReadGuard<'a, R, U> + pub fn map(s: Self, f: F) -> MappedRwLockReadGuard<'a, R, >::Output> where - F: FnOnce(&T) -> &U, + for<'any> F: FnOnceShim<'any, T>, { - let raw = s.raw; - let data = f(unsafe { &*s.data }); - mem::forget(s); + let (data, raw) = { + let s = mem::ManuallyDrop::new(s); + (unsafe { ptr::read(&s.data) }, s.raw) + }; + + // `panic::catch_unwind` isn't available in `core`, so we use a dummy guard to unlock + // the mutex in case of unwind. + let lock_guard: MappedRwLockReadGuard<'a, R, ()> = MappedRwLockReadGuard { + raw, + data: (), + marker: PhantomData, + }; + + let data = f.call(data); + + mem::forget(lock_guard); + MappedRwLockReadGuard { raw, data, @@ -2346,17 +2389,39 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> MappedRwLockReadGuard<'a, R, T> { /// used as `MappedRwLockReadGuard::map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn try_map(s: Self, f: F) -> Result, Self> + pub fn try_map( + s: Self, + f: F, + ) -> Result< + MappedRwLockReadGuard<'a, R, >::Output>, + MappedRwLockReadGuard<'a, R, >::Error>, + > where - F: FnOnce(&T) -> Option<&U>, + for<'any> F: FnOnceResultShim<'any, T>, { - let raw = s.raw; - let data = match f(unsafe { &*s.data }) { - Some(data) => data, - None => return Err(s), + let (data, raw) = { + let s = mem::ManuallyDrop::new(s); + (unsafe { ptr::read(&s.data) }, s.raw) }; - mem::forget(s); - Ok(MappedRwLockReadGuard { + + // `panic::catch_unwind` isn't available in `core`, so we use a dummy guard to unlock + // the mutex in case of unwind. + let lock_guard: MappedRwLockReadGuard<'a, R, ()> = MappedRwLockReadGuard { + raw, + data: (), + marker: PhantomData, + }; + + let out = f.call(data); + + mem::forget(lock_guard); + + out.map(|data| MappedRwLockReadGuard { + raw, + data, + marker: PhantomData, + }) + .map_err(|data| MappedRwLockReadGuard { raw, data, marker: PhantomData, @@ -2364,7 +2429,7 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> MappedRwLockReadGuard<'a, R, T> { } } -impl<'a, R: RawRwLockFair + 'a, T: ?Sized + 'a> MappedRwLockReadGuard<'a, R, T> { +impl<'a, R: RawRwLockFair + 'a, T: 'a> MappedRwLockReadGuard<'a, R, T> { /// Unlocks the `RwLock` using a fair unlock protocol. /// /// By default, `RwLock` is unfair and allow the current thread to re-lock @@ -2391,7 +2456,14 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> Deref for MappedRwLockReadGuard<'a, type Target = T; #[inline] fn deref(&self) -> &T { - unsafe { &*self.data } + &self.data + } +} + +impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> DerefMut for MappedRwLockReadGuard<'a, R, T> { + #[inline] + fn deref_mut(&mut self) -> &mut T { + &mut self.data } } @@ -2437,8 +2509,8 @@ unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> StableAddress #[must_use = "if unused the RwLock will immediately unlock"] pub struct MappedRwLockWriteGuard<'a, R: RawRwLock, T: ?Sized> { raw: &'a R, - data: *mut T, marker: PhantomData<&'a mut T>, + data: T, } unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + Sync + 'a> Sync @@ -2450,7 +2522,7 @@ unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + Send + 'a> Send for MappedRwLockW { } -impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> MappedRwLockWriteGuard<'a, R, T> { +impl<'a, R: RawRwLock + 'a, T: 'a> MappedRwLockWriteGuard<'a, R, T> { /// Make a new `MappedRwLockWriteGuard` for a component of the locked data. /// /// This operation cannot fail as the `MappedRwLockWriteGuard` passed @@ -2460,13 +2532,27 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> MappedRwLockWriteGuard<'a, R, T> { /// used as `MappedRwLockWriteGuard::map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn map(s: Self, f: F) -> MappedRwLockWriteGuard<'a, R, U> + pub fn map(s: Self, f: F) -> MappedRwLockWriteGuard<'a, R, >::Output> where - F: FnOnce(&mut T) -> &mut U, + for<'any> F: FnOnceShim<'any, T>, { - let raw = s.raw; - let data = f(unsafe { &mut *s.data }); - mem::forget(s); + let (data, raw) = { + let s = mem::ManuallyDrop::new(s); + (unsafe { ptr::read(&s.data) }, s.raw) + }; + + // `panic::catch_unwind` isn't available in `core`, so we use a dummy guard to unlock + // the mutex in case of unwind. + let lock_guard: MappedRwLockWriteGuard<'a, R, ()> = MappedRwLockWriteGuard { + raw, + data: (), + marker: PhantomData, + }; + + let data = f.call(data); + + mem::forget(lock_guard); + MappedRwLockWriteGuard { raw, data, @@ -2484,17 +2570,39 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> MappedRwLockWriteGuard<'a, R, T> { /// used as `MappedRwLockWriteGuard::map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn try_map(s: Self, f: F) -> Result, Self> + pub fn try_map( + s: Self, + f: F, + ) -> Result< + MappedRwLockWriteGuard<'a, R, >::Output>, + MappedRwLockWriteGuard<'a, R, >::Error>, + > where - F: FnOnce(&mut T) -> Option<&mut U>, + for<'any> F: FnOnceResultShim<'any, T>, { - let raw = s.raw; - let data = match f(unsafe { &mut *s.data }) { - Some(data) => data, - None => return Err(s), + let (data, raw) = { + let s = mem::ManuallyDrop::new(s); + (unsafe { ptr::read(&s.data) }, s.raw) }; - mem::forget(s); - Ok(MappedRwLockWriteGuard { + + // `panic::catch_unwind` isn't available in `core`, so we use a dummy guard to unlock + // the mutex in case of unwind. + let lock_guard: MappedRwLockWriteGuard<'a, R, ()> = MappedRwLockWriteGuard { + raw, + data: (), + marker: PhantomData, + }; + + let out = f.call(data); + + mem::forget(lock_guard); + + out.map(|data| MappedRwLockWriteGuard { + raw, + data, + marker: PhantomData, + }) + .map_err(|data| MappedRwLockWriteGuard { raw, data, marker: PhantomData, @@ -2502,7 +2610,7 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> MappedRwLockWriteGuard<'a, R, T> { } } -impl<'a, R: RawRwLockFair + 'a, T: ?Sized + 'a> MappedRwLockWriteGuard<'a, R, T> { +impl<'a, R: RawRwLockFair + 'a, T: 'a> MappedRwLockWriteGuard<'a, R, T> { /// Unlocks the `RwLock` using a fair unlock protocol. /// /// By default, `RwLock` is unfair and allow the current thread to re-lock @@ -2529,14 +2637,14 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> Deref for MappedRwLockWriteGuard<'a, type Target = T; #[inline] fn deref(&self) -> &T { - unsafe { &*self.data } + &self.data } } impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> DerefMut for MappedRwLockWriteGuard<'a, R, T> { #[inline] fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.data } + &mut self.data } } diff --git a/src/mutex.rs b/src/mutex.rs index 9f63cb94..81e05b12 100644 --- a/src/mutex.rs +++ b/src/mutex.rs @@ -109,6 +109,87 @@ pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, RawMutex, T>; /// thread. pub type MappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawMutex, T>; +#[cfg(doctest)] +mod doctests { + //! ```rust,no_run + //! use parking_lot::{Mutex, MutexGuard}; + //! + //! let m = Mutex::new((0, 0)); + //! let guard = MutexGuard::map( + //! m.lock(), + //! { |inner| &mut inner.0 } + //! as fn(&mut (usize, usize)) -> &mut usize + //! ); + //! ``` + //! + //! ```rust,no_run + //! use parking_lot::{Mutex, MutexGuard}; + //! + //! struct MutWrapper<'a, T>(&'a mut T); + //! + //! let m = Mutex::new((0, 0)); + //! let guard = MutexGuard::map( + //! m.lock(), + //! { |inner| MutWrapper(&mut inner.0) } + //! as fn(&mut (usize, usize)) -> MutWrapper<'_, usize> + //! ); + //! ``` + //! + //! ```rust,compile_fail + //! use parking_lot::{lock_api::RawMutex, MappedMutexGuard, Mutex, MutexGuard}; + //! + //! const FOO: usize = 0; + //! + //! static OUTER: Mutex<&usize> = Mutex::const_new(RawMutex::INIT, &FOO); + //! static M: Mutex = Mutex::const_new(RawMutex::INIT, 0); + //! + //! let guard = MappedMutexGuard::map( + //! MutexGuard::map(M.lock(), { |inner| inner } as fn(&mut usize) -> &mut usize), + //! |inner: &mut _| { + //! let inner = &*inner; + //! *OUTER.lock() = inner; + //! }, + //! ); + //! drop(guard); + //! + //! let outer: &usize = &*OUTER.lock(); + //! + //! assert_eq!(*outer, 0); + //! + //! *M.lock() = 1; + //! + //! assert_eq!(*outer, 1); + //! ``` + //! + //! ```rust,compile_fail + //! use parking_lot::{lock_api::RawMutex, MappedMutexGuard, Mutex, MutexGuard}; + //! + //! const FOO: usize = 0; + //! + //! static OUTER: Mutex<&usize> = Mutex::const_new(RawMutex::INIT, &FOO); + //! static M: Mutex = Mutex::const_new(RawMutex::INIT, 0); + //! + //! struct MutWrapper<'a, T>(&'a mut T); + //! + //! let guard = MappedMutexGuard::map( + //! MutexGuard::map(M.lock(), { |inner| MutWrapper(inner) } as fn(&mut usize) -> MutWrapper<'_, usize>), + //! |inner: MutWrapper<'_, _>| { + //! let inner = &*inner.0; + //! *OUTER.lock() = inner; + //! }, + //! ); + //! drop(guard); + //! + //! let outer: &usize = &*OUTER.lock(); + //! + //! assert_eq!(*outer, 0); + //! + //! *M.lock() = 1; + //! + //! assert_eq!(*outer, 1); + //! ``` +} + #[cfg(test)] mod tests { use crate::{Condvar, Mutex}; diff --git a/src/rwlock.rs b/src/rwlock.rs index 70e1b1a7..02714535 100644 --- a/src/rwlock.rs +++ b/src/rwlock.rs @@ -135,6 +135,39 @@ mod tests { use std::thread; use std::time::Duration; + #[cfg(doctest)] + mod doctests { + //! ```rust,no_run + //! use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; + //! + //! let m = RwLock::new((0, 0)); + //! + //! let guard = RwLockReadGuard::map(m.read(), |inner| &inner.0); + //! ``` + //! + //! ```rust,no_run + //! use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; + //! + //! let m = RwLock::new((0, 0)); + //! + //! let guard = RwLockWriteGuard::map(m.write(), |inner| &mut inner.0); + //! ``` + //! + //! ```rust,compile_fail + //! use std::sync::Mutex; + //! use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard, RawRwLock}; + //! + //! let mut outer = &0; + //! static M: RwLock = RwLock::const_new(RawRwLock::INIT, 0); + //! + //! let guard = RwLockReadGuard::map(M.read(), |inner: &'static i32| { + //! outer = inner; + //! }); + //! drop(guard); + //! *outer = 1; + //! ``` + } + #[cfg(feature = "serde")] use bincode::{deserialize, serialize};