diff options
Diffstat (limited to 'third_party/rust/lock_api/src/remutex.rs')
-rw-r--r-- | third_party/rust/lock_api/src/remutex.rs | 1036 |
1 files changed, 1036 insertions, 0 deletions
diff --git a/third_party/rust/lock_api/src/remutex.rs b/third_party/rust/lock_api/src/remutex.rs new file mode 100644 index 0000000000..3e2010f2b4 --- /dev/null +++ b/third_party/rust/lock_api/src/remutex.rs @@ -0,0 +1,1036 @@ +// Copyright 2018 Amanieu d'Antras +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use crate::{ + mutex::{RawMutex, RawMutexFair, RawMutexTimed}, + GuardNoSend, +}; +use core::{ + cell::{Cell, UnsafeCell}, + fmt, + marker::PhantomData, + mem, + num::NonZeroUsize, + ops::Deref, + sync::atomic::{AtomicUsize, Ordering}, +}; + +#[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; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +/// Helper trait which returns a non-zero thread ID. +/// +/// The simplest way to implement this trait is to return the address of a +/// thread-local variable. +/// +/// # Safety +/// +/// Implementations of this trait must ensure that no two active threads share +/// the same thread ID. However the ID of a thread that has exited can be +/// re-used since that thread is no longer active. +pub unsafe trait GetThreadId { + /// Initial value. + // A “non-constant” const item is a legacy way to supply an initialized value to downstream + // static items. Can hopefully be replaced with `const fn new() -> Self` at some point. + #[allow(clippy::declare_interior_mutable_const)] + const INIT: Self; + + /// Returns a non-zero thread ID which identifies the current thread of + /// execution. + fn nonzero_thread_id(&self) -> NonZeroUsize; +} + +/// A raw mutex type that wraps another raw mutex to provide reentrancy. +/// +/// Although this has the same methods as the [`RawMutex`] trait, it does +/// not implement it, and should not be used in the same way, since this +/// mutex can successfully acquire a lock multiple times in the same thread. +/// Only use this when you know you want a raw mutex that can be locked +/// reentrantly; you probably want [`ReentrantMutex`] instead. +/// +/// [`RawMutex`]: trait.RawMutex.html +/// [`ReentrantMutex`]: struct.ReentrantMutex.html +pub struct RawReentrantMutex<R, G> { + owner: AtomicUsize, + lock_count: Cell<usize>, + mutex: R, + get_thread_id: G, +} + +unsafe impl<R: RawMutex + Send, G: GetThreadId + Send> Send for RawReentrantMutex<R, G> {} +unsafe impl<R: RawMutex + Sync, G: GetThreadId + Sync> Sync for RawReentrantMutex<R, G> {} + +impl<R: RawMutex, G: GetThreadId> RawReentrantMutex<R, G> { + /// Initial value for an unlocked mutex. + #[allow(clippy::declare_interior_mutable_const)] + pub const INIT: Self = RawReentrantMutex { + owner: AtomicUsize::new(0), + lock_count: Cell::new(0), + mutex: R::INIT, + get_thread_id: G::INIT, + }; + + #[inline] + fn lock_internal<F: FnOnce() -> bool>(&self, try_lock: F) -> bool { + let id = self.get_thread_id.nonzero_thread_id().get(); + if self.owner.load(Ordering::Relaxed) == id { + self.lock_count.set( + self.lock_count + .get() + .checked_add(1) + .expect("ReentrantMutex lock count overflow"), + ); + } else { + if !try_lock() { + return false; + } + self.owner.store(id, Ordering::Relaxed); + debug_assert_eq!(self.lock_count.get(), 0); + self.lock_count.set(1); + } + true + } + + /// Acquires this mutex, blocking if it's held by another thread. + #[inline] + pub fn lock(&self) { + self.lock_internal(|| { + self.mutex.lock(); + true + }); + } + + /// Attempts to acquire this mutex without blocking. Returns `true` + /// if the lock was successfully acquired and `false` otherwise. + #[inline] + pub fn try_lock(&self) -> bool { + self.lock_internal(|| self.mutex.try_lock()) + } + + /// Unlocks this mutex. The inner mutex may not be unlocked if + /// this mutex was acquired previously in the current thread. + /// + /// # Safety + /// + /// This method may only be called if the mutex is held by the current thread. + #[inline] + pub unsafe fn unlock(&self) { + let lock_count = self.lock_count.get() - 1; + self.lock_count.set(lock_count); + if lock_count == 0 { + self.owner.store(0, Ordering::Relaxed); + self.mutex.unlock(); + } + } + + /// Checks whether the mutex is currently locked. + #[inline] + pub fn is_locked(&self) -> bool { + self.mutex.is_locked() + } + + /// Checks whether the mutex is currently held by the current thread. + #[inline] + pub fn is_owned_by_current_thread(&self) -> bool { + let id = self.get_thread_id.nonzero_thread_id().get(); + self.owner.load(Ordering::Relaxed) == id + } +} + +impl<R: RawMutexFair, G: GetThreadId> RawReentrantMutex<R, G> { + /// Unlocks this mutex using a fair unlock protocol. The inner mutex + /// may not be unlocked if this mutex was acquired previously in the + /// current thread. + /// + /// # Safety + /// + /// This method may only be called if the mutex is held by the current thread. + #[inline] + pub unsafe fn unlock_fair(&self) { + let lock_count = self.lock_count.get() - 1; + self.lock_count.set(lock_count); + if lock_count == 0 { + self.owner.store(0, Ordering::Relaxed); + self.mutex.unlock_fair(); + } + } + + /// Temporarily yields the mutex to a waiting thread if there is one. + /// + /// This method is functionally equivalent to calling `unlock_fair` followed + /// by `lock`, however it can be much more efficient in the case where there + /// are no waiting threads. + /// + /// # Safety + /// + /// This method may only be called if the mutex is held by the current thread. + #[inline] + pub unsafe fn bump(&self) { + if self.lock_count.get() == 1 { + let id = self.owner.load(Ordering::Relaxed); + self.owner.store(0, Ordering::Relaxed); + self.mutex.bump(); + self.owner.store(id, Ordering::Relaxed); + } + } +} + +impl<R: RawMutexTimed, G: GetThreadId> RawReentrantMutex<R, G> { + /// Attempts to acquire this lock until a timeout is reached. + #[inline] + pub fn try_lock_until(&self, timeout: R::Instant) -> bool { + self.lock_internal(|| self.mutex.try_lock_until(timeout)) + } + + /// Attempts to acquire this lock until a timeout is reached. + #[inline] + pub fn try_lock_for(&self, timeout: R::Duration) -> bool { + self.lock_internal(|| self.mutex.try_lock_for(timeout)) + } +} + +/// A mutex which can be recursively locked by a single thread. +/// +/// This type is identical to `Mutex` except for the following points: +/// +/// - Locking multiple times from the same thread will work correctly instead of +/// deadlocking. +/// - `ReentrantMutexGuard` does not give mutable references to the locked data. +/// Use a `RefCell` if you need this. +/// +/// See [`Mutex`](struct.Mutex.html) for more details about the underlying mutex +/// primitive. +pub struct ReentrantMutex<R, G, T: ?Sized> { + raw: RawReentrantMutex<R, G>, + data: UnsafeCell<T>, +} + +unsafe impl<R: RawMutex + Send, G: GetThreadId + Send, T: ?Sized + Send> Send + for ReentrantMutex<R, G, T> +{ +} +unsafe impl<R: RawMutex + Sync, G: GetThreadId + Sync, T: ?Sized + Send> Sync + for ReentrantMutex<R, G, T> +{ +} + +impl<R: RawMutex, G: GetThreadId, T> ReentrantMutex<R, G, T> { + /// Creates a new reentrant mutex in an unlocked state ready for use. + #[cfg(has_const_fn_trait_bound)] + #[inline] + pub const fn new(val: T) -> ReentrantMutex<R, G, T> { + ReentrantMutex { + data: UnsafeCell::new(val), + raw: RawReentrantMutex { + owner: AtomicUsize::new(0), + lock_count: Cell::new(0), + mutex: R::INIT, + get_thread_id: G::INIT, + }, + } + } + + /// Creates a new reentrant mutex in an unlocked state ready for use. + #[cfg(not(has_const_fn_trait_bound))] + #[inline] + pub fn new(val: T) -> ReentrantMutex<R, G, T> { + ReentrantMutex { + data: UnsafeCell::new(val), + raw: RawReentrantMutex { + owner: AtomicUsize::new(0), + lock_count: Cell::new(0), + mutex: R::INIT, + get_thread_id: G::INIT, + }, + } + } + + /// Consumes this mutex, returning the underlying data. + #[inline] + pub fn into_inner(self) -> T { + self.data.into_inner() + } +} + +impl<R, G, T> ReentrantMutex<R, G, T> { + /// Creates a new reentrant mutex based on a pre-existing raw mutex and a + /// helper to get the thread ID. + /// + /// This allows creating a reentrant mutex in a constant context on stable + /// Rust. + #[inline] + pub const fn const_new(raw_mutex: R, get_thread_id: G, val: T) -> ReentrantMutex<R, G, T> { + ReentrantMutex { + data: UnsafeCell::new(val), + raw: RawReentrantMutex { + owner: AtomicUsize::new(0), + lock_count: Cell::new(0), + mutex: raw_mutex, + get_thread_id, + }, + } + } +} + +impl<R: RawMutex, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { + /// # Safety + /// + /// The lock must be held when calling this method. + #[inline] + unsafe fn guard(&self) -> ReentrantMutexGuard<'_, R, G, T> { + ReentrantMutexGuard { + remutex: &self, + marker: PhantomData, + } + } + + /// Acquires a reentrant mutex, blocking the current thread until it is able + /// to do so. + /// + /// If the mutex is held by another thread then this function will block the + /// local thread until it is available to acquire the mutex. If the mutex is + /// already held by the current thread then this function will increment the + /// lock reference count and return immediately. Upon returning, + /// the thread is the only thread with the mutex held. An RAII guard is + /// returned to allow scoped unlock of the lock. When the guard goes out of + /// scope, the mutex will be unlocked. + #[inline] + pub fn lock(&self) -> ReentrantMutexGuard<'_, R, G, T> { + self.raw.lock(); + // SAFETY: The lock is held, as required. + unsafe { self.guard() } + } + + /// Attempts to acquire this lock. + /// + /// If the lock could not be acquired at this time, then `None` is returned. + /// Otherwise, an RAII guard is returned. The lock will be unlocked when the + /// guard is dropped. + /// + /// This function does not block. + #[inline] + pub fn try_lock(&self) -> Option<ReentrantMutexGuard<'_, R, G, T>> { + if self.raw.try_lock() { + // SAFETY: The lock is held, as required. + Some(unsafe { self.guard() }) + } else { + None + } + } + + /// Returns a mutable reference to the underlying data. + /// + /// Since this call borrows the `ReentrantMutex` mutably, no actual locking needs to + /// take place---the mutable borrow statically guarantees no locks exist. + #[inline] + pub fn get_mut(&mut self) -> &mut T { + unsafe { &mut *self.data.get() } + } + + /// Checks whether the mutex is currently locked. + #[inline] + pub fn is_locked(&self) -> bool { + self.raw.is_locked() + } + + /// Checks whether the mutex is currently held by the current thread. + #[inline] + pub fn is_owned_by_current_thread(&self) -> bool { + self.raw.is_owned_by_current_thread() + } + + /// Forcibly unlocks the mutex. + /// + /// This is useful when combined with `mem::forget` to hold a lock without + /// the need to maintain a `ReentrantMutexGuard` object alive, for example when + /// dealing with FFI. + /// + /// # Safety + /// + /// This method must only be called if the current thread logically owns a + /// `ReentrantMutexGuard` but that guard has be discarded using `mem::forget`. + /// Behavior is undefined if a mutex is unlocked when not locked. + #[inline] + pub unsafe fn force_unlock(&self) { + self.raw.unlock(); + } + + /// Returns the underlying raw mutex object. + /// + /// Note that you will most likely need to import the `RawMutex` trait from + /// `lock_api` to be able to call functions on the raw mutex. + /// + /// # Safety + /// + /// This method is unsafe because it allows unlocking a mutex while + /// still holding a reference to a `ReentrantMutexGuard`. + #[inline] + pub unsafe fn raw(&self) -> &R { + &self.raw.mutex + } + + /// Returns a raw pointer to the underlying data. + /// + /// This is useful when combined with `mem::forget` to hold a lock without + /// the need to maintain a `ReentrantMutexGuard` object alive, for example + /// when dealing with FFI. + /// + /// # Safety + /// + /// You must ensure that there are no data races when dereferencing the + /// returned pointer, for example if the current thread logically owns a + /// `ReentrantMutexGuard` but that guard has been discarded using + /// `mem::forget`. + #[inline] + pub fn data_ptr(&self) -> *mut T { + self.data.get() + } + + /// # Safety + /// + /// The lock must be held before calling this method. + #[cfg(feature = "arc_lock")] + #[inline] + unsafe fn guard_arc(self: &Arc<Self>) -> ArcReentrantMutexGuard<R, G, T> { + ArcReentrantMutexGuard { + remutex: self.clone(), + marker: PhantomData, + } + } + + /// Acquires a reentrant mutex through an `Arc`. + /// + /// This method is similar to the `lock` method; however, it requires the `ReentrantMutex` to be inside of an + /// `Arc` and the resulting mutex guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn lock_arc(self: &Arc<Self>) -> ArcReentrantMutexGuard<R, G, T> { + self.raw.lock(); + // SAFETY: locking guarantee is upheld + unsafe { self.guard_arc() } + } + + /// Attempts to acquire a reentrant mutex through an `Arc`. + /// + /// This method is similar to the `try_lock` method; however, it requires the `ReentrantMutex` to be inside + /// of an `Arc` and the resulting mutex guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_lock_arc(self: &Arc<Self>) -> Option<ArcReentrantMutexGuard<R, G, T>> { + if self.raw.try_lock() { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.guard_arc() }) + } else { + None + } + } +} + +impl<R: RawMutexFair, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { + /// Forcibly unlocks the mutex using a fair unlock protocol. + /// + /// This is useful when combined with `mem::forget` to hold a lock without + /// the need to maintain a `ReentrantMutexGuard` object alive, for example when + /// dealing with FFI. + /// + /// # Safety + /// + /// This method must only be called if the current thread logically owns a + /// `ReentrantMutexGuard` but that guard has be discarded using `mem::forget`. + /// Behavior is undefined if a mutex is unlocked when not locked. + #[inline] + pub unsafe fn force_unlock_fair(&self) { + self.raw.unlock_fair(); + } +} + +impl<R: RawMutexTimed, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { + /// Attempts to acquire this lock until a timeout is reached. + /// + /// If the lock could not be acquired before the timeout expired, then + /// `None` is returned. Otherwise, an RAII guard is returned. The lock will + /// be unlocked when the guard is dropped. + #[inline] + pub fn try_lock_for(&self, timeout: R::Duration) -> Option<ReentrantMutexGuard<'_, R, G, T>> { + if self.raw.try_lock_for(timeout) { + // SAFETY: The lock is held, as required. + Some(unsafe { self.guard() }) + } else { + None + } + } + + /// Attempts to acquire this lock until a timeout is reached. + /// + /// If the lock could not be acquired before the timeout expired, then + /// `None` is returned. Otherwise, an RAII guard is returned. The lock will + /// be unlocked when the guard is dropped. + #[inline] + pub fn try_lock_until(&self, timeout: R::Instant) -> Option<ReentrantMutexGuard<'_, R, G, T>> { + if self.raw.try_lock_until(timeout) { + // SAFETY: The lock is held, as required. + Some(unsafe { self.guard() }) + } else { + None + } + } + + /// Attempts to acquire this lock until a timeout is reached, through an `Arc`. + /// + /// This method is similar to the `try_lock_for` method; however, it requires the `ReentrantMutex` to be + /// inside of an `Arc` and the resulting mutex guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_lock_arc_for( + self: &Arc<Self>, + timeout: R::Duration, + ) -> Option<ArcReentrantMutexGuard<R, G, T>> { + if self.raw.try_lock_for(timeout) { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.guard_arc() }) + } else { + None + } + } + + /// Attempts to acquire this lock until a timeout is reached, through an `Arc`. + /// + /// This method is similar to the `try_lock_until` method; however, it requires the `ReentrantMutex` to be + /// inside of an `Arc` and the resulting mutex guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_lock_arc_until( + self: &Arc<Self>, + timeout: R::Instant, + ) -> Option<ArcReentrantMutexGuard<R, G, T>> { + if self.raw.try_lock_until(timeout) { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.guard_arc() }) + } else { + None + } + } +} + +impl<R: RawMutex, G: GetThreadId, T: ?Sized + Default> Default for ReentrantMutex<R, G, T> { + #[inline] + fn default() -> ReentrantMutex<R, G, T> { + ReentrantMutex::new(Default::default()) + } +} + +impl<R: RawMutex, G: GetThreadId, T> From<T> for ReentrantMutex<R, G, T> { + #[inline] + fn from(t: T) -> ReentrantMutex<R, G, T> { + ReentrantMutex::new(t) + } +} + +impl<R: RawMutex, G: GetThreadId, T: ?Sized + fmt::Debug> fmt::Debug for ReentrantMutex<R, G, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.try_lock() { + Some(guard) => f + .debug_struct("ReentrantMutex") + .field("data", &&*guard) + .finish(), + None => { + struct LockedPlaceholder; + impl fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("<locked>") + } + } + + f.debug_struct("ReentrantMutex") + .field("data", &LockedPlaceholder) + .finish() + } + } + } +} + +// Copied and modified from serde +#[cfg(feature = "serde")] +impl<R, G, T> Serialize for ReentrantMutex<R, G, T> +where + R: RawMutex, + G: GetThreadId, + T: Serialize + ?Sized, +{ + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + self.lock().serialize(serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de, R, G, T> Deserialize<'de> for ReentrantMutex<R, G, T> +where + R: RawMutex, + G: GetThreadId, + T: Deserialize<'de> + ?Sized, +{ + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + Deserialize::deserialize(deserializer).map(ReentrantMutex::new) + } +} + +/// An RAII implementation of a "scoped lock" of a reentrant mutex. When this structure +/// is dropped (falls out of scope), the lock will be unlocked. +/// +/// The data protected by the mutex can be accessed through this guard via its +/// `Deref` implementation. +#[must_use = "if unused the ReentrantMutex will immediately unlock"] +pub struct ReentrantMutexGuard<'a, R: RawMutex, G: GetThreadId, T: ?Sized> { + remutex: &'a ReentrantMutex<R, G, T>, + marker: PhantomData<(&'a T, GuardNoSend)>, +} + +unsafe impl<'a, R: RawMutex + Sync + 'a, G: GetThreadId + Sync + 'a, T: ?Sized + Sync + 'a> Sync + for ReentrantMutexGuard<'a, R, G, T> +{ +} + +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> ReentrantMutexGuard<'a, R, G, T> { + /// Returns a reference to the original `ReentrantMutex` object. + pub fn remutex(s: &Self) -> &'a ReentrantMutex<R, G, T> { + s.remutex + } + + /// Makes a new `MappedReentrantMutexGuard` for a component of the locked data. + /// + /// This operation cannot fail as the `ReentrantMutexGuard` passed + /// in already locked the mutex. + /// + /// This is an associated function that needs to be + /// used as `ReentrantMutexGuard::map(...)`. A method would interfere with methods of + /// the same name on the contents of the locked data. + #[inline] + pub fn map<U: ?Sized, F>(s: Self, f: F) -> MappedReentrantMutexGuard<'a, R, G, U> + where + F: FnOnce(&T) -> &U, + { + let raw = &s.remutex.raw; + let data = f(unsafe { &*s.remutex.data.get() }); + mem::forget(s); + MappedReentrantMutexGuard { + raw, + data, + marker: PhantomData, + } + } + + /// Attempts to make a new `MappedReentrantMutexGuard` for a component of the + /// locked data. The original guard is return if the closure returns `None`. + /// + /// This operation cannot fail as the `ReentrantMutexGuard` passed + /// in already locked the mutex. + /// + /// This is an associated function that needs to be + /// used as `ReentrantMutexGuard::try_map(...)`. A method would interfere with methods of + /// the same name on the contents of the locked data. + #[inline] + pub fn try_map<U: ?Sized, F>( + s: Self, + f: F, + ) -> Result<MappedReentrantMutexGuard<'a, R, G, U>, Self> + where + F: FnOnce(&T) -> Option<&U>, + { + let raw = &s.remutex.raw; + let data = match f(unsafe { &*s.remutex.data.get() }) { + Some(data) => data, + None => return Err(s), + }; + mem::forget(s); + Ok(MappedReentrantMutexGuard { + raw, + data, + marker: PhantomData, + }) + } + + /// Temporarily unlocks the mutex to execute the given function. + /// + /// This is safe because `&mut` guarantees that there exist no other + /// references to the data protected by the mutex. + #[inline] + pub fn unlocked<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: A ReentrantMutexGuard always holds the lock. + unsafe { + s.remutex.raw.unlock(); + } + defer!(s.remutex.raw.lock()); + f() + } +} + +impl<'a, R: RawMutexFair + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> + ReentrantMutexGuard<'a, R, G, T> +{ + /// Unlocks the mutex using a fair unlock protocol. + /// + /// By default, mutexes are unfair and allow the current thread to re-lock + /// the mutex before another has the chance to acquire the lock, even if + /// that thread has been blocked on the mutex for a long time. This is the + /// default because it allows much higher throughput as it avoids forcing a + /// context switch on every mutex unlock. This can result in one thread + /// acquiring a mutex many more times than other threads. + /// + /// However in some cases it can be beneficial to ensure fairness by forcing + /// the lock to pass on to a waiting thread if there is one. This is done by + /// using this method instead of dropping the `ReentrantMutexGuard` normally. + #[inline] + pub fn unlock_fair(s: Self) { + // Safety: A ReentrantMutexGuard always holds the lock + unsafe { + s.remutex.raw.unlock_fair(); + } + mem::forget(s); + } + + /// Temporarily unlocks the mutex to execute the given function. + /// + /// The mutex is unlocked a fair unlock protocol. + /// + /// This is safe because `&mut` guarantees that there exist no other + /// references to the data protected by the mutex. + #[inline] + pub fn unlocked_fair<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: A ReentrantMutexGuard always holds the lock + unsafe { + s.remutex.raw.unlock_fair(); + } + defer!(s.remutex.raw.lock()); + f() + } + + /// Temporarily yields the mutex to a waiting thread if there is one. + /// + /// This method is functionally equivalent to calling `unlock_fair` followed + /// by `lock`, however it can be much more efficient in the case where there + /// are no waiting threads. + #[inline] + pub fn bump(s: &mut Self) { + // Safety: A ReentrantMutexGuard always holds the lock + unsafe { + s.remutex.raw.bump(); + } + } +} + +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> Deref + for ReentrantMutexGuard<'a, R, G, T> +{ + type Target = T; + #[inline] + fn deref(&self) -> &T { + unsafe { &*self.remutex.data.get() } + } +} + +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> Drop + for ReentrantMutexGuard<'a, R, G, T> +{ + #[inline] + fn drop(&mut self) { + // Safety: A ReentrantMutexGuard always holds the lock. + unsafe { + self.remutex.raw.unlock(); + } + } +} + +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug + for ReentrantMutexGuard<'a, R, G, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display + for ReentrantMutexGuard<'a, R, G, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[cfg(feature = "owning_ref")] +unsafe impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> StableAddress + for ReentrantMutexGuard<'a, R, G, T> +{ +} + +/// An RAII mutex guard returned by the `Arc` locking operations on `ReentrantMutex`. +/// +/// This is similar to the `ReentrantMutexGuard` struct, except instead of using a reference to unlock the +/// `Mutex` it uses an `Arc<ReentrantMutex>`. This has several advantages, most notably that it has an `'static` +/// lifetime. +#[cfg(feature = "arc_lock")] +#[must_use = "if unused the ReentrantMutex will immediately unlock"] +pub struct ArcReentrantMutexGuard<R: RawMutex, G: GetThreadId, T: ?Sized> { + remutex: Arc<ReentrantMutex<R, G, T>>, + marker: PhantomData<GuardNoSend>, +} + +#[cfg(feature = "arc_lock")] +impl<R: RawMutex, G: GetThreadId, T: ?Sized> ArcReentrantMutexGuard<R, G, T> { + /// Returns a reference to the `ReentrantMutex` this object is guarding, contained in its `Arc`. + pub fn remutex(s: &Self) -> &Arc<ReentrantMutex<R, G, T>> { + &s.remutex + } + + /// Temporarily unlocks the mutex to execute the given function. + /// + /// This is safe because `&mut` guarantees that there exist no other + /// references to the data protected by the mutex. + #[inline] + pub fn unlocked<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: A ReentrantMutexGuard always holds the lock. + unsafe { + s.remutex.raw.unlock(); + } + defer!(s.remutex.raw.lock()); + f() + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawMutexFair, G: GetThreadId, T: ?Sized> ArcReentrantMutexGuard<R, G, T> { + /// Unlocks the mutex using a fair unlock protocol. + /// + /// This is functionally identical to the `unlock_fair` method on [`ReentrantMutexGuard`]. + #[inline] + pub fn unlock_fair(s: Self) { + // Safety: A ReentrantMutexGuard always holds the lock + unsafe { + s.remutex.raw.unlock_fair(); + } + + // SAFETY: ensure that the Arc's refcount is decremented + let mut s = ManuallyDrop::new(s); + unsafe { ptr::drop_in_place(&mut s.remutex) }; + } + + /// Temporarily unlocks the mutex to execute the given function. + /// + /// This is functionally identical to the `unlocked_fair` method on [`ReentrantMutexGuard`]. + #[inline] + pub fn unlocked_fair<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: A ReentrantMutexGuard always holds the lock + unsafe { + s.remutex.raw.unlock_fair(); + } + defer!(s.remutex.raw.lock()); + f() + } + + /// Temporarily yields the mutex to a waiting thread if there is one. + /// + /// This is functionally equivalent to the `bump` method on [`ReentrantMutexGuard`]. + #[inline] + pub fn bump(s: &mut Self) { + // Safety: A ReentrantMutexGuard always holds the lock + unsafe { + s.remutex.raw.bump(); + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawMutex, G: GetThreadId, T: ?Sized> Deref for ArcReentrantMutexGuard<R, G, T> { + type Target = T; + #[inline] + fn deref(&self) -> &T { + unsafe { &*self.remutex.data.get() } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawMutex, G: GetThreadId, T: ?Sized> Drop for ArcReentrantMutexGuard<R, G, T> { + #[inline] + fn drop(&mut self) { + // Safety: A ReentrantMutexGuard always holds the lock. + unsafe { + self.remutex.raw.unlock(); + } + } +} + +/// An RAII mutex guard returned by `ReentrantMutexGuard::map`, which can point to a +/// subfield of the protected data. +/// +/// The main difference between `MappedReentrantMutexGuard` and `ReentrantMutexGuard` is that the +/// former doesn't support temporarily unlocking and re-locking, since that +/// could introduce soundness issues if the locked object is modified by another +/// thread. +#[must_use = "if unused the ReentrantMutex will immediately unlock"] +pub struct MappedReentrantMutexGuard<'a, R: RawMutex, G: GetThreadId, T: ?Sized> { + raw: &'a RawReentrantMutex<R, G>, + data: *const T, + marker: PhantomData<&'a T>, +} + +unsafe impl<'a, R: RawMutex + Sync + 'a, G: GetThreadId + Sync + 'a, T: ?Sized + Sync + 'a> Sync + for MappedReentrantMutexGuard<'a, R, G, T> +{ +} + +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> + MappedReentrantMutexGuard<'a, R, G, T> +{ + /// Makes a new `MappedReentrantMutexGuard` for a component of the locked data. + /// + /// This operation cannot fail as the `MappedReentrantMutexGuard` passed + /// in already locked the mutex. + /// + /// This is an associated function that needs to be + /// used as `MappedReentrantMutexGuard::map(...)`. A method would interfere with methods of + /// the same name on the contents of the locked data. + #[inline] + pub fn map<U: ?Sized, F>(s: Self, f: F) -> MappedReentrantMutexGuard<'a, R, G, U> + where + F: FnOnce(&T) -> &U, + { + let raw = s.raw; + let data = f(unsafe { &*s.data }); + mem::forget(s); + MappedReentrantMutexGuard { + raw, + data, + marker: PhantomData, + } + } + + /// Attempts to make a new `MappedReentrantMutexGuard` for a component of the + /// locked data. The original guard is return if the closure returns `None`. + /// + /// This operation cannot fail as the `MappedReentrantMutexGuard` passed + /// in already locked the mutex. + /// + /// This is an associated function that needs to be + /// used as `MappedReentrantMutexGuard::try_map(...)`. A method would interfere with methods of + /// the same name on the contents of the locked data. + #[inline] + pub fn try_map<U: ?Sized, F>( + s: Self, + f: F, + ) -> Result<MappedReentrantMutexGuard<'a, R, G, U>, Self> + where + F: FnOnce(&T) -> Option<&U>, + { + let raw = s.raw; + let data = match f(unsafe { &*s.data }) { + Some(data) => data, + None => return Err(s), + }; + mem::forget(s); + Ok(MappedReentrantMutexGuard { + raw, + data, + marker: PhantomData, + }) + } +} + +impl<'a, R: RawMutexFair + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> + MappedReentrantMutexGuard<'a, R, G, T> +{ + /// Unlocks the mutex using a fair unlock protocol. + /// + /// By default, mutexes are unfair and allow the current thread to re-lock + /// the mutex before another has the chance to acquire the lock, even if + /// that thread has been blocked on the mutex for a long time. This is the + /// default because it allows much higher throughput as it avoids forcing a + /// context switch on every mutex unlock. This can result in one thread + /// acquiring a mutex many more times than other threads. + /// + /// However in some cases it can be beneficial to ensure fairness by forcing + /// the lock to pass on to a waiting thread if there is one. This is done by + /// using this method instead of dropping the `ReentrantMutexGuard` normally. + #[inline] + pub fn unlock_fair(s: Self) { + // Safety: A MappedReentrantMutexGuard always holds the lock + unsafe { + s.raw.unlock_fair(); + } + mem::forget(s); + } +} + +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> Deref + for MappedReentrantMutexGuard<'a, R, G, T> +{ + type Target = T; + #[inline] + fn deref(&self) -> &T { + unsafe { &*self.data } + } +} + +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> Drop + for MappedReentrantMutexGuard<'a, R, G, T> +{ + #[inline] + fn drop(&mut self) { + // Safety: A MappedReentrantMutexGuard always holds the lock. + unsafe { + self.raw.unlock(); + } + } +} + +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug + for MappedReentrantMutexGuard<'a, R, G, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display + for MappedReentrantMutexGuard<'a, R, G, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[cfg(feature = "owning_ref")] +unsafe impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> StableAddress + for MappedReentrantMutexGuard<'a, R, G, T> +{ +} |