Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make RawReentrantMutex public #233

Merged
merged 1 commit into from
May 19, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -101,8 +128,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 @@ -111,8 +141,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 @@ -123,13 +158,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