1 #![cfg_attr(test, allow(dead_code))] // why is this necessary? 2 use super::unsupported; 3 use crate::std::ffi::CStr; 4 use crate::std::io; 5 use crate::std::num::NonZeroUsize; 6 use crate::std::time::Duration; 7 8 use super::abi::usercalls; 9 10 pub struct Thread(task_queue::JoinHandle); 11 12 pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; 13 14 pub use self::task_queue::JoinNotifier; 15 16 mod task_queue { 17 use super::wait_notify; 18 use crate::std::sync::{Mutex, MutexGuard, Once}; 19 20 pub type JoinHandle = wait_notify::Waiter; 21 22 pub struct JoinNotifier(Option<wait_notify::Notifier>); 23 24 impl Drop for JoinNotifier { 25 fn drop(&mut self) { 26 self.0.take().unwrap().notify(); 27 } 28 } 29 30 pub(super) struct Task { 31 p: Box<dyn FnOnce()>, 32 done: JoinNotifier, 33 } 34 35 impl Task { 36 pub(super) fn new(p: Box<dyn FnOnce()>) -> (Task, JoinHandle) { 37 let (done, recv) = wait_notify::new(); 38 let done = JoinNotifier(Some(done)); 39 (Task { p, done }, recv) 40 } 41 42 pub(super) fn run(self) -> JoinNotifier { 43 (self.p)(); 44 self.done 45 } 46 } 47 48 #[cfg_attr(test, linkage = "available_externally")] 49 #[export_name = "_ZN16__rust_internals3std3sys3sgx6thread15TASK_QUEUE_INITE"] 50 static TASK_QUEUE_INIT: Once = Once::new(); 51 #[cfg_attr(test, linkage = "available_externally")] 52 #[export_name = "_ZN16__rust_internals3std3sys3sgx6thread10TASK_QUEUEE"] 53 static mut TASK_QUEUE: Option<Mutex<Vec<Task>>> = None; 54 55 pub(super) fn lock() -> MutexGuard<'static, Vec<Task>> { 56 unsafe { 57 TASK_QUEUE_INIT.call_once(|| TASK_QUEUE = Some(Default::default())); 58 TASK_QUEUE.as_ref().unwrap().lock().unwrap() 59 } 60 } 61 } 62 63 /// This module provides a synchronization primitive that does not use thread 64 /// local variables. This is needed for signaling that a thread has finished 65 /// execution. The signal is sent once all TLS destructors have finished at 66 /// which point no new thread locals should be created. 67 pub mod wait_notify { 68 use crate::std::pin::Pin; 69 use crate::std::sync::Arc; 70 use crate::std::sys_common::thread_parking::Parker; 71 72 pub struct Notifier(Arc<Parker>); 73 74 impl Notifier { 75 /// Notify the waiter. The waiter is either notified right away (if 76 /// currently blocked in `Waiter::wait()`) or later when it calls the 77 /// `Waiter::wait()` method. 78 pub fn notify(self) { 79 Pin::new(&*self.0).unpark() 80 } 81 } 82 83 pub struct Waiter(Arc<Parker>); 84 85 impl Waiter { 86 /// Wait for a notification. If `Notifier::notify()` has already been 87 /// called, this will return immediately, otherwise the current thread 88 /// is blocked until notified. 89 pub fn wait(self) { 90 // SAFETY: 91 // This is only ever called on one thread. 92 unsafe { Pin::new(&*self.0).park() } 93 } 94 } 95 96 pub fn new() -> (Notifier, Waiter) { 97 let inner = Arc::new(Parker::new()); 98 (Notifier(inner.clone()), Waiter(inner)) 99 } 100 } 101 102 impl Thread { 103 // unsafe: see thread::Builder::spawn_unchecked for safety requirements 104 pub unsafe fn new(_stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> { 105 let mut queue_lock = task_queue::lock(); 106 unsafe { usercalls::launch_thread()? }; 107 let (task, handle) = task_queue::Task::new(p); 108 queue_lock.push(task); 109 Ok(Thread(handle)) 110 } 111 112 pub(super) fn entry() -> JoinNotifier { 113 let mut pending_tasks = task_queue::lock(); 114 let task = rtunwrap!(Some, pending_tasks.pop()); 115 drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary 116 task.run() 117 } 118 119 pub fn yield_now() { 120 let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO)); 121 rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock); 122 } 123 124 /// SGX should protect in-enclave data from the outside (attacker), 125 /// so there should be no data leakage to the OS, 126 /// and therefore also no 1-1 mapping between SGX thread names and OS thread names. 127 /// 128 /// This is why the method is intentionally No-Op. 129 pub fn set_name(_name: &CStr) { 130 // Note that the internally visible SGX thread name is already provided 131 // by the platform-agnostic (target-agnostic) Rust thread code. 132 // This can be observed in the [`std::thread::tests::test_named_thread`] test, 133 // which succeeds as-is with the SGX target. 134 } 135 136 pub fn sleep(dur: Duration) { 137 usercalls::wait_timeout(0, dur, || true); 138 } 139 140 pub fn join(self) { 141 self.0.wait(); 142 } 143 } 144 145 pub fn available_parallelism() -> io::Result<NonZeroUsize> { 146 unsupported() 147 } 148 149 pub mod guard { 150 pub type Guard = !; 151 pub unsafe fn current() -> Option<Guard> { 152 None 153 } 154 pub unsafe fn init() -> Option<Guard> { 155 None 156 } 157 } 158