| // Copyright (C) 2017-2018 Baidu, Inc. All Rights Reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions |
| // are met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above copyright |
| // notice, this list of conditions and the following disclaimer in |
| // the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Baidu, Inc., nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| //! |
| //! The Intel(R) Software Guard Extensions SDK already supports mutex and conditional |
| //! variable synchronization mechanisms by means of the following APIand 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_tstdc.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 sgx_types::{self, SysError, sgx_thread_mutex_t, sgx_thread_cond_t, sgx_thread_condattr_t}; |
| use sgx_trts::libc; |
| use sgx_trts::trts::rsgx_abort; |
| use super::mutex::{self, SgxThreadMutex, SgxMutexGuard}; |
| use sys_common::poison::{LockResult, PoisonError}; |
| use core::sync::atomic::{AtomicUsize, Ordering}; |
| use core::cell::UnsafeCell; |
| use core::fmt; |
| use core::mem; |
| use core::alloc::AllocErr; |
| use alloc::boxed::Box; |
| |
| pub unsafe fn raw_cond(lock: &mut sgx_thread_cond_t) -> * mut sgx_thread_cond_t { |
| lock as * mut _ |
| } |
| |
| #[allow(dead_code)] |
| unsafe fn rsgx_thread_cond_init(cond: &mut sgx_thread_cond_t, unused: &sgx_thread_condattr_t ) -> SysError { |
| |
| let ret = sgx_types::sgx_thread_cond_init(raw_cond(cond), unused as * const sgx_thread_condattr_t); |
| if ret == 0 { Ok(()) } else { Err(ret) } |
| } |
| |
| unsafe fn rsgx_thread_cond_destroy(cond: &mut sgx_thread_cond_t) -> SysError { |
| |
| let ret = sgx_types::sgx_thread_cond_destroy(raw_cond(cond)); |
| if ret == 0 { Ok(()) } else { Err(ret) } |
| } |
| |
| unsafe fn rsgx_thread_cond_wait(cond: &mut sgx_thread_cond_t, mutex: &mut sgx_thread_mutex_t) -> SysError { |
| |
| let ret = sgx_types::sgx_thread_cond_wait(raw_cond(cond), mutex::raw_mutex(mutex)); |
| if ret == 0 { Ok(()) } else { Err(ret) } |
| } |
| |
| unsafe fn rsgx_thread_cond_signal(cond: &mut sgx_thread_cond_t) -> SysError { |
| |
| let ret = sgx_types::sgx_thread_cond_signal(raw_cond(cond)); |
| if ret == 0 { Ok(()) } else { Err(ret) } |
| } |
| |
| unsafe fn rsgx_thread_cond_broadcast(cond: &mut sgx_thread_cond_t) -> SysError { |
| |
| let ret = sgx_types::sgx_thread_cond_broadcast(raw_cond(cond)); |
| if ret == 0 { Ok(()) } else { Err(ret) } |
| } |
| |
| /// The structure of sgx condition. |
| pub struct SgxThreadCondvar { |
| cond: UnsafeCell<sgx_thread_cond_t>, |
| } |
| |
| unsafe impl Send for SgxThreadCondvar {} |
| unsafe impl Sync for SgxThreadCondvar {} |
| |
| impl SgxThreadCondvar { |
| /// |
| /// The function initializes a trusted condition variable within the enclave. |
| /// |
| /// # Description |
| /// |
| /// When a thread creates a condition variable within an enclave, it simply initializes the various |
| /// fields of the object to indicate that the condition variable is available. The results of using |
| /// a condition variable in a wait, signal or broadcast operation before it has been fully initialized |
| /// are undefined. To avoid race conditions in the initialization of a condition variable, it is |
| /// recommended statically initializing the condition variable with the macro SGX_THREAD_COND_INITIALIZER. |
| /// |
| /// # Requirements |
| /// |
| /// Library: libsgx_tstdc.a |
| /// |
| pub const fn new() -> Self { |
| SgxThreadCondvar{ cond: UnsafeCell::new(sgx_types::SGX_THREAD_COND_INITIALIZER) } |
| } |
| |
| /// |
| /// The function waits on a condition variable within an enclave. |
| /// |
| /// # Description |
| /// |
| /// A condition variable is always used in conjunction with a mutex. To wait on a |
| /// condition variable, a thread first needs to acquire the condition variable spin |
| /// lock. After the spin lock is acquired, the thread updates the condition variable |
| /// waiting queue. To avoid the lost wake-up signal problem, the condition variable |
| /// spin lock is released after the mutex. This order ensures the function atomically |
| /// releases the mutex and causes the calling thread to block on the condition variable, |
| /// with respect to other threads accessing the mutex and the condition variable. |
| /// After releasing the condition variable spin lock, the thread makes an OCALL to |
| /// get suspended. When the thread is awakened, it acquires the condition variable |
| /// spin lock. The thread then searches the condition variable queue. If the thread |
| /// is in the queue, it means that the thread was already waiting on the condition |
| /// variable outside the enclave, and it has been awakened unexpectedly. When this |
| /// happens, the thread releases the condition variable spin lock, makes an OCALL |
| /// and simply goes back to sleep. Otherwise, another thread has signaled or broadcasted |
| /// the condition variable and this thread may proceed. Before returning, the thread |
| /// releases the condition variable spin lock and acquires the mutex, ensuring that |
| /// upon returning from the function call the thread still owns the mutex. |
| /// |
| /// # Requirements |
| /// |
| /// Library: libsgx_tstdc.a |
| /// |
| /// # Parameters |
| /// |
| /// **mutex** |
| /// |
| /// The trusted mutex object that will be unlocked when the thread is blocked inthe condition variable |
| /// |
| /// # Errors |
| /// |
| /// **EINVAL** |
| /// |
| /// The trusted condition variable or mutex object is invalid or the mutex is not locked. |
| /// |
| /// **EPERM** |
| /// |
| /// The trusted mutex is locked by another thread. |
| /// |
| #[inline] |
| pub unsafe fn wait(&self, mutex: &SgxThreadMutex) -> SysError { |
| rsgx_thread_cond_wait(&mut *self.cond.get(), mutex.get_raw()) |
| } |
| |
| /// |
| /// The function wakes a pending thread waiting on the condition variable. |
| /// |
| /// # Description |
| /// |
| /// To signal a condition variable, a thread starts acquiring the condition variable |
| /// spin-lock. Then it inspects the status of the condition variable queue. If the |
| /// queue is empty it means that there are not any threads waiting on the condition |
| /// variable. When that happens, the thread releases the condition variable and returns. |
| /// However, if the queue is not empty, the thread removes the first thread waiting |
| /// in the queue. The thread then makes an OCALL to wake up the thread that is suspended |
| /// outside the enclave, but first the thread releases the condition variable spin-lock. |
| /// Upon returning from the OCALL, the thread continues normal execution. |
| /// |
| /// # Requirements |
| /// |
| /// Library: libsgx_tstdc.a |
| /// |
| /// # Errors |
| /// |
| /// **EINVAL** |
| /// |
| /// The trusted condition variable is invalid. |
| /// |
| #[inline] |
| pub unsafe fn signal(&self) -> SysError { |
| rsgx_thread_cond_signal(&mut *self.cond.get()) |
| } |
| |
| /// |
| /// The function wakes all pending threads waiting on the condition variable. |
| /// |
| /// # Description |
| /// |
| /// Broadcast and signal operations on a condition variable are analogous. The |
| /// only difference is that during a broadcast operation, the thread removes all |
| /// the threads waiting on the condition variable queue and wakes up all the |
| /// threads suspended outside the enclave in a single OCALL. |
| /// |
| /// # Requirements |
| /// |
| /// Library: libsgx_tstdc.a |
| /// |
| /// # Errors |
| /// |
| /// **EINVAL** |
| /// |
| /// The trusted condition variable is invalid. |
| /// |
| /// **ENOMEM** |
| /// |
| /// Internal memory allocation failed. |
| /// |
| #[inline] |
| pub unsafe fn broadcast(&self) -> SysError { |
| rsgx_thread_cond_broadcast(&mut *self.cond.get()) |
| } |
| |
| /// |
| /// The function destroys a trusted condition variable within an enclave. |
| /// |
| /// # Description |
| /// |
| /// The procedure first confirms that there are no threads waiting on the condition |
| /// variable before it is destroyed. The destroy operation acquires the spin lock at |
| /// the beginning of the operation to prevent other threads from signaling to or |
| /// waiting on the condition variable. |
| /// |
| /// # Requirements |
| /// |
| /// Library: libsgx_tstdc.a |
| /// |
| /// # Errors |
| /// |
| /// **EINVAL** |
| /// |
| /// The trusted condition variable is invalid. |
| /// |
| /// **EBUSY** |
| /// |
| /// The condition variable has pending threads waiting on it. |
| /// |
| #[inline] |
| pub unsafe fn destroy(&self) -> SysError { |
| rsgx_thread_cond_destroy(&mut *self.cond.get()) |
| } |
| |
| /// Get the pointer of sgx_thread_cond_t in SgxThreadCondvar. |
| #[allow(dead_code)] |
| #[inline] |
| pub unsafe fn get_raw(&self) -> &mut sgx_thread_cond_t { |
| &mut *self.cond.get() |
| } |
| } |
| |
| /// 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 and |
| /// are bindings to system-provided condition variables where possible. Note |
| /// that this module places one additional restriction over the system condition |
| /// variables: each condvar can be used with precisely one mutex at runtime. Any |
| /// attempt to use multiple mutexes on the same condition variable will result |
| /// in a runtime panic. If this is not desired, then the unsafe primitives in |
| /// `sys` do not have this restriction but may result in undefined behavior. |
| /// |
| pub struct SgxCondvar { |
| inner: Box<SgxThreadCondvar>, |
| mutex: AtomicUsize, |
| } |
| |
| impl SgxCondvar { |
| /// |
| /// Creates a new condition variable which is ready to be waited on and notified. |
| /// |
| pub fn new() -> Self { |
| SgxCondvar { |
| inner: Box::new(SgxThreadCondvar::new()), |
| mutex: AtomicUsize::new(0), |
| } |
| } |
| |
| /// 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 [`signal`] or [`broadcast`] 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 [`SgxMutex`] type. |
| /// |
| /// # Panics |
| /// |
| /// This function will [`panic!`] if it is used with more than one mutex |
| /// over time. Each condition variable is dynamically bound to exactly one |
| /// mutex to ensure defined behavior across platforms. If this functionality |
| /// is not desired, then unsafe primitives in `sys` are provided. |
| pub fn wait<'a, T>(&self, guard: SgxMutexGuard<'a, T>) -> LockResult<SgxMutexGuard<'a, T>> { |
| |
| let poisoned = unsafe { |
| let lock = mutex::guard_lock(&guard); |
| self.verify(lock); |
| 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 required condition is met. Spurious wakeups are |
| /// ignored and this function will only return once the condition has been |
| /// met. |
| /// |
| /// This function will atomically unlock the mutex specified (represented by |
| /// `guard`) and block the current thread. This means that any calls |
| /// to [`signal`] or [`broadcast`] 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. |
| /// |
| pub fn wait_until<'a, T, F>(&self, mut guard: SgxMutexGuard<'a, T>, |
| mut condition: F) |
| -> LockResult<SgxMutexGuard<'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 `signal` are not buffered |
| /// in any way. |
| /// |
| /// To wake up all threads, see [`broadcast`]. |
| pub fn signal(&self) { |
| unsafe { self.inner.signal(); } |
| } |
| |
| /// Wakes up all blocked threads on this condvar. |
| /// |
| /// This method will ensure that any current waiters on the condition |
| /// variable are awoken. Calls to `broadcast()` are not buffered in any |
| /// way. |
| /// |
| /// To wake up only one thread, see [`signal`]. |
| pub fn broadcast(&self) { |
| |
| unsafe { |
| let ret = self.inner.broadcast(); |
| match ret { |
| Err(r) if r == libc::ENOMEM => { |
| //let _layout = Layout::from_size_align(mem::size_of::<usize>(), 1).unwrap(); |
| sgx_trts::oom::rsgx_oom(AllocErr) |
| }, |
| _ => {}, |
| } |
| } |
| } |
| |
| fn verify(&self, mutex: &SgxThreadMutex) { |
| |
| let addr = mutex as *const _ as usize; |
| match self.mutex.compare_and_swap(0, addr, Ordering::SeqCst) { |
| // If we got out 0, then we have successfully bound the mutex to |
| // this cvar. |
| 0 => {}, |
| // If we get out a value that's the same as `addr`, then someone |
| // already beat us to the punch. |
| n if n == addr => {}, |
| // Anything else and we're using more than one mutex on this cvar, |
| // which is currently disallowed. |
| _ => panic!("attempted to use a condition variable with two mutexes."), |
| } |
| } |
| } |
| |
| impl fmt::Debug for SgxCondvar { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| f.pad("Condvar { .. }") |
| } |
| } |
| |
| impl Default for SgxCondvar { |
| /// Creates a `Condvar` which is ready to be waited on and notified. |
| fn default() -> Self { |
| SgxCondvar::new() |
| } |
| } |
| |
| impl Drop for SgxCondvar { |
| fn drop(&mut self) { |
| unsafe { self.inner.destroy(); } |
| } |
| } |