From 70e826a640b9a9e5756cfc90bd655d8ea4cebb49 Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Sun, 17 May 2020 13:15:02 -0500 Subject: [PATCH] Make RawReentrantMutex public --- lock_api/src/remutex.rs | 53 ++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/lock_api/src/remutex.rs b/lock_api/src/remutex.rs index bdfcc404..9c7fdb92 100644 --- a/lock_api/src/remutex.rs +++ b/lock_api/src/remutex.rs @@ -47,14 +47,36 @@ pub unsafe trait GetThreadId { fn nonzero_thread_id(&self) -> NonZeroUsize; } -struct RawReentrantMutex { +/// 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 { owner: AtomicUsize, lock_count: Cell, mutex: R, get_thread_id: G, } +unsafe impl Send for RawReentrantMutex {} +unsafe impl Sync for RawReentrantMutex {} + impl RawReentrantMutex { + /// 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 bool>(&self, try_lock: F) -> bool { let id = self.get_thread_id.nonzero_thread_id().get(); @@ -76,21 +98,26 @@ impl RawReentrantMutex { true } + /// Acquires this mutex, blocking if it's held by another thread. #[inline] - fn lock(&self) { + 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] - fn try_lock(&self) -> bool { + 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. #[inline] - fn unlock(&self) { + pub fn unlock(&self) { let lock_count = self.lock_count.get() - 1; self.lock_count.set(lock_count); if lock_count == 0 { @@ -101,8 +128,11 @@ impl RawReentrantMutex { } impl RawReentrantMutex { + /// 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. #[inline] - fn unlock_fair(&self) { + pub fn unlock_fair(&self) { let lock_count = self.lock_count.get() - 1; self.lock_count.set(lock_count); if lock_count == 0 { @@ -111,8 +141,13 @@ impl RawReentrantMutex { } } + /// 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] - fn bump(&self) { + pub fn bump(&self) { if self.lock_count.get() == 1 { let id = self.owner.load(Ordering::Relaxed); self.owner.store(0, Ordering::Relaxed); @@ -123,13 +158,15 @@ impl RawReentrantMutex { } impl RawReentrantMutex { + /// Attempts to acquire this lock until a timeout is reached. #[inline] - fn try_lock_until(&self, timeout: R::Instant) -> bool { + 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] - fn try_lock_for(&self, timeout: R::Duration) -> bool { + pub fn try_lock_for(&self, timeout: R::Duration) -> bool { self.lock_internal(|| self.mutex.try_lock_for(timeout)) } }