|
1 |
| -#![deny(unsafe_op_in_unsafe_fn)] |
2 |
| - |
3 | 1 | use crate::ffi::CStr;
|
4 | 2 | use crate::io;
|
5 | 3 | use crate::mem;
|
6 | 4 | use crate::num::NonZeroUsize;
|
7 | 5 | use crate::sys::unsupported;
|
8 | 6 | use crate::time::Duration;
|
9 | 7 |
|
10 |
| -pub struct Thread(!); |
| 8 | +cfg_if::cfg_if! { |
| 9 | + if #[cfg(target_feature = "atomics")] { |
| 10 | + use crate::cmp; |
| 11 | + use crate::ptr; |
| 12 | + use crate::sys::os; |
| 13 | + // Add a few symbols not in upstream `libc` just yet. |
| 14 | + mod libc { |
| 15 | + pub use crate::ffi; |
| 16 | + pub use crate::mem; |
| 17 | + pub use libc::*; |
| 18 | + |
| 19 | + // defined in wasi-libc |
| 20 | + // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108 |
| 21 | + #[repr(C)] |
| 22 | + union pthread_attr_union { |
| 23 | + __i: [ffi::c_int; if mem::size_of::<ffi::c_int>() == 8 { 14 } else { 9 }], |
| 24 | + __vi: [ffi::c_int; if mem::size_of::<ffi::c_int>() == 8 { 14 } else { 9 }], |
| 25 | + __s: [ffi::c_ulong; if mem::size_of::<ffi::c_int>() == 8 { 7 } else { 9 }], |
| 26 | + } |
| 27 | + |
| 28 | + #[repr(C)] |
| 29 | + pub struct pthread_attr_t { |
| 30 | + __u: pthread_attr_union, |
| 31 | + } |
| 32 | + |
| 33 | + #[allow(non_camel_case_types)] |
| 34 | + pub type pthread_t = *mut ffi::c_void; |
| 35 | + |
| 36 | + extern "C" { |
| 37 | + pub fn pthread_create( |
| 38 | + native: *mut pthread_t, |
| 39 | + attr: *const pthread_attr_t, |
| 40 | + f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void, |
| 41 | + value: *mut ffi::c_void, |
| 42 | + ) -> ffi::c_int; |
| 43 | + pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int; |
| 44 | + pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int; |
| 45 | + pub fn pthread_attr_setstacksize( |
| 46 | + attr: *mut pthread_attr_t, |
| 47 | + stack_size: libc::size_t, |
| 48 | + ) -> ffi::c_int; |
| 49 | + pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int; |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + pub struct Thread { |
| 54 | + id: libc::pthread_t, |
| 55 | + } |
| 56 | + } else { |
| 57 | + pub struct Thread(!); |
| 58 | + } |
| 59 | +} |
11 | 60 |
|
12 | 61 | pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
|
13 | 62 |
|
14 | 63 | impl Thread {
|
15 | 64 | // unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
16 |
| - pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> { |
17 |
| - unsupported() |
| 65 | + cfg_if::cfg_if! { |
| 66 | + if #[cfg(target_feature = "atomics")] { |
| 67 | + pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> { |
| 68 | + let p = Box::into_raw(Box::new(p)); |
| 69 | + let mut native: libc::pthread_t = mem::zeroed(); |
| 70 | + let mut attr: libc::pthread_attr_t = mem::zeroed(); |
| 71 | + assert_eq!(libc::pthread_attr_init(&mut attr), 0); |
| 72 | + |
| 73 | + let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE); |
| 74 | + |
| 75 | + match libc::pthread_attr_setstacksize(&mut attr, stack_size) { |
| 76 | + 0 => {} |
| 77 | + n => { |
| 78 | + assert_eq!(n, libc::EINVAL); |
| 79 | + // EINVAL means |stack_size| is either too small or not a |
| 80 | + // multiple of the system page size. Because it's definitely |
| 81 | + // >= PTHREAD_STACK_MIN, it must be an alignment issue. |
| 82 | + // Round up to the nearest page and try again. |
| 83 | + let page_size = os::page_size(); |
| 84 | + let stack_size = |
| 85 | + (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); |
| 86 | + assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0); |
| 87 | + } |
| 88 | + }; |
| 89 | + |
| 90 | + let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _); |
| 91 | + // Note: if the thread creation fails and this assert fails, then p will |
| 92 | + // be leaked. However, an alternative design could cause double-free |
| 93 | + // which is clearly worse. |
| 94 | + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); |
| 95 | + |
| 96 | + return if ret != 0 { |
| 97 | + // The thread failed to start and as a result p was not consumed. Therefore, it is |
| 98 | + // safe to reconstruct the box so that it gets deallocated. |
| 99 | + drop(Box::from_raw(p)); |
| 100 | + Err(io::Error::from_raw_os_error(ret)) |
| 101 | + } else { |
| 102 | + Ok(Thread { id: native }) |
| 103 | + }; |
| 104 | + |
| 105 | + extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { |
| 106 | + unsafe { |
| 107 | + // Finally, let's run some code. |
| 108 | + Box::from_raw(main as *mut Box<dyn FnOnce()>)(); |
| 109 | + } |
| 110 | + ptr::null_mut() |
| 111 | + } |
| 112 | + } |
| 113 | + } else { |
| 114 | + pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> { |
| 115 | + unsupported() |
| 116 | + } |
| 117 | + } |
18 | 118 | }
|
19 | 119 |
|
20 | 120 | pub fn yield_now() {
|
@@ -62,7 +162,19 @@ impl Thread {
|
62 | 162 | }
|
63 | 163 |
|
64 | 164 | pub fn join(self) {
|
65 |
| - self.0 |
| 165 | + cfg_if::cfg_if! { |
| 166 | + if #[cfg(target_feature = "atomics")] { |
| 167 | + unsafe { |
| 168 | + let ret = libc::pthread_join(self.id, ptr::null_mut()); |
| 169 | + mem::forget(self); |
| 170 | + if ret != 0 { |
| 171 | + rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); |
| 172 | + } |
| 173 | + } |
| 174 | + } else { |
| 175 | + self.0 |
| 176 | + } |
| 177 | + } |
66 | 178 | }
|
67 | 179 | }
|
68 | 180 |
|
|
0 commit comments