1 #![cfg_attr( 2 all(unix, not(miri), not(asan)), 3 expect(dead_code, reason = "not used, but typechecked") 4 )] 5 6 use crate::PoolConcurrencyLimitError; 7 use crate::prelude::*; 8 use crate::runtime::vm::PoolingInstanceAllocatorConfig; 9 use std::sync::atomic::{AtomicU64, Ordering}; 10 11 /// A generic implementation of a stack pool. 12 /// 13 /// This implementation technically doesn't actually pool anything at this time. 14 /// Originally this was the implementation for non-Unix (e.g. Windows and 15 /// MIRI), but nowadays this is also used for fuzzing. For more documentation 16 /// for why this is used on fuzzing see the `asan` module in the 17 /// `wasmtime-fiber` crate. 18 /// 19 /// Currently the only purpose of `StackPool` is to limit the total number of 20 /// concurrent stacks while otherwise leveraging `wasmtime_fiber::FiberStack` 21 /// natively. 22 #[derive(Debug)] 23 pub struct StackPool { 24 stack_size: usize, 25 stack_zeroing: bool, 26 live_stacks: AtomicU64, 27 stack_limit: u64, 28 } 29 30 impl StackPool { 31 #[cfg(test)] enabled() -> bool32 pub fn enabled() -> bool { 33 false 34 } 35 new(config: &PoolingInstanceAllocatorConfig) -> Result<Self>36 pub fn new(config: &PoolingInstanceAllocatorConfig) -> Result<Self> { 37 Ok(StackPool { 38 stack_size: config.stack_size, 39 stack_zeroing: config.async_stack_zeroing, 40 live_stacks: AtomicU64::new(0), 41 stack_limit: config.limits.total_stacks.into(), 42 }) 43 } 44 is_empty(&self) -> bool45 pub fn is_empty(&self) -> bool { 46 self.live_stacks.load(Ordering::Acquire) == 0 47 } 48 allocate(&self) -> Result<wasmtime_fiber::FiberStack>49 pub fn allocate(&self) -> Result<wasmtime_fiber::FiberStack> { 50 if self.stack_size == 0 { 51 bail!("fiber stack allocation not supported") 52 } 53 54 let old_count = self.live_stacks.fetch_add(1, Ordering::AcqRel); 55 if old_count >= self.stack_limit { 56 self.live_stacks.fetch_sub(1, Ordering::AcqRel); 57 return Err(PoolConcurrencyLimitError::new( 58 usize::try_from(self.stack_limit).unwrap(), 59 "fibers", 60 ) 61 .into()); 62 } 63 64 match wasmtime_fiber::FiberStack::new(self.stack_size, self.stack_zeroing) { 65 Ok(stack) => Ok(stack), 66 Err(e) => { 67 self.live_stacks.fetch_sub(1, Ordering::AcqRel); 68 69 #[allow( 70 clippy::useless_conversion, 71 reason = "some `cfg`s have `wasmtime::Error` as the error type, others don't" 72 )] 73 let e = crate::Error::from(e); 74 75 Err(e) 76 } 77 } 78 } 79 zero_stack( &self, _stack: &mut wasmtime_fiber::FiberStack, _decommit: impl FnMut(*mut u8, usize), ) -> usize80 pub unsafe fn zero_stack( 81 &self, 82 _stack: &mut wasmtime_fiber::FiberStack, 83 _decommit: impl FnMut(*mut u8, usize), 84 ) -> usize { 85 // No need to actually zero the stack, since the stack won't ever be 86 // reused on non-unix systems. 87 0 88 } 89 90 /// Safety: see the unix implementation. deallocate(&self, stack: wasmtime_fiber::FiberStack, _bytes_resident: usize)91 pub unsafe fn deallocate(&self, stack: wasmtime_fiber::FiberStack, _bytes_resident: usize) { 92 self.live_stacks.fetch_sub(1, Ordering::AcqRel); 93 // A no-op as we don't actually own the fiber stack on Windows. 94 let _ = stack; 95 } 96 unused_warm_slots(&self) -> u3297 pub fn unused_warm_slots(&self) -> u32 { 98 0 99 } 100 unused_bytes_resident(&self) -> Option<usize>101 pub fn unused_bytes_resident(&self) -> Option<usize> { 102 None 103 } 104 } 105