Skip to content

Commit 0358fa1

Browse files
committed
Add wasm32-wasi-threads target + WASI threads
1 parent b63c4ab commit 0358fa1

File tree

4 files changed

+141
-12
lines changed

4 files changed

+141
-12
lines changed

std/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public
4747
[target.'cfg(target_os = "hermit")'.dependencies]
4848
hermit-abi = { version = "0.3.2", features = ['rustc-dep-of-std'], public = true }
4949

50-
[target.wasm32-wasi.dependencies]
50+
[target.'cfg(target_os = "wasi")'.dependencies]
5151
wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false }
5252

5353
[features]

std/src/sys/wasi/mod.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ pub mod fs;
2929
#[path = "../wasm/atomics/futex.rs"]
3030
pub mod futex;
3131
pub mod io;
32-
#[path = "../unsupported/locks/mod.rs"]
33-
pub mod locks;
32+
3433
pub mod net;
3534
pub mod os;
3635
#[path = "../unix/os_str.rs"]
@@ -47,14 +46,27 @@ pub mod thread;
4746
pub mod thread_local_dtor;
4847
#[path = "../unsupported/thread_local_key.rs"]
4948
pub mod thread_local_key;
50-
#[path = "../unsupported/thread_parking.rs"]
51-
pub mod thread_parking;
5249
pub mod time;
5350

5451
cfg_if::cfg_if! {
55-
if #[cfg(not(target_feature = "atomics"))] {
52+
if #[cfg(target_feature = "atomics")] {
53+
#[path = "../unix/locks"]
54+
pub mod locks {
55+
#![allow(unsafe_op_in_unsafe_fn)]
56+
mod futex_condvar;
57+
mod futex_mutex;
58+
mod futex_rwlock;
59+
pub(crate) use futex_condvar::Condvar;
60+
pub(crate) use futex_mutex::Mutex;
61+
pub(crate) use futex_rwlock::RwLock;
62+
}
63+
} else {
64+
#[path = "../unsupported/locks/mod.rs"]
65+
pub mod locks;
5666
#[path = "../unsupported/once.rs"]
5767
pub mod once;
68+
#[path = "../unsupported/thread_parking.rs"]
69+
pub mod thread_parking;
5870
}
5971
}
6072

std/src/sys/wasi/os.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,11 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> {
224224
})
225225
}
226226

227+
#[allow(dead_code)]
228+
pub fn page_size() -> usize {
229+
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
230+
}
231+
227232
pub fn temp_dir() -> PathBuf {
228233
panic!("no filesystem on wasm")
229234
}

std/src/sys/wasi/thread.rs

Lines changed: 118 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,120 @@
1-
#![deny(unsafe_op_in_unsafe_fn)]
2-
31
use crate::ffi::CStr;
42
use crate::io;
53
use crate::mem;
64
use crate::num::NonZeroUsize;
75
use crate::sys::unsupported;
86
use crate::time::Duration;
97

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+
}
1160

1261
pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
1362

1463
impl Thread {
1564
// 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+
}
18118
}
19119

20120
pub fn yield_now() {
@@ -62,7 +162,19 @@ impl Thread {
62162
}
63163

64164
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+
}
66178
}
67179
}
68180

0 commit comments

Comments
 (0)