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