Skip to content

Commit

Permalink
Add wasm32-wasi-threads target + WASI threads
Browse files Browse the repository at this point in the history
  • Loading branch information
g0djan committed Jul 29, 2023
1 parent b63c4ab commit 0358fa1
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 12 deletions.
2 changes: 1 addition & 1 deletion std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public
[target.'cfg(target_os = "hermit")'.dependencies]
hermit-abi = { version = "0.3.2", features = ['rustc-dep-of-std'], public = true }

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

[features]
Expand Down
22 changes: 17 additions & 5 deletions std/src/sys/wasi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ pub mod fs;
#[path = "../wasm/atomics/futex.rs"]
pub mod futex;
pub mod io;
#[path = "../unsupported/locks/mod.rs"]
pub mod locks;

pub mod net;
pub mod os;
#[path = "../unix/os_str.rs"]
Expand All @@ -47,14 +46,27 @@ pub mod thread;
pub mod thread_local_dtor;
#[path = "../unsupported/thread_local_key.rs"]
pub mod thread_local_key;
#[path = "../unsupported/thread_parking.rs"]
pub mod thread_parking;
pub mod time;

cfg_if::cfg_if! {
if #[cfg(not(target_feature = "atomics"))] {
if #[cfg(target_feature = "atomics")] {
#[path = "../unix/locks"]
pub mod locks {
#![allow(unsafe_op_in_unsafe_fn)]
mod futex_condvar;
mod futex_mutex;
mod futex_rwlock;
pub(crate) use futex_condvar::Condvar;
pub(crate) use futex_mutex::Mutex;
pub(crate) use futex_rwlock::RwLock;
}
} else {
#[path = "../unsupported/locks/mod.rs"]
pub mod locks;
#[path = "../unsupported/once.rs"]
pub mod once;
#[path = "../unsupported/thread_parking.rs"]
pub mod thread_parking;
}
}

Expand Down
5 changes: 5 additions & 0 deletions std/src/sys/wasi/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> {
})
}

#[allow(dead_code)]
pub fn page_size() -> usize {
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
}

pub fn temp_dir() -> PathBuf {
panic!("no filesystem on wasm")
}
Expand Down
124 changes: 118 additions & 6 deletions std/src/sys/wasi/thread.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,120 @@
#![deny(unsafe_op_in_unsafe_fn)]

use crate::ffi::CStr;
use crate::io;
use crate::mem;
use crate::num::NonZeroUsize;
use crate::sys::unsupported;
use crate::time::Duration;

pub struct Thread(!);
cfg_if::cfg_if! {
if #[cfg(target_feature = "atomics")] {
use crate::cmp;
use crate::ptr;
use crate::sys::os;
// Add a few symbols not in upstream `libc` just yet.
mod libc {
pub use crate::ffi;
pub use crate::mem;
pub use libc::*;

// defined in wasi-libc
// https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108
#[repr(C)]
union pthread_attr_union {
__i: [ffi::c_int; if mem::size_of::<ffi::c_int>() == 8 { 14 } else { 9 }],
__vi: [ffi::c_int; if mem::size_of::<ffi::c_int>() == 8 { 14 } else { 9 }],
__s: [ffi::c_ulong; if mem::size_of::<ffi::c_int>() == 8 { 7 } else { 9 }],
}

#[repr(C)]
pub struct pthread_attr_t {
__u: pthread_attr_union,
}

#[allow(non_camel_case_types)]
pub type pthread_t = *mut ffi::c_void;

extern "C" {
pub fn pthread_create(
native: *mut pthread_t,
attr: *const pthread_attr_t,
f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void,
value: *mut ffi::c_void,
) -> ffi::c_int;
pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int;
pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int;
pub fn pthread_attr_setstacksize(
attr: *mut pthread_attr_t,
stack_size: libc::size_t,
) -> ffi::c_int;
pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int;
}
}

pub struct Thread {
id: libc::pthread_t,
}
} else {
pub struct Thread(!);
}
}

pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;

impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
unsupported()
cfg_if::cfg_if! {
if #[cfg(target_feature = "atomics")] {
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
let p = Box::into_raw(Box::new(p));
let mut native: libc::pthread_t = mem::zeroed();
let mut attr: libc::pthread_attr_t = mem::zeroed();
assert_eq!(libc::pthread_attr_init(&mut attr), 0);

let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE);

match libc::pthread_attr_setstacksize(&mut attr, stack_size) {
0 => {}
n => {
assert_eq!(n, libc::EINVAL);
// EINVAL means |stack_size| is either too small or not a
// multiple of the system page size. Because it's definitely
// >= PTHREAD_STACK_MIN, it must be an alignment issue.
// Round up to the nearest page and try again.
let page_size = os::page_size();
let stack_size =
(stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1);
assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0);
}
};

let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _);
// Note: if the thread creation fails and this assert fails, then p will
// be leaked. However, an alternative design could cause double-free
// which is clearly worse.
assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);

return if ret != 0 {
// The thread failed to start and as a result p was not consumed. Therefore, it is
// safe to reconstruct the box so that it gets deallocated.
drop(Box::from_raw(p));
Err(io::Error::from_raw_os_error(ret))
} else {
Ok(Thread { id: native })
};

extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
unsafe {
// Finally, let's run some code.
Box::from_raw(main as *mut Box<dyn FnOnce()>)();
}
ptr::null_mut()
}
}
} else {
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
unsupported()
}
}
}

pub fn yield_now() {
Expand Down Expand Up @@ -62,7 +162,19 @@ impl Thread {
}

pub fn join(self) {
self.0
cfg_if::cfg_if! {
if #[cfg(target_feature = "atomics")] {
unsafe {
let ret = libc::pthread_join(self.id, ptr::null_mut());
mem::forget(self);
if ret != 0 {
rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret));
}
}
} else {
self.0
}
}
}
}

Expand Down

0 comments on commit 0358fa1

Please sign in to comment.