summaryrefslogtreecommitdiff
path: root/src/libstd/sys/wasm/alloc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/sys/wasm/alloc.rs')
-rw-r--r--src/libstd/sys/wasm/alloc.rs158
1 files changed, 0 insertions, 158 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!
-}