sgx_tstd/src/sync/mutex.rs (208 lines of code) (raw):

// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License.. //! //! The Intel(R) Software Guard Extensions SDK already supports mutex and conditional //! variable synchronization mechanisms by means of the following API and data types //! defined in the Types and Enumerations section. Some functions included in the //! trusted Thread Synchronization library may make calls outside the enclave (OCALLs). //! If you use any of the APIs below, you must first import the needed OCALL functions //! from sgx_tstd.edl. Otherwise, you will get a linker error when the enclave is //! being built; see Calling Functions outside the Enclave for additional details. //! The table below illustrates the primitives that the Intel(R) SGX Thread //! Synchronization library supports, as well as the OCALLs that each API function needs. //! use crate::cell::UnsafeCell; use crate::fmt; use crate::ops::{Deref, DerefMut}; use crate::sync::{poison, LockResult, TryLockError, TryLockResult}; use crate::sys_common::mutex as sys; /// 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 /// /// # Examples /// /// ``` /// use std::sync::{Arc, SgxMutex as Mutex}; /// use std::thread; /// use std::sync::mpsc::channel; /// /// const N: usize = 10; /// /// // Spawn a few threads to increment a shared variable (non-atomically), and /// // let the main thread know once all increments are done. /// // /// // Here we're using an Arc to share memory among threads, and the data inside /// // the Arc is protected with a mutex. /// let data = Arc::new(Mutex::new(0)); /// /// let (tx, rx) = channel(); /// for _ in 0..N { /// let (data, tx) = (Arc::clone(&data), tx.clone()); /// thread::spawn(move || { /// // The shared state can only be accessed once the lock is held. /// // Our non-atomic increment is safe because we're the only thread /// // which can access the shared state when the lock is held. /// // /// // We unwrap() the return value to assert that we are not expecting /// // threads to ever fail while holding the lock. /// let mut data = data.lock().unwrap(); /// *data += 1; /// if *data == N { /// tx.send(()).unwrap(); /// } /// // the lock is unlocked here when `data` goes out of scope. /// }); /// } /// /// rx.recv().unwrap(); /// ``` /// /// To recover from a poisoned mutex: /// /// ``` /// use std::sync::{Arc, SgxMutex as Mutex}; /// use std::thread; /// /// let lock = Arc::new(Mutex::new(0_u32)); /// let lock2 = Arc::clone(&lock); /// /// let _ = thread::spawn(move || -> () { /// // This thread will acquire the mutex first, unwrapping the result of /// // `lock` because the lock has not been poisoned. /// let _guard = lock2.lock().unwrap(); /// /// // This panic while holding the lock (`_guard` is in scope) will poison /// // the mutex. /// panic!(); /// }).join(); /// /// // The lock is poisoned by this point, but the returned result can be /// // pattern matched on to return the underlying guard on both branches. /// let mut guard = match lock.lock() { /// Ok(guard) => guard, /// Err(poisoned) => poisoned.into_inner(), /// }; /// /// *guard += 1; /// ``` /// /// It is sometimes necessary to manually drop the mutex guard to unlock it /// sooner than the end of the enclosing scope. /// /// ``` /// use std::sync::{Arc, SgxMutex as Mutex}; /// use std::thread; /// /// const N: usize = 3; /// /// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4])); /// let res_mutex = Arc::new(Mutex::new(0)); /// /// let mut threads = Vec::with_capacity(N); /// (0..N).for_each(|_| { /// let data_mutex_clone = Arc::clone(&data_mutex); /// let res_mutex_clone = Arc::clone(&res_mutex); /// /// threads.push(thread::spawn(move || { /// let mut data = data_mutex_clone.lock().unwrap(); /// // This is the result of some important and long-ish work. /// let result = data.iter().fold(0, |acc, x| acc + x * 2); /// data.push(result); /// drop(data); /// *res_mutex_clone.lock().unwrap() += result; /// })); /// }); /// /// let mut data = data_mutex.lock().unwrap(); /// // This is the result of some important and long-ish work. /// let result = data.iter().fold(0, |acc, x| acc + x * 2); /// data.push(result); /// // We drop the `data` explicitly because it's not necessary anymore and the /// // thread still has work to do. This allow other threads to start working on /// // the data immediately, without waiting for the rest of the unrelated work /// // to be done here. /// // /// // It's even more important here than in the threads because we `.join` the /// // threads after that. If we had not dropped the mutex guard, a thread could /// // be waiting forever for it, causing a deadlock. /// drop(data); /// // Here the mutex guard is not assigned to a variable and so, even if the /// // scope does not end after this line, the mutex is still released: there is /// // no deadlock. /// *res_mutex.lock().unwrap() += result; /// /// threads.into_iter().for_each(|thread| { /// thread /// .join() /// .expect("The thread creating or execution failed !") /// }); /// /// assert_eq!(*res_mutex.lock().unwrap(), 800); /// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "Mutex")] pub struct SgxMutex<T: ?Sized> { inner: sys::MovableMutex, 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 SgxMutex<T> {} unsafe impl<T: ?Sized + Send> Sync for SgxMutex<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 SgxMutexGuard<'a, T: ?Sized + 'a> { lock: &'a SgxMutex<T>, poison: poison::Guard, } impl<T: ?Sized> !Send for SgxMutexGuard<'_, T> {} unsafe impl<T: ?Sized + Sync> Sync for SgxMutexGuard<'_, T> {} impl<T> SgxMutex<T> { /// Creates a new mutex in an unlocked state ready for use. /// /// # Examples /// /// ``` /// use std::sync::SgxMutex; /// /// let mutex = SgxMutex::new(0); /// ``` #[inline] pub const fn new(t: T) -> SgxMutex<T> { SgxMutex { inner: sys::MovableMutex::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t), } } } impl<T: ?Sized> SgxMutex<T> { /// /// The function locks a trusted mutex object within an enclave. /// /// 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. /// /// # Examples /// /// ``` /// use std::sync::{Arc, SgxMutex as Mutex}; /// use std::thread; /// /// let mutex = Arc::new(Mutex::new(0)); /// let c_mutex = Arc::clone(&mutex); /// /// thread::spawn(move || { /// *c_mutex.lock().unwrap() = 10; /// }).join().expect("thread::spawn failed"); /// assert_eq!(*mutex.lock().unwrap(), 10); /// ``` pub fn lock(&self) -> LockResult<SgxMutexGuard<'_, T>> { unsafe { self.inner.raw_lock(); SgxMutexGuard::new(self) } } /// /// The function tries to lock a trusted mutex object within an enclave. /// /// 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 /// /// # Examples /// /// ``` /// use std::sync::{Arc, SgxMutex as Mutex}; /// use std::thread; /// /// let mutex = Arc::new(Mutex::new(0)); /// let c_mutex = Arc::clone(&mutex); /// /// thread::spawn(move || { /// let mut lock = c_mutex.try_lock(); /// if let Ok(ref mut mutex) = lock { /// **mutex = 10; /// } else { /// println!("try_lock failed"); /// } /// }).join().expect("thread::spawn failed"); /// assert_eq!(*mutex.lock().unwrap(), 10); /// ``` pub fn try_lock(&self) -> TryLockResult<SgxMutexGuard<'_, T>> { unsafe { if self.inner.try_lock() { Ok(SgxMutexGuard::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::SgxMutex as Mutex; /// let mutex = Mutex::new(0); /// /// let mut guard = mutex.lock().unwrap(); /// *guard += 20; /// Mutex::unlock(guard); /// ``` pub fn unlock(guard: SgxMutexGuard<'_, 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. /// /// # Examples /// /// ``` /// use std::sync::{Arc, SgxMutex as Mutex}; /// use std::thread; /// /// let mutex = Arc::new(Mutex::new(0)); /// let c_mutex = Arc::clone(&mutex); /// /// let _ = thread::spawn(move || { /// let _lock = c_mutex.lock().unwrap(); /// panic!(); // the mutex gets poisoned /// }).join(); /// assert_eq!(mutex.is_poisoned(), true); /// ``` #[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. /// /// # Examples /// /// ``` /// #![feature(mutex_unpoison)] /// /// use std::sync::{Arc, SgxMutex as Mutex}; /// use std::thread; /// /// let mutex = Arc::new(Mutex::new(0)); /// let c_mutex = Arc::clone(&mutex); /// /// let _ = thread::spawn(move || { /// let _lock = c_mutex.lock().unwrap(); /// panic!(); // the mutex gets poisoned /// }).join(); /// /// assert_eq!(mutex.is_poisoned(), true); /// let x = mutex.lock().unwrap_or_else(|mut e| { /// **e.get_mut() = 1; /// mutex.clear_poison(); /// e.into_inner() /// }); /// assert_eq!(mutex.is_poisoned(), false); /// assert_eq!(*x, 1); /// ``` #[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::SgxMutex as 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::SgxMutex as 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 SgxMutex<T> { /// Creates a new mutex in an unlocked state ready for use. /// This is equivalent to [`Mutex::new`]. fn from(t: T) -> Self { SgxMutex::new(t) } } impl<T: ?Sized + Default> Default for SgxMutex<T> { /// Creates a `SgxMutex<T>`, with the `Default` value for T. fn default() -> SgxMutex<T> { SgxMutex::new(Default::default()) } } impl<T: ?Sized + fmt::Debug> fmt::Debug for SgxMutex<T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_struct("SgxMutex"); 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> SgxMutexGuard<'mutex, T> { unsafe fn new(lock: &'mutex SgxMutex<T>) -> LockResult<SgxMutexGuard<'mutex, T>> { poison::map_result(lock.poison.guard(), |guard| SgxMutexGuard { lock, poison: guard }) } } impl<T: ?Sized> Deref for SgxMutexGuard<'_, T> { type Target = T; fn deref(&self) -> &T { unsafe { &*self.lock.data.get() } } } impl<T: ?Sized> DerefMut for SgxMutexGuard<'_, T> { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.lock.data.get() } } } impl<T: ?Sized> Drop for SgxMutexGuard<'_, T> { #[inline] fn drop(&mut self) { unsafe { self.lock.poison.done(&self.poison); self.lock.inner.raw_unlock(); } } } impl<T: ?Sized + fmt::Debug> fmt::Debug for SgxMutexGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } impl<T: ?Sized + fmt::Display> fmt::Display for SgxMutexGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } pub fn guard_lock<'a, T: ?Sized>(guard: &SgxMutexGuard<'a, T>) -> &'a sys::MovableMutex { &guard.lock.inner } pub fn guard_poison<'a, T: ?Sized>(guard: &SgxMutexGuard<'a, T>) -> &'a poison::Flag { &guard.lock.poison }