xref: /drstd/src/std/sys/wasi/thread.rs (revision 0fe3ff0054d3aec7fbf9bddecfecb10bc7d23a51)
1 use crate::std::ffi::CStr;
2 use crate::std::io;
3 use crate::std::mem;
4 use crate::std::num::NonZeroUsize;
5 use crate::std::sys::unsupported;
6 use crate::std::time::Duration;
7 use dlibc;
8 
9 cfg_if::cfg_if! {
10     if #[cfg(target_feature = "atomics")] {
11         use crate::std::cmp;
12         use crate::std::ptr;
13         use crate::std::sys::os;
14         // Add a few symbols not in upstream `libc` just yet.
15         mod libc {
16             pub use crate::std::ffi;
17             pub use crate::std::mem;
18             pub use dlibc::*;
19 
20             // defined in wasi-libc
21             // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108
22             #[repr(C)]
23             union pthread_attr_union {
24                 __i: [ffi::c_int; if mem::size_of::<ffi::c_long>() == 8 { 14 } else { 9 }],
25                 __vi: [ffi::c_int; if mem::size_of::<ffi::c_long>() == 8 { 14 } else { 9 }],
26                 __s: [ffi::c_ulong; if mem::size_of::<ffi::c_long>() == 8 { 7 } else { 9 }],
27             }
28 
29             #[repr(C)]
30             pub struct pthread_attr_t {
31                 __u: pthread_attr_union,
32             }
33 
34             #[allow(non_camel_case_types)]
35             pub type pthread_t = *mut ffi::c_void;
36 
37             extern "C" {
38                 pub fn pthread_create(
39                     native: *mut pthread_t,
40                     attr: *const pthread_attr_t,
41                     f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void,
42                     value: *mut ffi::c_void,
43                 ) -> ffi::c_int;
44                 pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int;
45                 pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int;
46                 pub fn pthread_attr_setstacksize(
47                     attr: *mut pthread_attr_t,
48                     stack_size: dlibc::size_t,
49                 ) -> ffi::c_int;
50                 pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int;
51                 pub fn pthread_detach(thread: pthread_t) -> ffi::c_int;
52             }
53         }
54 
55         pub struct Thread {
56             id: dlibc::pthread_t,
57         }
58 
59         impl Drop for Thread {
60             fn drop(&mut self) {
61                 let ret = unsafe { dlibc::pthread_detach(self.id) };
62                 debug_assert_eq!(ret, 0);
63             }
64         }
65     } else {
66         pub struct Thread(!);
67     }
68 }
69 
70 pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
71 
72 impl Thread {
73     // unsafe: see thread::Builder::spawn_unchecked for safety requirements
74     cfg_if::cfg_if! {
75         if #[cfg(target_feature = "atomics")] {
76             pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
77                 let p = Box::into_raw(Box::new(p));
78                 let mut native: dlibc::pthread_t = mem::zeroed();
79                 let mut attr: dlibc::pthread_attr_t = mem::zeroed();
80                 assert_eq!(dlibc::pthread_attr_init(&mut attr), 0);
81 
82                 let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE);
83 
84                 match dlibc::pthread_attr_setstacksize(&mut attr, stack_size) {
85                     0 => {}
86                     n => {
87                         assert_eq!(n, dlibc::EINVAL);
88                         // EINVAL means |stack_size| is either too small or not a
89                         // multiple of the system page size. Because it's definitely
90                         // >= PTHREAD_STACK_MIN, it must be an alignment issue.
91                         // Round up to the nearest page and try again.
92                         let page_size = os::page_size();
93                         let stack_size =
94                             (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1);
95                         assert_eq!(dlibc::pthread_attr_setstacksize(&mut attr, stack_size), 0);
96                     }
97                 };
98 
99                 let ret = dlibc::pthread_create(&mut native, &attr, thread_start, p as *mut _);
100                 // Note: if the thread creation fails and this assert fails, then p will
101                 // be leaked. However, an alternative design could cause double-free
102                 // which is clearly worse.
103                 assert_eq!(dlibc::pthread_attr_destroy(&mut attr), 0);
104 
105                 return if ret != 0 {
106                     // The thread failed to start and as a result p was not consumed. Therefore, it is
107                     // safe to reconstruct the box so that it gets deallocated.
108                     drop(Box::from_raw(p));
109                     Err(io::Error::from_raw_os_error(ret))
110                 } else {
111                     Ok(Thread { id: native })
112                 };
113 
114                 extern "C" fn thread_start(main: *mut dlibc::c_void) -> *mut dlibc::c_void {
115                     unsafe {
116                         // Finally, let's run some code.
117                         Box::from_raw(main as *mut Box<dyn FnOnce()>)();
118                     }
119                     ptr::null_mut()
120                 }
121             }
122         } else {
123             pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
124                 unsupported()
125             }
126         }
127     }
128 
129     pub fn yield_now() {
130         let ret = unsafe { wasi::sched_yield() };
131         debug_assert_eq!(ret, Ok(()));
132     }
133 
134     pub fn set_name(_name: &CStr) {
135         // nope
136     }
137 
138     pub fn sleep(dur: Duration) {
139         let nanos = dur.as_nanos();
140         assert!(nanos <= u64::MAX as u128);
141 
142         const USERDATA: wasi::Userdata = 0x0123_45678;
143 
144         let clock = wasi::SubscriptionClock {
145             id: wasi::CLOCKID_MONOTONIC,
146             timeout: nanos as u64,
147             precision: 0,
148             flags: 0,
149         };
150 
151         let in_ = wasi::Subscription {
152             userdata: USERDATA,
153             u: wasi::SubscriptionU {
154                 tag: 0,
155                 u: wasi::SubscriptionUU { clock },
156             },
157         };
158         unsafe {
159             let mut event: wasi::Event = mem::zeroed();
160             let res = wasi::poll_oneoff(&in_, &mut event, 1);
161             match (res, event) {
162                 (
163                     Ok(1),
164                     wasi::Event {
165                         userdata: USERDATA,
166                         error: wasi::ERRNO_SUCCESS,
167                         type_: wasi::EVENTTYPE_CLOCK,
168                         ..
169                     },
170                 ) => {}
171                 _ => panic!("thread::sleep(): unexpected result of poll_oneoff"),
172             }
173         }
174     }
175 
176     pub fn join(self) {
177         cfg_if::cfg_if! {
178             if #[cfg(target_feature = "atomics")] {
179                 unsafe {
180                     let ret = dlibc::pthread_join(self.id, ptr::null_mut());
181                     mem::forget(self);
182                     if ret != 0 {
183                         rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret));
184                     }
185                 }
186             } else {
187                 self.0
188             }
189         }
190     }
191 }
192 
193 pub fn available_parallelism() -> io::Result<NonZeroUsize> {
194     unsupported()
195 }
196 
197 pub mod guard {
198     pub type Guard = !;
199     pub unsafe fn current() -> Option<Guard> {
200         None
201     }
202     pub unsafe fn init() -> Option<Guard> {
203         None
204     }
205 }
206