-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add `mutex.rs` implementation which more or less copies from [rust source](https://github.com/rust-lang/rust.git) at [606c3907](rust-lang/rust@606c390)
- Loading branch information
1 parent
1d47a22
commit 538e33b
Showing
6 changed files
with
359 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,347 @@ | ||
// Copyright (c) The Rust Foundation | ||
// Copyright (c) 2023 The MobileCoin Foundation | ||
|
||
//! mutex.rs implementation more or less copied from | ||
//! [rust source](https://github.com/rust-lang/rust.git) at | ||
//! [606c3907](https://github.com/rust-lang/rust/commit/606c3907251397a42e23d3e60de31be9d32525d5) | ||
//! | ||
//! Differences: | ||
//! - The imports were changed to work with the `mc-sgx` crates. | ||
//! - The stable attributes have been removed | ||
//! - The unstable attributes have been removed | ||
//! - Items that are crate only were converted from `pub` to `pub(crate)` | ||
//! - Removed examples that were not possible in an SGX enclave have been omitted | ||
//! - Ran `cargo fmt` | ||
//! - Removed unnecessary unsafe blocks | ||
#![allow(dead_code)] | ||
|
||
use crate::sys::locks as sys; | ||
use crate::{poison, LockResult, TryLockError, TryLockResult}; | ||
use core::cell::UnsafeCell; | ||
use core::fmt; | ||
use core::ops::{Deref, DerefMut}; | ||
|
||
/// A mutual exclusion primitive useful for protecting shared data | ||
/// | ||
/// This mutex will block threads waiting for the lock to become available. The | ||
/// mutex can be created via a [`new`] constructor. Each mutex has a type parameter | ||
/// which represents the data that it is protecting. The data can only be accessed | ||
/// through the RAII guards returned from [`lock`] and [`try_lock`], which | ||
/// guarantees that the data is only ever accessed when the mutex is locked. | ||
/// | ||
/// # Poisoning | ||
/// | ||
/// The mutexes in this module implement a strategy called "poisoning" where a | ||
/// mutex is considered poisoned whenever a thread panics while holding the | ||
/// mutex. Once a mutex is poisoned, all other threads are unable to access the | ||
/// data by default as it is likely tainted (some invariant is not being | ||
/// upheld). | ||
/// | ||
/// For a mutex, this means that the [`lock`] and [`try_lock`] methods return a | ||
/// [`Result`] which indicates whether a mutex has been poisoned or not. Most | ||
/// usage of a mutex will simply [`unwrap()`] these results, propagating panics | ||
/// among threads to ensure that a possibly invalid invariant is not witnessed. | ||
/// | ||
/// A poisoned mutex, however, does not prevent all access to the underlying | ||
/// data. The [`PoisonError`] type has an [`into_inner`] method which will return | ||
/// the guard that would have otherwise been returned on a successful lock. This | ||
/// allows access to the data, despite the lock being poisoned. | ||
/// | ||
/// [`new`]: Self::new | ||
/// [`lock`]: Self::lock | ||
/// [`try_lock`]: Self::try_lock | ||
/// [`unwrap()`]: Result::unwrap | ||
/// [`PoisonError`]: super::PoisonError | ||
/// [`into_inner`]: super::PoisonError::into_inner | ||
pub struct Mutex<T: ?Sized> { | ||
inner: sys::Mutex, | ||
poison: poison::Flag, | ||
data: UnsafeCell<T>, | ||
} | ||
|
||
// these are the only places where `T: Send` matters; all other | ||
// functionality works fine on a single thread. | ||
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {} | ||
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {} | ||
|
||
/// An RAII implementation of a "scoped lock" of a 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`] and [`DerefMut`] implementations. | ||
/// | ||
/// This structure is created by the [`lock`] and [`try_lock`] methods on | ||
/// [`Mutex`]. | ||
/// | ||
/// [`lock`]: Mutex::lock | ||
/// [`try_lock`]: Mutex::try_lock | ||
#[must_use = "if unused the Mutex will immediately unlock"] | ||
#[must_not_suspend = "holding a MutexGuard across suspend \ | ||
points can cause deadlocks, delays, \ | ||
and cause Futures to not implement `Send`"] | ||
#[clippy::has_significant_drop] | ||
pub struct MutexGuard<'a, T: ?Sized + 'a> { | ||
lock: &'a Mutex<T>, | ||
poison: poison::Guard, | ||
} | ||
|
||
impl<T: ?Sized> !Send for MutexGuard<'_, T> {} | ||
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {} | ||
|
||
impl<T> Mutex<T> { | ||
/// Creates a new mutex in an unlocked state ready for use. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// use std::sync::Mutex; | ||
/// | ||
/// let mutex = Mutex::new(0); | ||
/// ``` | ||
#[inline] | ||
pub const fn new(t: T) -> Mutex<T> { | ||
Mutex { | ||
inner: sys::Mutex::new(), | ||
poison: poison::Flag::new(), | ||
data: UnsafeCell::new(t), | ||
} | ||
} | ||
} | ||
|
||
impl<T: ?Sized> Mutex<T> { | ||
/// Acquires a mutex, blocking the current thread until it is able to do so. | ||
/// | ||
/// This function will block the local thread until it is available to acquire | ||
/// the mutex. Upon returning, the thread is the only thread with the lock | ||
/// 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. | ||
/// | ||
/// The exact behavior on locking a mutex in the thread which already holds | ||
/// the lock is left unspecified. However, this function will not return on | ||
/// the second call (it might panic or deadlock, for example). | ||
/// | ||
/// # Errors | ||
/// | ||
/// If another user of this mutex panicked while holding the mutex, then | ||
/// this call will return an error once the mutex is acquired. | ||
/// | ||
/// # Panics | ||
/// | ||
/// This function might panic when called if the lock is already held by | ||
/// the current thread. | ||
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> { | ||
unsafe { | ||
self.inner.lock(); | ||
MutexGuard::new(self) | ||
} | ||
} | ||
|
||
/// Attempts to acquire this lock. | ||
/// | ||
/// If the lock could not be acquired at this time, then [`Err`] is returned. | ||
/// Otherwise, an RAII guard is returned. The lock will be unlocked when the | ||
/// guard is dropped. | ||
/// | ||
/// This function does not block. | ||
/// | ||
/// # Errors | ||
/// | ||
/// If another user of this mutex panicked while holding the mutex, then | ||
/// this call will return the [`Poisoned`] error if the mutex would | ||
/// otherwise be acquired. | ||
/// | ||
/// If the mutex could not be acquired because it is already locked, then | ||
/// this call will return the [`WouldBlock`] error. | ||
/// | ||
/// [`Poisoned`]: TryLockError::Poisoned | ||
/// [`WouldBlock`]: TryLockError::WouldBlock | ||
pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> { | ||
unsafe { | ||
if self.inner.try_lock() { | ||
Ok(MutexGuard::new(self)?) | ||
} else { | ||
Err(TryLockError::WouldBlock) | ||
} | ||
} | ||
} | ||
|
||
/// Immediately drops the guard, and consequently unlocks the mutex. | ||
/// | ||
/// This function is equivalent to calling [`drop`] on the guard but is more self-documenting. | ||
/// Alternately, the guard will be automatically dropped when it goes out of scope. | ||
/// | ||
/// ``` | ||
/// #![feature(mutex_unlock)] | ||
/// | ||
/// use std::sync::Mutex; | ||
/// let mutex = Mutex::new(0); | ||
/// | ||
/// let mut guard = mutex.lock().unwrap(); | ||
/// *guard += 20; | ||
/// Mutex::unlock(guard); | ||
/// ``` | ||
pub fn unlock(guard: MutexGuard<'_, T>) { | ||
drop(guard); | ||
} | ||
|
||
/// Determines whether the mutex is poisoned. | ||
/// | ||
/// If another thread is active, the mutex can still become poisoned at any | ||
/// time. You should not trust a `false` value for program correctness | ||
/// without additional synchronization. | ||
#[inline] | ||
pub fn is_poisoned(&self) -> bool { | ||
self.poison.get() | ||
} | ||
|
||
/// Clear the poisoned state from a mutex | ||
/// | ||
/// If the mutex is poisoned, it will remain poisoned until this function is called. This | ||
/// allows recovering from a poisoned state and marking that it has recovered. For example, if | ||
/// the value is overwritten by a known-good value, then the mutex can be marked as | ||
/// un-poisoned. Or possibly, the value could be inspected to determine if it is in a | ||
/// consistent state, and if so the poison is removed. | ||
#[inline] | ||
pub fn clear_poison(&self) { | ||
self.poison.clear(); | ||
} | ||
|
||
/// Consumes this mutex, returning the underlying data. | ||
/// | ||
/// # Errors | ||
/// | ||
/// If another user of this mutex panicked while holding the mutex, then | ||
/// this call will return an error instead. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// use std::sync::Mutex; | ||
/// | ||
/// let mutex = Mutex::new(0); | ||
/// assert_eq!(mutex.into_inner().unwrap(), 0); | ||
/// ``` | ||
pub fn into_inner(self) -> LockResult<T> | ||
where | ||
T: Sized, | ||
{ | ||
let data = self.data.into_inner(); | ||
poison::map_result(self.poison.borrow(), |()| data) | ||
} | ||
|
||
/// Returns a mutable reference to the underlying data. | ||
/// | ||
/// Since this call borrows the `Mutex` mutably, no actual locking needs to | ||
/// take place -- the mutable borrow statically guarantees no locks exist. | ||
/// | ||
/// # Errors | ||
/// | ||
/// If another user of this mutex panicked while holding the mutex, then | ||
/// this call will return an error instead. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// use std::sync::Mutex; | ||
/// | ||
/// let mut mutex = Mutex::new(0); | ||
/// *mutex.get_mut().unwrap() = 10; | ||
/// assert_eq!(*mutex.lock().unwrap(), 10); | ||
/// ``` | ||
pub fn get_mut(&mut self) -> LockResult<&mut T> { | ||
let data = self.data.get_mut(); | ||
poison::map_result(self.poison.borrow(), |()| data) | ||
} | ||
} | ||
|
||
impl<T> From<T> for Mutex<T> { | ||
/// Creates a new mutex in an unlocked state ready for use. | ||
/// This is equivalent to [`Mutex::new`]. | ||
fn from(t: T) -> Self { | ||
Mutex::new(t) | ||
} | ||
} | ||
|
||
impl<T: ?Sized + Default> Default for Mutex<T> { | ||
/// Creates a `Mutex<T>`, with the `Default` value for T. | ||
fn default() -> Mutex<T> { | ||
Mutex::new(Default::default()) | ||
} | ||
} | ||
|
||
impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
let mut d = f.debug_struct("Mutex"); | ||
match self.try_lock() { | ||
Ok(guard) => { | ||
d.field("data", &&*guard); | ||
} | ||
Err(TryLockError::Poisoned(err)) => { | ||
d.field("data", &&**err.get_ref()); | ||
} | ||
Err(TryLockError::WouldBlock) => { | ||
struct LockedPlaceholder; | ||
impl fmt::Debug for LockedPlaceholder { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
f.write_str("<locked>") | ||
} | ||
} | ||
d.field("data", &LockedPlaceholder); | ||
} | ||
} | ||
d.field("poisoned", &self.poison.get()); | ||
d.finish_non_exhaustive() | ||
} | ||
} | ||
|
||
impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { | ||
unsafe fn new(lock: &'mutex Mutex<T>) -> LockResult<MutexGuard<'mutex, T>> { | ||
poison::map_result(lock.poison.guard(), |guard| MutexGuard { | ||
lock, | ||
poison: guard, | ||
}) | ||
} | ||
} | ||
|
||
impl<T: ?Sized> Deref for MutexGuard<'_, T> { | ||
type Target = T; | ||
|
||
fn deref(&self) -> &T { | ||
unsafe { &*self.lock.data.get() } | ||
} | ||
} | ||
|
||
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> { | ||
fn deref_mut(&mut self) -> &mut T { | ||
unsafe { &mut *self.lock.data.get() } | ||
} | ||
} | ||
|
||
impl<T: ?Sized> Drop for MutexGuard<'_, T> { | ||
#[inline] | ||
fn drop(&mut self) { | ||
self.lock.poison.done(&self.poison); | ||
self.lock.inner.unlock(); | ||
} | ||
} | ||
|
||
impl<T: ?Sized + fmt::Debug> fmt::Debug for MutexGuard<'_, T> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
fmt::Debug::fmt(&**self, f) | ||
} | ||
} | ||
|
||
impl<T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'_, T> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
(**self).fmt(f) | ||
} | ||
} | ||
|
||
pub(crate) fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { | ||
&guard.lock.inner | ||
} | ||
|
||
pub(crate) fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag { | ||
&guard.lock.poison | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// Copyright (c) 2023 The MobileCoin Foundation | ||
|
||
//! Platform specific concurrency locking primitives. | ||
mod mutex; | ||
pub(crate) use mutex::Mutex; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters