diff options
Diffstat (limited to 'library/std/src/sys/thread/sgx.rs')
| -rw-r--r-- | library/std/src/sys/thread/sgx.rs | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/library/std/src/sys/thread/sgx.rs b/library/std/src/sys/thread/sgx.rs new file mode 100644 index 00000000000..1f613badcd7 --- /dev/null +++ b/library/std/src/sys/thread/sgx.rs @@ -0,0 +1,158 @@ +#![cfg_attr(test, allow(dead_code))] // why is this necessary? + +use super::abi::{thread, usercalls}; +use super::unsupported; +use crate::ffi::CStr; +use crate::io; +use crate::num::NonZero; +use crate::time::{Duration, Instant}; + +pub struct Thread(task_queue::JoinHandle); + +pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; + +pub use self::task_queue::JoinNotifier; + +mod task_queue { + use super::wait_notify; + use crate::sync::{Mutex, MutexGuard}; + + pub type JoinHandle = wait_notify::Waiter; + + pub struct JoinNotifier(Option<wait_notify::Notifier>); + + impl Drop for JoinNotifier { + fn drop(&mut self) { + self.0.take().unwrap().notify(); + } + } + + pub(super) struct Task { + p: Box<dyn FnOnce() + Send>, + done: JoinNotifier, + } + + impl Task { + pub(super) fn new(p: Box<dyn FnOnce() + Send>) -> (Task, JoinHandle) { + let (done, recv) = wait_notify::new(); + let done = JoinNotifier(Some(done)); + (Task { p, done }, recv) + } + + pub(super) fn run(self) -> JoinNotifier { + (self.p)(); + self.done + } + } + + // Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests + #[cfg_attr(test, linkage = "available_externally")] + #[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx6thread10TASK_QUEUEE")] + static TASK_QUEUE: Mutex<Vec<Task>> = Mutex::new(Vec::new()); + + pub(super) fn lock() -> MutexGuard<'static, Vec<Task>> { + TASK_QUEUE.lock().unwrap() + } +} + +/// This module provides a synchronization primitive that does not use thread +/// local variables. This is needed for signaling that a thread has finished +/// execution. The signal is sent once all TLS destructors have finished at +/// which point no new thread locals should be created. +pub mod wait_notify { + use crate::pin::Pin; + use crate::sync::Arc; + use crate::sys::sync::Parker; + + pub struct Notifier(Arc<Parker>); + + impl Notifier { + /// Notify the waiter. The waiter is either notified right away (if + /// currently blocked in `Waiter::wait()`) or later when it calls the + /// `Waiter::wait()` method. + pub fn notify(self) { + Pin::new(&*self.0).unpark() + } + } + + pub struct Waiter(Arc<Parker>); + + impl Waiter { + /// Wait for a notification. If `Notifier::notify()` has already been + /// called, this will return immediately, otherwise the current thread + /// is blocked until notified. + pub fn wait(self) { + // SAFETY: + // This is only ever called on one thread. + unsafe { Pin::new(&*self.0).park() } + } + } + + pub fn new() -> (Notifier, Waiter) { + let inner = Arc::new(Parker::new()); + (Notifier(inner.clone()), Waiter(inner)) + } +} + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + p: Box<dyn FnOnce() + Send>, + ) -> io::Result<Thread> { + let mut queue_lock = task_queue::lock(); + unsafe { usercalls::launch_thread()? }; + let (task, handle) = task_queue::Task::new(p); + queue_lock.push(task); + Ok(Thread(handle)) + } + + pub(super) fn entry() -> JoinNotifier { + let mut pending_tasks = task_queue::lock(); + let task = rtunwrap!(Some, pending_tasks.pop()); + drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary + task.run() + } + + pub fn yield_now() { + let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO)); + rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock); + } + + /// SGX should protect in-enclave data from the outside (attacker), + /// so there should be no data leakage to the OS, + /// and therefore also no 1-1 mapping between SGX thread names and OS thread names. + /// + /// This is why the method is intentionally No-Op. + pub fn set_name(_name: &CStr) { + // Note that the internally visible SGX thread name is already provided + // by the platform-agnostic (target-agnostic) Rust thread code. + // This can be observed in the [`std::thread::tests::test_named_thread`] test, + // which succeeds as-is with the SGX target. + } + + pub fn sleep(dur: Duration) { + usercalls::wait_timeout(0, dur, || true); + } + + pub fn sleep_until(deadline: Instant) { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + Self::sleep(delay); + } + } + + pub fn join(self) { + self.0.wait(); + } +} + +pub(crate) fn current_os_id() -> Option<u64> { + Some(thread::current().addr().get() as u64) +} + +pub fn available_parallelism() -> io::Result<NonZero<usize>> { + unsupported() +} |
