Skip to content

Commit

Permalink
Merge pull request #233 from coolreader18/pub-raw-reentrant
Browse files Browse the repository at this point in the history
  • Loading branch information
Amanieu committed May 19, 2020
2 parents bedb401 + 70e826a commit b338bb5
Showing 1 changed file with 45 additions and 8 deletions.
53 changes: 45 additions & 8 deletions lock_api/src/remutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,36 @@ pub unsafe trait GetThreadId {
fn nonzero_thread_id(&self) -> NonZeroUsize;
}

struct RawReentrantMutex<R, G> {
/// 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();
Expand All @@ -76,21 +98,26 @@ impl<R: RawMutex, G: GetThreadId> RawReentrantMutex<R, G> {
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 {
Expand All @@ -106,8 +133,11 @@ impl<R: RawMutex, G: GetThreadId> RawReentrantMutex<R, G> {
}

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.
#[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 {
Expand All @@ -116,8 +146,13 @@ impl<R: RawMutexFair, G: GetThreadId> RawReentrantMutex<R, G> {
}
}

/// 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);
Expand All @@ -128,13 +163,15 @@ impl<R: RawMutexFair, G: GetThreadId> RawReentrantMutex<R, G> {
}

impl<R: RawMutexTimed, G: GetThreadId> RawReentrantMutex<R, G> {
/// 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))
}
}
Expand Down

0 comments on commit b338bb5

Please sign in to comment.