Skip to main content

wasmtime/runtime/vm/instance/allocator/
on_demand.rs

1use super::{
2    InstanceAllocationRequest, InstanceAllocator, MemoryAllocationIndex, TableAllocationIndex,
3};
4use crate::prelude::*;
5use crate::runtime::vm::CompiledModuleId;
6use crate::runtime::vm::instance::RuntimeMemoryCreator;
7use crate::runtime::vm::memory::{DefaultMemoryCreator, Memory};
8use crate::runtime::vm::mpk::ProtectionKey;
9use crate::runtime::vm::table::Table;
10use alloc::sync::Arc;
11use core::future::Future;
12use core::pin::Pin;
13use wasmtime_environ::{
14    DefinedMemoryIndex, DefinedTableIndex, HostPtr, MemoryKind, Module, VMOffsets,
15};
16
17#[cfg(feature = "gc")]
18use crate::runtime::vm::{GcHeap, GcHeapAllocationIndex, GcRuntime};
19
20#[cfg(feature = "async")]
21use wasmtime_fiber::RuntimeFiberStackCreator;
22
23#[cfg(feature = "component-model")]
24use wasmtime_environ::{
25    StaticModuleIndex,
26    component::{Component, VMComponentOffsets},
27};
28
29/// Represents the on-demand instance allocator.
30#[derive(Clone)]
31pub struct OnDemandInstanceAllocator {
32    mem_creator: Option<Arc<dyn RuntimeMemoryCreator>>,
33    #[cfg(feature = "async")]
34    stack_creator: Option<Arc<dyn RuntimeFiberStackCreator>>,
35    #[cfg(feature = "async")]
36    stack_size: usize,
37    #[cfg(feature = "async")]
38    stack_zeroing: bool,
39}
40
41impl OnDemandInstanceAllocator {
42    /// Creates a new on-demand instance allocator.
43    pub fn new(
44        mem_creator: Option<Arc<dyn RuntimeMemoryCreator>>,
45        stack_size: usize,
46        stack_zeroing: bool,
47    ) -> Self {
48        let _ = (stack_size, stack_zeroing); // suppress warnings when async feature is disabled.
49        Self {
50            mem_creator,
51            #[cfg(feature = "async")]
52            stack_creator: None,
53            #[cfg(feature = "async")]
54            stack_size,
55            #[cfg(feature = "async")]
56            stack_zeroing,
57        }
58    }
59
60    /// Set the stack creator.
61    #[cfg(feature = "async")]
62    pub fn set_stack_creator(&mut self, stack_creator: Arc<dyn RuntimeFiberStackCreator>) {
63        self.stack_creator = Some(stack_creator);
64    }
65}
66
67impl Default for OnDemandInstanceAllocator {
68    fn default() -> Self {
69        Self {
70            mem_creator: None,
71            #[cfg(feature = "async")]
72            stack_creator: None,
73            #[cfg(feature = "async")]
74            stack_size: 0,
75            #[cfg(feature = "async")]
76            stack_zeroing: false,
77        }
78    }
79}
80
81unsafe impl InstanceAllocator for OnDemandInstanceAllocator {
82    #[cfg(feature = "component-model")]
83    fn validate_component<'a>(
84        &self,
85        _component: &Component,
86        _offsets: &VMComponentOffsets<HostPtr>,
87        _get_module: &'a dyn Fn(StaticModuleIndex) -> &'a Module,
88    ) -> Result<()> {
89        Ok(())
90    }
91
92    fn validate_module(&self, _module: &Module, _offsets: &VMOffsets<HostPtr>) -> Result<()> {
93        Ok(())
94    }
95
96    #[cfg(feature = "gc")]
97    fn validate_memory(&self, _memory: &wasmtime_environ::Memory) -> Result<()> {
98        Ok(())
99    }
100
101    #[cfg(feature = "component-model")]
102    fn increment_component_instance_count(&self) -> Result<()> {
103        Ok(())
104    }
105
106    #[cfg(feature = "component-model")]
107    fn decrement_component_instance_count(&self) {}
108
109    fn increment_core_instance_count(&self) -> Result<()> {
110        Ok(())
111    }
112
113    fn decrement_core_instance_count(&self) {}
114
115    fn allocate_memory<'a, 'b: 'a, 'c: 'a>(
116        &'a self,
117        request: &'a mut InstanceAllocationRequest<'b, 'c>,
118        ty: &'a wasmtime_environ::Memory,
119        memory_index: Option<DefinedMemoryIndex>,
120        memory_kind: MemoryKind,
121    ) -> Pin<Box<dyn Future<Output = Result<(MemoryAllocationIndex, Memory)>> + Send + 'a>> {
122        debug_assert_eq!(memory_index.is_none(), memory_kind == MemoryKind::GcHeap);
123
124        let creator = self
125            .mem_creator
126            .as_deref()
127            .unwrap_or_else(|| &DefaultMemoryCreator);
128
129        crate::runtime::box_future(async move {
130            let image = if let Some(memory_index) = memory_index {
131                request.runtime_info.memory_image(memory_index)?
132            } else {
133                None
134            };
135
136            let allocation_index = MemoryAllocationIndex::default();
137            let memory = Memory::new_dynamic(
138                ty,
139                request.store.engine(),
140                creator,
141                image,
142                request.limiter.as_deref_mut(),
143                memory_kind,
144            )
145            .await?;
146            Ok((allocation_index, memory))
147        })
148    }
149
150    unsafe fn deallocate_memory(
151        &self,
152        _memory_index: Option<DefinedMemoryIndex>,
153        allocation_index: MemoryAllocationIndex,
154        _memory: Memory,
155    ) {
156        debug_assert_eq!(allocation_index, MemoryAllocationIndex::default());
157        // Normal destructors do all the necessary clean up.
158    }
159
160    fn allocate_table<'a, 'b: 'a, 'c: 'a>(
161        &'a self,
162        request: &'a mut InstanceAllocationRequest<'b, 'c>,
163        ty: &'a wasmtime_environ::Table,
164        _table_index: DefinedTableIndex,
165    ) -> Pin<Box<dyn Future<Output = Result<(TableAllocationIndex, Table)>> + Send + 'a>> {
166        crate::runtime::box_future(async move {
167            let allocation_index = TableAllocationIndex::default();
168            let table = Table::new_dynamic(
169                ty,
170                request.store.engine().tunables(),
171                request.limiter.as_deref_mut(),
172            )
173            .await?;
174            Ok((allocation_index, table))
175        })
176    }
177
178    unsafe fn deallocate_table(
179        &self,
180        _table_index: DefinedTableIndex,
181        allocation_index: TableAllocationIndex,
182        _table: Table,
183    ) {
184        debug_assert_eq!(allocation_index, TableAllocationIndex::default());
185        // Normal destructors do all the necessary clean up.
186    }
187
188    #[cfg(feature = "async")]
189    fn allocate_fiber_stack(&self) -> Result<wasmtime_fiber::FiberStack> {
190        if self.stack_size == 0 {
191            crate::bail!("fiber stacks are not supported by the allocator")
192        }
193        let stack = match &self.stack_creator {
194            Some(stack_creator) => {
195                let stack = stack_creator.new_stack(self.stack_size, self.stack_zeroing)?;
196                wasmtime_fiber::FiberStack::from_custom(stack)
197            }
198            None => wasmtime_fiber::FiberStack::new(self.stack_size, self.stack_zeroing),
199        }?;
200        Ok(stack)
201    }
202
203    #[cfg(feature = "async")]
204    unsafe fn deallocate_fiber_stack(&self, stack: wasmtime_fiber::FiberStack) {
205        // The on-demand allocator has no further bookkeeping for fiber stacks
206        // beyond dropping them.
207        let _ = stack;
208    }
209
210    fn purge_module(&self, _: CompiledModuleId) {}
211
212    fn next_available_pkey(&self) -> Option<ProtectionKey> {
213        // The on-demand allocator cannot use protection keys--it requires
214        // back-to-back allocation of memory slots that this allocator cannot
215        // guarantee.
216        None
217    }
218
219    fn restrict_to_pkey(&self, _: ProtectionKey) {
220        // The on-demand allocator cannot use protection keys; an on-demand
221        // allocator will never hand out protection keys to the stores its
222        // engine creates.
223        unreachable!()
224    }
225
226    fn allow_all_pkeys(&self) {
227        // The on-demand allocator cannot use protection keys; an on-demand
228        // allocator will never hand out protection keys to the stores its
229        // engine creates.
230        unreachable!()
231    }
232
233    #[cfg(feature = "gc")]
234    fn allocate_gc_heap(
235        &self,
236        engine: &crate::Engine,
237        gc_runtime: &dyn GcRuntime,
238        memory_alloc_index: MemoryAllocationIndex,
239        memory: Memory,
240    ) -> Result<(GcHeapAllocationIndex, Box<dyn GcHeap>)> {
241        debug_assert_eq!(memory_alloc_index, MemoryAllocationIndex::default());
242        let mut heap = gc_runtime.new_gc_heap(engine)?;
243        heap.attach(memory);
244        Ok((GcHeapAllocationIndex::default(), heap))
245    }
246
247    #[cfg(feature = "gc")]
248    fn deallocate_gc_heap(
249        &self,
250        allocation_index: GcHeapAllocationIndex,
251        mut gc_heap: Box<dyn crate::runtime::vm::GcHeap>,
252    ) -> (MemoryAllocationIndex, Memory) {
253        debug_assert_eq!(allocation_index, GcHeapAllocationIndex::default());
254        (MemoryAllocationIndex::default(), gc_heap.detach())
255    }
256}