Skip to content

Commit

Permalink
Add Condvar to mc-sgx-sync crate
Browse files Browse the repository at this point in the history
Add `condvar.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
nick-mobilecoin committed Jan 26, 2023
1 parent 454435e commit 1863475
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 1 deletion.
228 changes: 228 additions & 0 deletions sync/src/condvar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// Copyright (c) The Rust Foundation
// Copyright (c) 2023 The MobileCoin Foundation

//! condvar.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
//! - Removed timeout since there isn't a secure timer in SGX enclaves
use crate::sys::locks as sys;
use crate::{mutex, LockResult, MutexGuard, PoisonError};
use core::fmt;

/// A Condition Variable
///
/// Condition variables represent the ability to block a thread such that it
/// consumes no CPU time while waiting for an event to occur. Condition
/// variables are typically associated with a boolean predicate (a condition)
/// and a mutex. The predicate is always verified inside of the mutex before
/// determining that a thread must block.
///
/// Functions in this module will block the current **thread** of execution.
/// Note that any attempt to use multiple mutexes on the same condition
/// variable may result in a runtime panic.
pub struct Condvar {
inner: sys::Condvar,
}

impl Condvar {
/// Creates a new condition variable which is ready to be waited on and
/// notified.
///
/// # Examples
///
/// ```
/// use mc_sgx_sync::Condvar;
///
/// let condvar = Condvar::new();
/// ```
#[must_use]
pub const fn new() -> Condvar {
Condvar {
inner: sys::Condvar::new(),
}
}

/// Blocks the current thread until this condition variable receives a
/// notification.
///
/// This function will atomically unlock the mutex specified (represented by
/// `guard`) and block the current thread. This means that any calls
/// to [`notify_one`] or [`notify_all`] which happen logically after the
/// mutex is unlocked are candidates to wake this thread up. When this
/// function call returns, the lock specified will have been re-acquired.
///
/// Note that this function is susceptible to spurious wakeups. Condition
/// variables normally have a boolean predicate associated with them, and
/// the predicate must always be checked each time this function returns to
/// protect against spurious wakeups.
///
/// # Errors
///
/// This function will return an error if the mutex being waited on is
/// poisoned when this thread re-acquires the lock. For more information,
/// see information about [poisoning] on the [`Mutex`] type.
///
/// # Panics
///
/// This function may [`panic!`] if it is used with more than one mutex
/// over time.
///
/// [`notify_one`]: Self::notify_one
/// [`notify_all`]: Self::notify_all
/// [poisoning]: super::Mutex#poisoning
/// [`Mutex`]: super::Mutex
///
/// # Examples
///
/// ```
/// use mc_sgx_sync::{Mutex, Condvar};
///
/// let pair = (Mutex::new(false), Condvar::new());
///
/// // Wait for the thread to start up.
/// let (lock, cvar) = &*pair;
/// let mut started = lock.lock().unwrap();
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
/// while !*started {
/// started = cvar.wait(started).unwrap();
/// }
/// ```
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult<MutexGuard<'a, T>> {
let poisoned = {
let lock = mutex::guard_lock(&guard);
self.inner.wait(lock);
mutex::guard_poison(&guard).get()
};
if poisoned {
Err(PoisonError::new(guard))
} else {
Ok(guard)
}
}

/// Blocks the current thread until this condition variable receives a
/// notification and the provided condition is false.
///
/// This function will atomically unlock the mutex specified (represented by
/// `guard`) and block the current thread. This means that any calls
/// to [`notify_one`] or [`notify_all`] which happen logically after the
/// mutex is unlocked are candidates to wake this thread up. When this
/// function call returns, the lock specified will have been re-acquired.
///
/// # Errors
///
/// This function will return an error if the mutex being waited on is
/// poisoned when this thread re-acquires the lock. For more information,
/// see information about [poisoning] on the [`Mutex`] type.
///
/// [`notify_one`]: Self::notify_one
/// [`notify_all`]: Self::notify_all
/// [poisoning]: super::Mutex#poisoning
/// [`Mutex`]: super::Mutex
///
/// # Examples
///
/// ```
/// use mc_sgx_sync::{Mutex, Condvar};
///
/// let pair = (Mutex::new(true), Condvar::new());
///
/// // Wait for the thread to start up.
/// let (lock, cvar) = &*pair;
/// // As long as the value inside the `Mutex<bool>` is `true`, we wait.
/// let _guard = cvar.wait_while(lock.lock().unwrap(), |pending| { *pending }).unwrap();
/// ```
pub fn wait_while<'a, T, F>(
&self,
mut guard: MutexGuard<'a, T>,
mut condition: F,
) -> LockResult<MutexGuard<'a, T>>
where
F: FnMut(&mut T) -> bool,
{
while condition(&mut *guard) {
guard = self.wait(guard)?;
}
Ok(guard)
}

/// Wakes up one blocked thread on this condvar.
///
/// If there is a blocked thread on this condition variable, then it will
/// be woken up from its call to [`wait`]. Calls to `notify_one` are not
/// buffered in any way.
///
/// To wake up all threads, see [`notify_all`].
///
/// [`wait`]: Self::wait
/// [`notify_all`]: Self::notify_all
///
/// # Examples
///
/// ```
/// use mc_sgx_sync::{Mutex, Condvar};
///
/// let pair = (Mutex::new(false), Condvar::new());
///
/// let (lock, cvar) = &*pair;
/// // We notify the condvar that the value has changed.
/// {
/// let mut started = lock.lock().unwrap();
/// *started = true;
/// cvar.notify_one();
/// }
/// ```
pub fn notify_one(&self) {
self.inner.notify_one()
}

/// Wakes up all blocked threads on this condvar.
///
/// This method will ensure that any current waiters on the condition
/// variable are awoken. Calls to `notify_all()` are not buffered in any
/// way.
///
/// To wake up only one thread, see [`notify_one`].
///
/// [`notify_one`]: Self::notify_one
///
/// # Examples
///
/// ```
/// use mc_sgx_sync::{Mutex, Condvar};
///
/// let pair = (Mutex::new(false), Condvar::new());
///
/// {
/// let mut started = lock.lock().unwrap();
/// *started = true;
/// // We notify the condvar that the value has changed.
/// cvar.notify_all();
/// }
/// ```
pub fn notify_all(&self) {
self.inner.notify_all()
}
}

impl fmt::Debug for Condvar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Condvar").finish_non_exhaustive()
}
}

impl Default for Condvar {
/// Creates a `Condvar` which is ready to be waited on and notified.
fn default() -> Condvar {
Condvar::new()
}
}
2 changes: 2 additions & 0 deletions sync/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
#![no_std]
#![feature(error_in_core, must_not_suspend, negative_impls)]

mod condvar;
mod mutex;
mod poison;
pub use condvar::Condvar;
pub use mutex::{Mutex, MutexGuard};
pub use poison::{LockResult, PoisonError, TryLockError, TryLockResult};
mod sys;
1 change: 1 addition & 0 deletions sync/src/sys/locks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
mod condvar;
mod mutex;

pub(crate) use condvar::Condvar;
pub(crate) use mutex::Mutex;
1 change: 0 additions & 1 deletion sync/src/sys/locks/condvar.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) 2023 The MobileCoin Foundation

//! Rust condition variable implementation used in SGX environments
#![allow(dead_code)]
use crate::sys::locks::Mutex;
use mc_sgx_tstdc::Condvar as SgxCondvar;

Expand Down

0 comments on commit 1863475

Please sign in to comment.