| // 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; |
| |
| pub use crate::sys_common::mutex::SgxThreadMutex; |
| |
| /// A mutual exclusion primitive useful for protecting shared data |
| /// |
| /// This mutex will block threads waiting for the lock to become available. The |
| /// mutex can also be statically initialized or 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, 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, 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_type")] |
| pub struct SgxMutex<T: ?Sized> { |
| inner: sys::SgxMovableThreadMutex, |
| 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"] |
| 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); |
| /// ``` |
| pub fn new(t: T) -> SgxMutex<T> { |
| SgxMutex { |
| inner: sys::SgxMovableThreadMutex::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 { |
| match self.inner.try_lock() { |
| Ok(_) => Ok(SgxMutexGuard::new(self)?), |
| Err(_) => 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() |
| } |
| |
| /// 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; |
| /// |
| /// let mutex = SgxMutex::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; |
| /// |
| /// let mut mutex = SgxMutex::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.borrow(), |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) { |
| let result = unsafe { |
| self.lock.poison.done(&self.poison); |
| self.lock.inner.raw_unlock() |
| }; |
| debug_assert_eq!(result, Ok(()), "Error when unlocking an SgxMutex: {}", result.unwrap_err()); |
| } |
| } |
| |
| 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::SgxMovableThreadMutex { |
| &guard.lock.inner |
| } |
| |
| pub fn guard_poison<'a, T: ?Sized>(guard: &SgxMutexGuard<'a, T>) -> &'a poison::Flag { |
| &guard.lock.poison |
| } |