diff options
Diffstat (limited to 'src/libstd/sys/wasm/alloc.rs')
| -rw-r--r-- | src/libstd/sys/wasm/alloc.rs | 158 | 
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! -} | 
