origin_studio/
thread.rs

1//! Native threads.
2
3use crate::boxed::Box;
4use crate::io;
5use core::mem::forget;
6use core::num::NonZeroUsize;
7use core::ptr::{null_mut, NonNull};
8
9// Rust does't need the OS tids, it just needs unique ids, so we just use the
10// raw `Thread` value casted to `usize`.
11#[allow(dead_code)] // TODO: obviate this
12pub struct ThreadId(usize);
13
14pub struct Thread(origin::thread::Thread);
15
16impl Thread {
17    pub fn id(&self) -> ThreadId {
18        ThreadId(self.0.to_raw().addr())
19    }
20}
21
22pub struct JoinHandle(Thread);
23
24impl JoinHandle {
25    pub fn join(self) -> io::Result<()> {
26        unsafe {
27            origin::thread::join(self.0 .0);
28        }
29
30        // Don't call drop, which would detach the thread we just joined.
31        forget(self);
32
33        Ok(())
34    }
35}
36
37impl Drop for JoinHandle {
38    fn drop(&mut self) {
39        unsafe {
40            origin::thread::detach(self.0 .0);
41        }
42    }
43}
44
45pub fn spawn<F>(f: F) -> JoinHandle
46where
47    F: FnOnce() + Send + 'static,
48{
49    // Pack up the closure.
50    let boxed: Box<dyn FnOnce() + Send + 'static> = Box::new(move || {
51        #[cfg(feature = "stack-overflow")]
52        let _handler = unsafe { crate::stack_overflow::Handler::new() };
53
54        f()
55    });
56
57    // We could avoid double boxing by enabling the unstable `ptr_metadata`
58    // feature, using `.to_raw_parts()` on the box pointer, though it does
59    // also require transmuting the metadata into `*mut c_void` and back.
60    /*
61    let raw: *mut (dyn FnOnce() + Send + 'static) = Box::into_raw(boxed);
62    let (callee, metadata) = raw.to_raw_parts();
63    let args = [
64        NonNull::new(callee as _),
65        NonNull::new(unsafe { transmute(metadata) }),
66    ];
67    */
68    let boxed = Box::new(boxed);
69    let raw: *mut Box<dyn FnOnce() + Send + 'static> = Box::into_raw(boxed);
70    let args = [NonNull::new(raw.cast())];
71
72    let thread = unsafe {
73        let r = origin::thread::create(
74            move |args| {
75                // Unpack and call.
76                /*
77                let (callee, metadata) = (args[0], args[1]);
78                let raw: *mut (dyn FnOnce() + Send + 'static) =
79                    ptr::from_raw_parts_mut(transmute(callee), transmute(metadata));
80                let boxed = Box::from_raw(raw);
81                boxed();
82                */
83                let raw: *mut Box<dyn FnOnce() + Send + 'static> = match args[0] {
84                    Some(raw) => raw.as_ptr().cast(),
85                    None => null_mut(),
86                };
87                let boxed: Box<Box<dyn FnOnce() + Send + 'static>> = Box::from_raw(raw);
88                (*boxed)();
89
90                None
91            },
92            &args,
93            origin::thread::default_stack_size(),
94            origin::thread::default_guard_size(),
95        );
96        r.unwrap()
97    };
98
99    JoinHandle(Thread(thread))
100}
101
102pub fn current() -> Thread {
103    Thread(origin::thread::current())
104}
105
106pub(crate) struct GetThreadId;
107
108unsafe impl rustix_futex_sync::lock_api::GetThreadId for GetThreadId {
109    const INIT: Self = Self;
110
111    fn nonzero_thread_id(&self) -> NonZeroUsize {
112        origin::thread::current().to_raw_non_null().addr()
113    }
114}
115
116pub(crate) type ReentrantMutex<T> = rustix_futex_sync::ReentrantMutex<GetThreadId, T>;
117pub(crate) type ReentrantMutexGuard<'a, T> =
118    rustix_futex_sync::ReentrantMutexGuard<'a, GetThreadId, T>;