| #![cfg_attr(test, allow(dead_code))] // why is this necessary? | |
| use crate::io; | |
| use crate::sys::pal::abi::{thread, usercalls}; | |
| use crate::thread::ThreadInit; | |
| use crate::time::Duration; | |
| 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}; | |
| use crate::thread::ThreadInit; | |
| 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 { | |
| init: Box<ThreadInit>, | |
| done: JoinNotifier, | |
| } | |
| impl Task { | |
| pub(super) fn new(init: Box<ThreadInit>) -> (Task, JoinHandle) { | |
| let (done, recv) = wait_notify::new(); | |
| let done = JoinNotifier(Some(done)); | |
| (Task { init, done }, recv) | |
| } | |
| pub(super) fn run(self) -> JoinNotifier { | |
| let rust_start = self.init.init(); | |
| rust_start(); | |
| 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, init: Box<ThreadInit>) -> io::Result<Thread> { | |
| let mut queue_lock = task_queue::lock(); | |
| unsafe { usercalls::launch_thread()? }; | |
| let (task, handle) = task_queue::Task::new(init); | |
| queue_lock.push(task); | |
| Ok(Thread(handle)) | |
| } | |
| pub(crate) 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 join(self) { | |
| self.0.wait(); | |
| } | |
| } | |
| pub fn current_os_id() -> Option<u64> { | |
| Some(thread::current().addr().get() as u64) | |
| } | |
| pub fn sleep(dur: Duration) { | |
| usercalls::wait_timeout(0, dur, || true); | |
| } | |
| pub fn yield_now() { | |
| let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO)); | |
| rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock); | |
| } |