diff options
| author | bors <bors@rust-lang.org> | 2020-07-27 17:39:01 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2020-07-27 17:39:01 +0000 |
| commit | 54e000891ffccd4cbfb92146b92736c83085df63 (patch) | |
| tree | 1200bb13eb9ae22def4c43bc657bc56da8faedc6 /src/libstd/sys/wasm | |
| parent | 4a90e36c85336d1d4b209556c1a9733210bbff19 (diff) | |
| parent | 6d9705220fec4553d693a7c19d99496e14c89edf (diff) | |
| download | rust-tmp-nightly.tar.gz | |
Auto merge of #73265 - mark-i-m:mv-std, r=<try>tmp-nightly
mv std libs to library/
This is the first step in refactoring the directory layout of this repository, with further followup steps planned (but not done yet).
Background: currently, all crates are under src/, without nested src directories and with the unconventional `lib*` prefixes (e.g., `src/libcore/lib.rs`). This directory structures is not idiomatic and makes the `src/` directory rather overwhelming. To improve contributor experience and make things a bit more approachable, we are reorganizing the repo a bit.
In this PR, we move the standard libs (basically anything that is "runtime", as opposed to part of the compiler, build system, or one of the tools, etc). The new layout moves these libraries to a new `library/` directory in the root of the repo. Additionally, we remove the `lib*` prefixes and add nested `src/` directories. The other crates/tools in this repo are not touched. So in summary:
```
library/<crate>/src/*.rs
src/<all the rest> // unchanged
```
where `<crate>` is:
- core
- alloc
- std
- test
- proc_macro
- panic_abort
- panic_unwind
- profiler_builtins
- term
- unwind
- rtstartup
- backtrace
- rustc-std-workspace-*
There was a lot of discussion about this and a few rounds of compiler team approvals, FCPs, MCPs, and nominations. The original MCP is https://github.com/rust-lang/compiler-team/issues/298. The final approval of the compiler team was given here: https://github.com/rust-lang/rust/pull/73265#issuecomment-659498446.
The name `library` was chosen to complement a later move of the compiler crates to a `compiler/` directory. There was a lot of discussion around adding the nested `src/` directories. Note that this does increase the nesting depth (plausibly important for manual traversal of the tree, e.g., through GitHub's UI or `cd`), but this is deemed to be better as it fits the standard layout of Rust crates throughout most of the ecosystem, though there is some debate about how much this should apply to multi-crate projects. Overall, there seem to be more people in favor of nested `src/` than against.
After this PR, there are no dependencies out of the `library/` directory except on the `build_helper` (or crates.io crates).
Diffstat (limited to 'src/libstd/sys/wasm')
| -rw-r--r-- | src/libstd/sys/wasm/alloc.rs | 158 | ||||
| -rw-r--r-- | src/libstd/sys/wasm/args.rs | 46 | ||||
| -rw-r--r-- | src/libstd/sys/wasm/condvar_atomics.rs | 94 | ||||
| -rw-r--r-- | src/libstd/sys/wasm/env.rs | 9 | ||||
| -rw-r--r-- | src/libstd/sys/wasm/mod.rs | 70 | ||||
| -rw-r--r-- | src/libstd/sys/wasm/mutex_atomics.rs | 147 | ||||
| -rw-r--r-- | src/libstd/sys/wasm/rwlock_atomics.rs | 144 | ||||
| -rw-r--r-- | src/libstd/sys/wasm/thread.rs | 98 |
8 files changed, 0 insertions, 766 deletions
diff --git a/src/libstd/sys/wasm/alloc.rs b/src/libstd/sys/wasm/alloc.rs deleted file mode 100644 index 32b8b5bdaea..00000000000 --- a/src/libstd/sys/wasm/alloc.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! This is an implementation of a global allocator on the wasm32 platform when -//! emscripten is not in use. In that situation there's no actual runtime for us -//! to lean on for allocation, so instead we provide our own! -//! -//! The wasm32 instruction set has two instructions for getting the current -//! amount of memory and growing the amount of memory. These instructions are the -//! foundation on which we're able to build an allocator, so we do so! Note that -//! the instructions are also pretty "global" and this is the "global" allocator -//! after all! -//! -//! The current allocator here is the `dlmalloc` crate which we've got included -//! in the rust-lang/rust repository as a submodule. The crate is a port of -//! dlmalloc.c from C to Rust and is basically just so we can have "pure Rust" -//! for now which is currently technically required (can't link with C yet). -//! -//! The crate itself provides a global allocator which on wasm has no -//! synchronization as there are no threads! - -use crate::alloc::{GlobalAlloc, Layout, System}; - -static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::DLMALLOC_INIT; - -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let _lock = lock::lock(); - DLMALLOC.malloc(layout.size(), layout.align()) - } - - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - let _lock = lock::lock(); - DLMALLOC.calloc(layout.size(), layout.align()) - } - - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - let _lock = lock::lock(); - DLMALLOC.free(ptr, layout.size(), layout.align()) - } - - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - let _lock = lock::lock(); - DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) - } -} - -#[cfg(target_feature = "atomics")] -mod lock { - use crate::sync::atomic::{AtomicI32, Ordering::SeqCst}; - - static LOCKED: AtomicI32 = AtomicI32::new(0); - - pub struct DropLock; - - pub fn lock() -> DropLock { - loop { - if LOCKED.swap(1, SeqCst) == 0 { - return DropLock; - } - // Ok so here's where things get a little depressing. At this point - // in time we need to synchronously acquire a lock, but we're - // contending with some other thread. Typically we'd execute some - // form of `i32.atomic.wait` like so: - // - // unsafe { - // let r = core::arch::wasm32::i32_atomic_wait( - // LOCKED.as_mut_ptr(), - // 1, // expected value - // -1, // timeout - // ); - // debug_assert!(r == 0 || r == 1); - // } - // - // Unfortunately though in doing so we would cause issues for the - // main thread. The main thread in a web browser *cannot ever - // block*, no exceptions. This means that the main thread can't - // actually execute the `i32.atomic.wait` instruction. - // - // As a result if we want to work within the context of browsers we - // need to figure out some sort of allocation scheme for the main - // thread where when there's contention on the global malloc lock we - // do... something. - // - // Possible ideas include: - // - // 1. Attempt to acquire the global lock. If it fails, fall back to - // memory allocation via `memory.grow`. Later just ... somehow - // ... inject this raw page back into the main allocator as it - // gets sliced up over time. This strategy has the downside of - // forcing allocation of a page to happen whenever the main - // thread contents with other threads, which is unfortunate. - // - // 2. Maintain a form of "two level" allocator scheme where the main - // thread has its own allocator. Somehow this allocator would - // also be balanced with a global allocator, not only to have - // allocations cross between threads but also to ensure that the - // two allocators stay "balanced" in terms of free'd memory and - // such. This, however, seems significantly complicated. - // - // Out of a lack of other ideas, the current strategy implemented - // here is to simply spin. Typical spin loop algorithms have some - // form of "hint" here to the CPU that it's what we're doing to - // ensure that the CPU doesn't get too hot, but wasm doesn't have - // such an instruction. - // - // To be clear, spinning here is not a great solution. - // Another thread with the lock may take quite a long time to wake - // up. For example it could be in `memory.grow` or it could be - // evicted from the CPU for a timeslice like 10ms. For these periods - // of time our thread will "helpfully" sit here and eat CPU time - // until it itself is evicted or the lock holder finishes. This - // means we're just burning and wasting CPU time to no one's - // benefit. - // - // Spinning does have the nice properties, though, of being - // semantically correct, being fair to all threads for memory - // allocation, and being simple enough to implement. - // - // This will surely (hopefully) be replaced in the future with a - // real memory allocator that can handle the restriction of the main - // thread. - // - // - // FIXME: We can also possibly add an optimization here to detect - // when a thread is the main thread or not and block on all - // non-main-thread threads. Currently, however, we have no way - // of knowing which wasm thread is on the browser main thread, but - // if we could figure out we could at least somewhat mitigate the - // cost of this spinning. - } - } - - impl Drop for DropLock { - fn drop(&mut self) { - let r = LOCKED.swap(0, SeqCst); - debug_assert_eq!(r, 1); - - // Note that due to the above logic we don't actually need to wake - // anyone up, but if we did it'd likely look something like this: - // - // unsafe { - // core::arch::wasm32::atomic_notify( - // LOCKED.as_mut_ptr(), - // 1, // only one thread - // ); - // } - } - } -} - -#[cfg(not(target_feature = "atomics"))] -mod lock { - #[inline] - pub fn lock() {} // no atomics, no threads, that's easy! -} diff --git a/src/libstd/sys/wasm/args.rs b/src/libstd/sys/wasm/args.rs deleted file mode 100644 index 3b6557ae325..00000000000 --- a/src/libstd/sys/wasm/args.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::ffi::OsString; -use crate::marker::PhantomData; -use crate::vec; - -pub unsafe fn init(_argc: isize, _argv: *const *const u8) { - // On wasm these should always be null, so there's nothing for us to do here -} - -pub unsafe fn cleanup() {} - -pub fn args() -> Args { - Args { iter: Vec::new().into_iter(), _dont_send_or_sync_me: PhantomData } -} - -pub struct Args { - iter: vec::IntoIter<OsString>, - _dont_send_or_sync_me: PhantomData<*mut ()>, -} - -impl Args { - pub fn inner_debug(&self) -> &[OsString] { - self.iter.as_slice() - } -} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option<OsString> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option<OsString> { - self.iter.next_back() - } -} diff --git a/src/libstd/sys/wasm/condvar_atomics.rs b/src/libstd/sys/wasm/condvar_atomics.rs deleted file mode 100644 index 1859cdd5a0e..00000000000 --- a/src/libstd/sys/wasm/condvar_atomics.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::arch::wasm32; -use crate::cmp; -use crate::mem; -use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; -use crate::sys::mutex::Mutex; -use crate::time::Duration; - -pub struct Condvar { - cnt: AtomicUsize, -} - -// Condition variables are implemented with a simple counter internally that is -// likely to cause spurious wakeups. Blocking on a condition variable will first -// read the value of the internal counter, unlock the given mutex, and then -// block if and only if the counter's value is still the same. Notifying a -// condition variable will modify the counter (add one for now) and then wake up -// a thread waiting on the address of the counter. -// -// A thread waiting on the condition variable will as a result avoid going to -// sleep if it's notified after the lock is unlocked but before it fully goes to -// sleep. A sleeping thread is guaranteed to be woken up at some point as it can -// only be woken up with a call to `wake`. -// -// Note that it's possible for 2 or more threads to be woken up by a call to -// `notify_one` with this implementation. That can happen where the modification -// of `cnt` causes any threads in the middle of `wait` to avoid going to sleep, -// and the subsequent `wake` may wake up a thread that's actually blocking. We -// consider this a spurious wakeup, though, which all users of condition -// variables must already be prepared to handle. As a result, this source of -// spurious wakeups is currently though to be ok, although it may be problematic -// later on if it causes too many spurious wakeups. - -impl Condvar { - pub const fn new() -> Condvar { - Condvar { cnt: AtomicUsize::new(0) } - } - - #[inline] - pub unsafe fn init(&mut self) { - // nothing to do - } - - pub unsafe fn notify_one(&self) { - self.cnt.fetch_add(1, SeqCst); - wasm32::atomic_notify(self.ptr(), 1); - } - - #[inline] - pub unsafe fn notify_all(&self) { - self.cnt.fetch_add(1, SeqCst); - wasm32::atomic_notify(self.ptr(), u32::MAX); // -1 == "wake everyone" - } - - pub unsafe fn wait(&self, mutex: &Mutex) { - // "atomically block and unlock" implemented by loading our current - // counter's value, unlocking the mutex, and blocking if the counter - // still has the same value. - // - // Notifications happen by incrementing the counter and then waking a - // thread. Incrementing the counter after we unlock the mutex will - // prevent us from sleeping and otherwise the call to `wake` will - // wake us up once we're asleep. - let ticket = self.cnt.load(SeqCst) as i32; - mutex.unlock(); - let val = wasm32::i32_atomic_wait(self.ptr(), ticket, -1); - // 0 == woken, 1 == not equal to `ticket`, 2 == timeout (shouldn't happen) - debug_assert!(val == 0 || val == 1); - mutex.lock(); - } - - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - let ticket = self.cnt.load(SeqCst) as i32; - mutex.unlock(); - let nanos = dur.as_nanos(); - let nanos = cmp::min(i64::MAX as u128, nanos); - - // If the return value is 2 then a timeout happened, so we return - // `false` as we weren't actually notified. - let ret = wasm32::i32_atomic_wait(self.ptr(), ticket, nanos as i64) != 2; - mutex.lock(); - return ret; - } - - #[inline] - pub unsafe fn destroy(&self) { - // nothing to do - } - - #[inline] - fn ptr(&self) -> *mut i32 { - assert_eq!(mem::size_of::<usize>(), mem::size_of::<i32>()); - self.cnt.as_mut_ptr() as *mut i32 - } -} diff --git a/src/libstd/sys/wasm/env.rs b/src/libstd/sys/wasm/env.rs deleted file mode 100644 index 730e356d7fe..00000000000 --- a/src/libstd/sys/wasm/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod os { - pub const FAMILY: &str = ""; - pub const OS: &str = ""; - pub const DLL_PREFIX: &str = ""; - pub const DLL_SUFFIX: &str = ".wasm"; - pub const DLL_EXTENSION: &str = "wasm"; - pub const EXE_SUFFIX: &str = ".wasm"; - pub const EXE_EXTENSION: &str = "wasm"; -} diff --git a/src/libstd/sys/wasm/mod.rs b/src/libstd/sys/wasm/mod.rs deleted file mode 100644 index 3de58904043..00000000000 --- a/src/libstd/sys/wasm/mod.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! System bindings for the wasm/web platform -//! -//! This module contains the facade (aka platform-specific) implementations of -//! OS level functionality for wasm. Note that this wasm is *not* the emscripten -//! wasm, so we have no runtime here. -//! -//! This is all super highly experimental and not actually intended for -//! wide/production use yet, it's still all in the experimental category. This -//! will likely change over time. -//! -//! Currently all functions here are basically stubs that immediately return -//! errors. The hope is that with a portability lint we can turn actually just -//! remove all this and just omit parts of the standard library if we're -//! compiling for wasm. That way it's a compile time error for something that's -//! guaranteed to be a runtime error! - -pub mod alloc; -pub mod args; -#[path = "../unsupported/cmath.rs"] -pub mod cmath; -pub mod env; -#[path = "../unsupported/fs.rs"] -pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; -#[path = "../unsupported/os.rs"] -pub mod os; -#[path = "../unsupported/path.rs"] -pub mod path; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; -#[path = "../unsupported/process.rs"] -pub mod process; -#[path = "../unsupported/stack_overflow.rs"] -pub mod stack_overflow; -#[path = "../unsupported/stdio.rs"] -pub mod stdio; -pub mod thread; -#[path = "../unsupported/thread_local_dtor.rs"] -pub mod thread_local_dtor; -#[path = "../unsupported/thread_local_key.rs"] -pub mod thread_local_key; -#[path = "../unsupported/time.rs"] -pub mod time; - -pub use crate::sys_common::os_str_bytes as os_str; - -cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { - #[path = "condvar_atomics.rs"] - pub mod condvar; - #[path = "mutex_atomics.rs"] - pub mod mutex; - #[path = "rwlock_atomics.rs"] - pub mod rwlock; - } else { - #[path = "../unsupported/condvar.rs"] - pub mod condvar; - #[path = "../unsupported/mutex.rs"] - pub mod mutex; - #[path = "../unsupported/rwlock.rs"] - pub mod rwlock; - } -} - -#[path = "../unsupported/common.rs"] -mod common; -pub use common::*; diff --git a/src/libstd/sys/wasm/mutex_atomics.rs b/src/libstd/sys/wasm/mutex_atomics.rs deleted file mode 100644 index 268a53bb564..00000000000 --- a/src/libstd/sys/wasm/mutex_atomics.rs +++ /dev/null @@ -1,147 +0,0 @@ -use crate::arch::wasm32; -use crate::cell::UnsafeCell; -use crate::mem; -use crate::sync::atomic::{AtomicU32, AtomicUsize, Ordering::SeqCst}; -use crate::sys::thread; - -pub struct Mutex { - locked: AtomicUsize, -} - -// Mutexes have a pretty simple implementation where they contain an `i32` -// internally that is 0 when unlocked and 1 when the mutex is locked. -// Acquisition has a fast path where it attempts to cmpxchg the 0 to a 1, and -// if it fails it then waits for a notification. Releasing a lock is then done -// by swapping in 0 and then notifying any waiters, if present. - -impl Mutex { - pub const fn new() -> Mutex { - Mutex { locked: AtomicUsize::new(0) } - } - - #[inline] - pub unsafe fn init(&mut self) { - // nothing to do - } - - pub unsafe fn lock(&self) { - while !self.try_lock() { - let val = wasm32::i32_atomic_wait( - self.ptr(), - 1, // we expect our mutex is locked - -1, // wait infinitely - ); - // we should have either woke up (0) or got a not-equal due to a - // race (1). We should never time out (2) - debug_assert!(val == 0 || val == 1); - } - } - - pub unsafe fn unlock(&self) { - let prev = self.locked.swap(0, SeqCst); - debug_assert_eq!(prev, 1); - wasm32::atomic_notify(self.ptr(), 1); // wake up one waiter, if any - } - - #[inline] - pub unsafe fn try_lock(&self) -> bool { - self.locked.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() - } - - #[inline] - pub unsafe fn destroy(&self) { - // nothing to do - } - - #[inline] - fn ptr(&self) -> *mut i32 { - assert_eq!(mem::size_of::<usize>(), mem::size_of::<i32>()); - self.locked.as_mut_ptr() as *mut i32 - } -} - -pub struct ReentrantMutex { - owner: AtomicU32, - recursions: UnsafeCell<u32>, -} - -unsafe impl Send for ReentrantMutex {} -unsafe impl Sync for ReentrantMutex {} - -// Reentrant mutexes are similarly implemented to mutexs above except that -// instead of "1" meaning unlocked we use the id of a thread to represent -// whether it has locked a mutex. That way we have an atomic counter which -// always holds the id of the thread that currently holds the lock (or 0 if the -// lock is unlocked). -// -// Once a thread acquires a lock recursively, which it detects by looking at -// the value that's already there, it will update a local `recursions` counter -// in a nonatomic fashion (as we hold the lock). The lock is then fully -// released when this recursion counter reaches 0. - -impl ReentrantMutex { - pub const unsafe fn uninitialized() -> ReentrantMutex { - ReentrantMutex { owner: AtomicU32::new(0), recursions: UnsafeCell::new(0) } - } - - pub unsafe fn init(&self) { - // nothing to do... - } - - pub unsafe fn lock(&self) { - let me = thread::my_id(); - while let Err(owner) = self._try_lock(me) { - let val = wasm32::i32_atomic_wait(self.ptr(), owner as i32, -1); - debug_assert!(val == 0 || val == 1); - } - } - - #[inline] - pub unsafe fn try_lock(&self) -> bool { - self._try_lock(thread::my_id()).is_ok() - } - - #[inline] - 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 - Ok(_) => { - debug_assert_eq!(*self.recursions.get(), 0); - Ok(()) - } - - // we currently own this lock, so let's update our count and return - // true. - Err(n) if n == id => { - *self.recursions.get() += 1; - Ok(()) - } - - // Someone else owns the lock, let our caller take care of it - Err(other) => Err(other), - } - } - - pub unsafe fn unlock(&self) { - // If we didn't ever recursively lock the lock then we fully unlock the - // mutex and wake up a waiter, if any. Otherwise we decrement our - // recursive counter and let some one else take care of the zero. - match *self.recursions.get() { - 0 => { - self.owner.swap(0, SeqCst); - wasm32::atomic_notify(self.ptr() as *mut i32, 1); // wake up one waiter, if any - } - ref mut n => *n -= 1, - } - } - - pub unsafe fn destroy(&self) { - // nothing to do... - } - - #[inline] - fn ptr(&self) -> *mut i32 { - self.owner.as_mut_ptr() as *mut i32 - } -} diff --git a/src/libstd/sys/wasm/rwlock_atomics.rs b/src/libstd/sys/wasm/rwlock_atomics.rs deleted file mode 100644 index 06442e925f4..00000000000 --- a/src/libstd/sys/wasm/rwlock_atomics.rs +++ /dev/null @@ -1,144 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::sys::condvar::Condvar; -use crate::sys::mutex::Mutex; - -pub struct RWLock { - lock: Mutex, - cond: Condvar, - state: UnsafeCell<State>, -} - -enum State { - Unlocked, - Reading(usize), - Writing, -} - -unsafe impl Send for RWLock {} -unsafe impl Sync for RWLock {} - -// This rwlock implementation is a relatively simple implementation which has a -// condition variable for readers/writers as well as a mutex protecting the -// internal state of the lock. A current downside of the implementation is that -// unlocking the lock will notify *all* waiters rather than just readers or just -// writers. This can cause lots of "thundering stampede" problems. While -// hopefully correct this implementation is very likely to want to be changed in -// the future. - -impl RWLock { - pub const fn new() -> RWLock { - RWLock { lock: Mutex::new(), cond: Condvar::new(), state: UnsafeCell::new(State::Unlocked) } - } - - #[inline] - pub unsafe fn read(&self) { - self.lock.lock(); - while !(*self.state.get()).inc_readers() { - self.cond.wait(&self.lock); - } - self.lock.unlock(); - } - - #[inline] - pub unsafe fn try_read(&self) -> bool { - self.lock.lock(); - let ok = (*self.state.get()).inc_readers(); - self.lock.unlock(); - return ok; - } - - #[inline] - pub unsafe fn write(&self) { - self.lock.lock(); - while !(*self.state.get()).inc_writers() { - self.cond.wait(&self.lock); - } - self.lock.unlock(); - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - self.lock.lock(); - let ok = (*self.state.get()).inc_writers(); - self.lock.unlock(); - return ok; - } - - #[inline] - pub unsafe fn read_unlock(&self) { - self.lock.lock(); - let notify = (*self.state.get()).dec_readers(); - self.lock.unlock(); - if notify { - // FIXME: should only wake up one of these some of the time - self.cond.notify_all(); - } - } - - #[inline] - pub unsafe fn write_unlock(&self) { - self.lock.lock(); - (*self.state.get()).dec_writers(); - self.lock.unlock(); - // FIXME: should only wake up one of these some of the time - self.cond.notify_all(); - } - - #[inline] - pub unsafe fn destroy(&self) { - self.lock.destroy(); - self.cond.destroy(); - } -} - -impl State { - fn inc_readers(&mut self) -> bool { - match *self { - State::Unlocked => { - *self = State::Reading(1); - true - } - State::Reading(ref mut cnt) => { - *cnt += 1; - true - } - State::Writing => false, - } - } - - fn inc_writers(&mut self) -> bool { - match *self { - State::Unlocked => { - *self = State::Writing; - true - } - State::Reading(_) | State::Writing => false, - } - } - - fn dec_readers(&mut self) -> bool { - let zero = match *self { - State::Reading(ref mut cnt) => { - *cnt -= 1; - *cnt == 0 - } - State::Unlocked | State::Writing => invalid(), - }; - if zero { - *self = State::Unlocked; - } - zero - } - - fn dec_writers(&mut self) { - match *self { - State::Writing => {} - State::Unlocked | State::Reading(_) => invalid(), - } - *self = State::Unlocked; - } -} - -fn invalid() -> ! { - panic!("inconsistent rwlock"); -} diff --git a/src/libstd/sys/wasm/thread.rs b/src/libstd/sys/wasm/thread.rs deleted file mode 100644 index 0a11896a004..00000000000 --- a/src/libstd/sys/wasm/thread.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::ffi::CStr; -use crate::io; -use crate::sys::{unsupported, Void}; -use crate::time::Duration; - -pub struct Thread(Void); - -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() - } - - pub fn yield_now() { - // do nothing - } - - pub fn set_name(_name: &CStr) { - // nope - } - - #[cfg(not(target_feature = "atomics"))] - pub fn sleep(_dur: Duration) { - panic!("can't sleep"); - } - - #[cfg(target_feature = "atomics")] - pub fn sleep(dur: Duration) { - use crate::arch::wasm32; - use crate::cmp; - - // Use an atomic wait to block the current thread artificially with a - // timeout listed. Note that we should never be notified (return value - // of 0) or our comparison should never fail (return value of 1) so we - // should always only resume execution through a timeout (return value - // 2). - let mut nanos = dur.as_nanos(); - while nanos > 0 { - let amt = cmp::min(i64::MAX as u128, nanos); - let mut x = 0; - let val = unsafe { wasm32::i32_atomic_wait(&mut x, 0, amt as i64) }; - debug_assert_eq!(val, 2); - nanos -= amt; - } - } - - pub fn join(self) { - match self.0 {} - } -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option<Guard> { - None - } - pub unsafe fn init() -> Option<Guard> { - None - } -} - -// This is only used by atomics primitives when the `atomics` feature is -// enabled. In that mode we currently just use our own thread-local to store our -// current thread's ID, and then we lazily initialize it to something allocated -// from a global counter. -#[cfg(target_feature = "atomics")] -pub fn my_id() -> u32 { - use crate::sync::atomic::{AtomicU32, Ordering::SeqCst}; - - static NEXT_ID: AtomicU32 = AtomicU32::new(0); - - #[thread_local] - static mut MY_ID: u32 = 0; - - unsafe { - // If our thread ID isn't set yet then we need to allocate one. Do so - // with with a simple "atomically add to a global counter" strategy. - // This strategy doesn't handled what happens when the counter - // overflows, however, so just abort everything once the counter - // overflows and eventually we could have some sort of recycling scheme - // (or maybe this is all totally irrelevant by that point!). In any case - // though we're using a CAS loop instead of a `fetch_add` to ensure that - // the global counter never overflows. - if MY_ID == 0 { - let mut cur = NEXT_ID.load(SeqCst); - MY_ID = loop { - let next = cur.checked_add(1).unwrap_or_else(|| crate::arch::wasm32::unreachable()); - match NEXT_ID.compare_exchange(cur, next, SeqCst, SeqCst) { - Ok(_) => break next, - Err(i) => cur = i, - } - }; - } - MY_ID - } -} |
