From 951576051bafe03cd6ea36707e4ed8b25355fb58 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 11 Nov 2020 22:25:48 +0000 Subject: [PATCH 1/2] Update thread and futex APIs to work with Emscripten This updates the thread and futex APIs in `std` to match the APIs exposed by Emscripten. This allows threads to run on `wasm32-unknown-emscripten` and the thread parker to compile without errors related to the missing `futex` module. To make use of this, Rust code must be compiled with `-C target-feature=atomics` and Emscripten must link with `-pthread`. I have confirmed this works well locally when building multithreaded crates. Attempting to enable `std` thread tests currently fails for seemingly obscure reasons and Emscripten is currently disabled in CI, so further work is needed to have proper test coverage here. --- library/std/src/sys/unix/futex.rs | 43 +++++++++++++++++++++++++++++- library/std/src/sys/unix/thread.rs | 20 +------------- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs index e6f0c48c59b1b..7ec3794d0b051 100644 --- a/library/std/src/sys/unix/futex.rs +++ b/library/std/src/sys/unix/futex.rs @@ -1,10 +1,17 @@ -#![cfg(any(target_os = "linux", target_os = "android"))] +#![cfg(any( + target_os = "linux", + target_os = "android", + all(target_os = "emscripten", target_feature = "atomics") +))] +#[cfg(any(target_os = "linux", target_os = "android"))] use crate::convert::TryInto; +#[cfg(any(target_os = "linux", target_os = "android"))] use crate::ptr::null; use crate::sync::atomic::AtomicI32; use crate::time::Duration; +#[cfg(any(target_os = "linux", target_os = "android"))] pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option) { let timespec = timeout.and_then(|d| { Some(libc::timespec { @@ -25,6 +32,29 @@ pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option) { } } +#[cfg(target_os = "emscripten")] +pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option) { + extern "C" { + fn emscripten_futex_wait( + addr: *const AtomicI32, + val: libc::c_uint, + max_wait_ms: libc::c_double, + ) -> libc::c_int; + } + + let timeout_ms = timeout.map(|d| d.as_millis()); + unsafe { + emscripten_futex_wait( + futex as *const AtomicI32, + // `val` is declared unsigned to match the Emscripten headers, but since it's used as + // an opaque value, we can ignore the meaning of signed vs. unsigned and cast here. + expected as libc::c_uint, + timeout_ms.map_or(crate::f64::INFINITY, |d| d as libc::c_double), + ); + } +} + +#[cfg(any(target_os = "linux", target_os = "android"))] pub fn futex_wake(futex: &AtomicI32) { unsafe { libc::syscall( @@ -35,3 +65,14 @@ pub fn futex_wake(futex: &AtomicI32) { ); } } + +#[cfg(target_os = "emscripten")] +pub fn futex_wake(futex: &AtomicI32) { + extern "C" { + fn emscripten_futex_wake(addr: *const AtomicI32, count: libc::c_int) -> libc::c_int; + } + + unsafe { + emscripten_futex_wake(futex as *const AtomicI32, 1); + } +} diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index fdf114d6df6fe..b7b72cc392fd3 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -22,24 +22,6 @@ pub struct Thread { unsafe impl Send for Thread {} unsafe impl Sync for Thread {} -// The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc, -// so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS. -#[cfg(not(target_os = "emscripten"))] -unsafe fn pthread_attr_setstacksize( - attr: *mut libc::pthread_attr_t, - stack_size: libc::size_t, -) -> libc::c_int { - libc::pthread_attr_setstacksize(attr, stack_size) -} - -#[cfg(target_os = "emscripten")] -unsafe fn pthread_attr_setstacksize( - _attr: *mut libc::pthread_attr_t, - _stack_size: libc::size_t, -) -> libc::c_int { - panic!() -} - impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements pub unsafe fn new(stack: usize, p: Box) -> io::Result { @@ -50,7 +32,7 @@ impl Thread { let stack_size = cmp::max(stack, min_stack_size(&attr)); - match pthread_attr_setstacksize(&mut attr, stack_size) { + match libc::pthread_attr_setstacksize(&mut attr, stack_size) { 0 => {} n => { assert_eq!(n, libc::EINVAL); From bf3be09ee800de4ec98dfeea5088639e1c538813 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 12 Nov 2020 03:40:15 +0000 Subject: [PATCH 2/2] Fix timeout conversion --- library/std/src/sys/unix/futex.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs index 7ec3794d0b051..42ddc1d514ec7 100644 --- a/library/std/src/sys/unix/futex.rs +++ b/library/std/src/sys/unix/futex.rs @@ -42,14 +42,13 @@ pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option) { ) -> libc::c_int; } - let timeout_ms = timeout.map(|d| d.as_millis()); unsafe { emscripten_futex_wait( futex as *const AtomicI32, // `val` is declared unsigned to match the Emscripten headers, but since it's used as // an opaque value, we can ignore the meaning of signed vs. unsigned and cast here. expected as libc::c_uint, - timeout_ms.map_or(crate::f64::INFINITY, |d| d as libc::c_double), + timeout.map_or(crate::f64::INFINITY, |d| d.as_secs_f64() * 1000.0), ); } }