Skip to content

Commit

Permalink
Merge pull request #291 from notgull/arc-lock
Browse files Browse the repository at this point in the history
  • Loading branch information
Amanieu committed Aug 1, 2021
2 parents 0dcf7ce + a9f5671 commit 5cb2c0a
Show file tree
Hide file tree
Showing 7 changed files with 1,181 additions and 3 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/rust.yml
Expand Up @@ -17,7 +17,7 @@ jobs:
matrix:
os: [ubuntu, macos, windows]
channel: [1.36.0, stable, beta, nightly]
feature: [serde, deadlock_detection]
feature: [arc_lock, serde, deadlock_detection]
exclude:
- feature: deadlock_detection
channel: '1.36.0'
Expand Down Expand Up @@ -53,7 +53,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- run: rustup default nightly
- run: cargo doc --workspace --features serde,deadlock_detection --no-deps -p parking_lot -p parking_lot_core -p lock_api
- run: cargo doc --workspace --features arc_lock,serde,deadlock_detection --no-deps -p parking_lot -p parking_lot_core -p lock_api
benchmark:
runs-on: ubuntu-latest
steps:
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -23,6 +23,7 @@ bincode = "1.3.0"

[features]
default = []
arc_lock = ["lock_api/arc_lock"]
owning_ref = ["lock_api/owning_ref"]
nightly = ["parking_lot_core/nightly", "lock_api/nightly"]
deadlock_detection = ["parking_lot_core/deadlock_detection"]
Expand Down
1 change: 1 addition & 0 deletions lock_api/Cargo.toml
Expand Up @@ -20,3 +20,4 @@ serde = { version = "1.0.114", default-features = false, optional = true }

[features]
nightly = []
arc_lock = []
7 changes: 6 additions & 1 deletion lock_api/src/lib.rs
Expand Up @@ -79,9 +79,11 @@
//!
//! # Cargo features
//!
//! This crate supports two cargo features:
//! This crate supports three cargo features:
//!
//! - `owning_ref`: Allows your lock types to be used with the `owning_ref` crate.
//! - `arc_lock`: Enables locking from an `Arc`. This enables types such as `ArcMutexGuard`. Note that this
//! requires the `alloc` crate to be present.
//! - `nightly`: Enables nightly-only features. At the moment the only such
//! feature is `const fn` constructors for lock types.

Expand All @@ -93,6 +95,9 @@
#[macro_use]
extern crate scopeguard;

#[cfg(feature = "arc_lock")]
extern crate alloc;

/// Marker type which indicates that the Guard type for a lock is `Send`.
pub struct GuardSend(());

Expand Down
189 changes: 189 additions & 0 deletions lock_api/src/mutex.rs
Expand Up @@ -11,6 +11,13 @@ use core::marker::PhantomData;
use core::mem;
use core::ops::{Deref, DerefMut};

#[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;

Expand Down Expand Up @@ -286,6 +293,45 @@ impl<R: RawMutex, T: ?Sized> Mutex<R, T> {
pub fn data_ptr(&self) -> *mut T {
self.data.get()
}

/// # Safety
///
/// The lock needs to be held for the behavior of this function to be defined.
#[cfg(feature = "arc_lock")]
#[inline]
unsafe fn guard_arc(self: &Arc<Self>) -> ArcMutexGuard<R, T> {
ArcMutexGuard {
mutex: self.clone(),
marker: PhantomData,
}
}

/// Acquires a lock through an `Arc`.
///
/// This method is similar to the `lock` method; however, it requires the `Mutex` 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>) -> ArcMutexGuard<R, T> {
self.raw.lock();
// SAFETY: the locking guarantee is upheld
unsafe { self.guard_arc() }
}

/// Attempts to acquire a lock through an `Arc`.
///
/// This method is similar to the `try_lock` method; however, it requires the `Mutex` 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<ArcMutexGuard<R, T>> {
if self.raw.try_lock() {
// SAFETY: locking guarantee is upheld
Some(unsafe { self.guard_arc() })
} else {
None
}
}
}

impl<R: RawMutexFair, T: ?Sized> Mutex<R, T> {
Expand Down Expand Up @@ -336,6 +382,39 @@ impl<R: RawMutexTimed, T: ?Sized> Mutex<R, T> {
None
}
}

/// Attempts to acquire this lock through an `Arc` until a timeout is reached.
///
/// This method is similar to the `try_lock_for` method; however, it requires the `Mutex` 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<ArcMutexGuard<R, 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 through an `Arc` until a timeout is reached.
///
/// This method is similar to the `try_lock_until` method; however, it requires the `Mutex` 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<ArcMutexGuard<R, T>> {
if self.raw.try_lock_until(timeout) {
// SAFETY: locking guarantee is upheld
Some(unsafe { self.guard_arc() })
} else {
None
}
}
}

impl<R: RawMutex, T: ?Sized + Default> Default for Mutex<R, T> {
Expand Down Expand Up @@ -583,6 +662,116 @@ impl<'a, R: RawMutex + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display for Mutex
#[cfg(feature = "owning_ref")]
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
/// uses an `Arc<Mutex>`. 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"]
pub struct ArcMutexGuard<R: RawMutex, T: ?Sized> {
mutex: Arc<Mutex<R, T>>,
marker: PhantomData<R::GuardMarker>,
}

#[cfg(feature = "arc_lock")]
impl<R: RawMutex, T: ?Sized> ArcMutexGuard<R, T> {
/// Returns a reference to the `Mutex` this is guarding, contained in its `Arc`.
#[inline]
pub fn mutex(&self) -> &Arc<Mutex<R, T>> {
&self.mutex
}

/// 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 MutexGuard always holds the lock.
unsafe {
s.mutex.raw.unlock();
}
defer!(s.mutex.raw.lock());
f()
}
}

#[cfg(feature = "arc_lock")]
impl<R: RawMutexFair, T: ?Sized> ArcMutexGuard<R, T> {
/// Unlocks the mutex using a fair unlock protocol.
///
/// This is functionally identical to the `unlock_fair` method on [`MutexGuard`].
#[inline]
pub fn unlock_fair(s: Self) {
// Safety: A MutexGuard always holds the lock.
unsafe {
s.mutex.raw.unlock_fair();
}

// SAFETY: make sure the Arc gets it reference decremented
let mut s = ManuallyDrop::new(s);
unsafe { ptr::drop_in_place(&mut s.mutex) };
}

/// Temporarily unlocks the mutex to execute the given function.
///
/// This is functionally identical to the `unlocked_fair` method on [`MutexGuard`].
#[inline]
pub fn unlocked_fair<F, U>(s: &mut Self, f: F) -> U
where
F: FnOnce() -> U,
{
// Safety: A MutexGuard always holds the lock.
unsafe {
s.mutex.raw.unlock_fair();
}
defer!(s.mutex.raw.lock());
f()
}

/// Temporarily yields the mutex to a waiting thread if there is one.
///
/// This is functionally identical to the `bump` method on [`MutexGuard`].
#[inline]
pub fn bump(s: &mut Self) {
// Safety: A MutexGuard always holds the lock.
unsafe {
s.mutex.raw.bump();
}
}
}

#[cfg(feature = "arc_lock")]
impl<R: RawMutex, T: ?Sized> Deref for ArcMutexGuard<R, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.mutex.data.get() }
}
}

#[cfg(feature = "arc_lock")]
impl<R: RawMutex, T: ?Sized> DerefMut for ArcMutexGuard<R, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.mutex.data.get() }
}
}

#[cfg(feature = "arc_lock")]
impl<R: RawMutex, T: ?Sized> Drop for ArcMutexGuard<R, T> {
#[inline]
fn drop(&mut self) {
// Safety: A MutexGuard always holds the lock.
unsafe {
self.mutex.raw.unlock();
}
}
}

/// An RAII mutex guard returned by `MutexGuard::map`, which can point to a
/// subfield of the protected data.
///
Expand Down

0 comments on commit 5cb2c0a

Please sign in to comment.