force_alloc_system = []
panic-unwind = ["panic_unwind"]
profiler = ["profiler_builtins"]
+
+# An off-by-default feature which enables a linux-syscall-like ABI for libstd to
+# interoperate with the host environment. Currently not well documented and
+# requires rebuilding the standard library to use it.
wasm_syscall = []
+
+# An off-by-default features to enable libstd to assume that wasm-bindgen is in
+# the environment for hooking up some thread-related information like the
+# current thread id and accessing/getting the current thread's TCB
+wasm-bindgen-threads = []
use arch::wasm32::atomic;
use cell::UnsafeCell;
use mem;
-use sync::atomic::{AtomicUsize, AtomicU64, Ordering::SeqCst};
+use sync::atomic::{AtomicUsize, AtomicU32, Ordering::SeqCst};
+use sys::thread;
pub struct Mutex {
locked: AtomicUsize,
}
pub struct ReentrantMutex {
- owner: AtomicU64,
+ owner: AtomicU32,
recursions: UnsafeCell<u32>,
}
impl ReentrantMutex {
pub unsafe fn uninitialized() -> ReentrantMutex {
ReentrantMutex {
- owner: AtomicU64::new(0),
+ owner: AtomicU32::new(0),
recursions: UnsafeCell::new(0),
}
}
}
pub unsafe fn lock(&self) {
- let me = thread_id();
+ let me = thread::my_id();
while let Err(owner) = self._try_lock(me) {
- let val = atomic::wait_i64(self.ptr(), owner as i64, -1);
+ let val = atomic::wait_i32(self.ptr(), owner as i32, -1);
debug_assert!(val == 0 || val == 1);
}
}
#[inline]
pub unsafe fn try_lock(&self) -> bool {
- self._try_lock(thread_id()).is_ok()
+ self._try_lock(thread::my_id()).is_ok()
}
#[inline]
- unsafe fn _try_lock(&self, id: u64) -> Result<(), u64> {
+ unsafe fn _try_lock(&self, id: u32) -> Result<(), u32> {
let id = id.checked_add(1).unwrap(); // make sure `id` isn't 0
match self.owner.compare_exchange(0, id, SeqCst, SeqCst) {
// we transitioned from unlocked to locked
}
#[inline]
- fn ptr(&self) -> *mut i64 {
- &self.owner as *const AtomicU64 as *mut i64
+ fn ptr(&self) -> *mut i32 {
+ &self.owner as *const AtomicU32 as *mut i32
}
}
-
-fn thread_id() -> u64 {
- panic!("thread ids not implemented on wasm with atomics yet")
-}
pub unsafe fn init() -> Option<Guard> { None }
pub unsafe fn deinit() {}
}
+
+cfg_if! {
+ if #[cfg(all(target_feature = "atomics", feature = "wasm-bindgen-threads"))] {
+ #[link(wasm_import_module = "__wbindgen_thread_xform__")]
+ extern {
+ fn __wbindgen_current_id() -> u32;
+ fn __wbindgen_tcb_get() -> u32;
+ fn __wbindgen_tcb_set(ptr: u32);
+ }
+ pub fn my_id() -> u32 {
+ unsafe { __wbindgen_current_id() }
+ }
+
+ // These are currently only ever used in `thread_local_atomics.rs`, if
+ // you'd like to use them be sure to update that and make sure everyone
+ // agrees what's what.
+ pub fn tcb_get() -> *mut u8 {
+ use mem;
+ assert_eq!(mem::size_of::<*mut u8>(), mem::size_of::<u32>());
+ unsafe { __wbindgen_tcb_get() as *mut u8 }
+ }
+
+ pub fn tcb_set(ptr: *mut u8) {
+ unsafe { __wbindgen_tcb_set(ptr as u32); }
+ }
+
+ // FIXME: still need something for hooking exiting a thread to free
+ // data...
+
+ } else if #[cfg(target_feature = "atomics")] {
+ pub fn my_id() -> u32 {
+ panic!("thread ids not implemented on wasm with atomics yet")
+ }
+
+ pub fn tcb_get() -> *mut u8 {
+ panic!("thread local data not implemented on wasm with atomics yet")
+ }
+
+ pub fn tcb_set(ptr: *mut u8) {
+ panic!("thread local data not implemented on wasm with atomics yet")
+ }
+ } else {
+ // stubbed out because no functions actually access these intrinsics
+ // unless atomics are enabled
+ }
+}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use sys::thread;
+use sync::atomic::{AtomicUsize, Ordering::SeqCst};
+
+const MAX_KEYS: usize = 128;
+static NEXT_KEY: AtomicUsize = AtomicUsize::new(0);
+
+struct ThreadControlBlock {
+ keys: [*mut u8; MAX_KEYS],
+}
+
+impl ThreadControlBlock {
+ fn new() -> ThreadControlBlock {
+ ThreadControlBlock {
+ keys: [0 as *mut u8; MAX_KEYS],
+ }
+ }
+
+ fn get() -> *mut ThreadControlBlock {
+ let ptr = thread::tcb_get();
+ if !ptr.is_null() {
+ return ptr as *mut ThreadControlBlock
+ }
+ let tcb = Box::into_raw(Box::new(ThreadControlBlock::new()));
+ thread::tcb_set(tcb as *mut u8);
+ tcb
+ }
+}
+
pub type Key = usize;
-pub unsafe fn create(_dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
- panic!("TLS on wasm with atomics not implemented yet");
+pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
+ drop(dtor); // FIXME: need to figure out how to hook thread exit to run this
+ let key = NEXT_KEY.fetch_add(1, SeqCst);
+ if key >= MAX_KEYS {
+ NEXT_KEY.store(MAX_KEYS, SeqCst);
+ panic!("cannot allocate space for more TLS keys");
+ }
+ // offset by 1 so we never hand out 0. This is currently required by
+ // `sys_common/thread_local.rs` where it can't cope with keys of value 0
+ // because it messes up the atomic management.
+ return key + 1
}
-pub unsafe fn set(_key: Key, _value: *mut u8) {
- panic!("TLS on wasm with atomics not implemented yet");
+pub unsafe fn set(key: Key, value: *mut u8) {
+ (*ThreadControlBlock::get()).keys[key - 1] = value;
}
-pub unsafe fn get(_key: Key) -> *mut u8 {
- panic!("TLS on wasm with atomics not implemented yet");
+pub unsafe fn get(key: Key) -> *mut u8 {
+ (*ThreadControlBlock::get()).keys[key - 1]
}
pub unsafe fn destroy(_key: Key) {
- panic!("TLS on wasm with atomics not implemented yet");
+ // FIXME: should implement this somehow, this isn't typically called but it
+ // can be called if two threads race to initialize a TLS slot and one ends
+ // up not being needed.
}
#[inline]
&'static $crate::cell::UnsafeCell<
$crate::option::Option<$t>>>
{
- #[cfg(target_arch = "wasm32")]
+ #[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))]
static __KEY: $crate::thread::__StaticLocalKeyInner<$t> =
$crate::thread::__StaticLocalKeyInner::new();
#[thread_local]
- #[cfg(all(target_thread_local, not(target_arch = "wasm32")))]
+ #[cfg(all(
+ target_thread_local,
+ not(all(target_arch = "wasm32", not(target_feature = "atomics"))),
+ ))]
static __KEY: $crate::thread::__FastLocalKeyInner<$t> =
$crate::thread::__FastLocalKeyInner::new();
- #[cfg(all(not(target_thread_local), not(target_arch = "wasm32")))]
+ #[cfg(all(
+ not(target_thread_local),
+ not(all(target_arch = "wasm32", not(target_feature = "atomics"))),
+ ))]
static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
$crate::thread::__OsLocalKeyInner::new();
/// On some platforms like wasm32 there's no threads, so no need to generate
/// thread locals and we can instead just use plain statics!
#[doc(hidden)]
-#[cfg(target_arch = "wasm32")]
+#[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))]
pub mod statik {
use cell::UnsafeCell;
use fmt;
// where available, but both are needed.
#[unstable(feature = "libstd_thread_internals", issue = "0")]
-#[cfg(target_arch = "wasm32")]
+#[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))]
#[doc(hidden)] pub use self::local::statik::Key as __StaticLocalKeyInner;
#[unstable(feature = "libstd_thread_internals", issue = "0")]
#[cfg(target_thread_local)]