wasmtime/runtime/vm/instance/allocator/pooling/
gc_heap_pool.rs1use super::GcHeapAllocationIndex;
2use super::index_allocator::{SimpleIndexAllocator, SlotId};
3use crate::runtime::vm::{GcHeap, GcRuntime, PoolingInstanceAllocatorConfig, Result};
4use crate::vm::{Memory, MemoryAllocationIndex};
5use crate::{Engine, prelude::*};
6use std::sync::Mutex;
7
8enum HeapSlot {
9 Free(Option<Box<dyn GcHeap>>),
12
13 InUse(MemoryAllocationIndex),
16}
17
18impl HeapSlot {
19 fn alloc(&mut self, memory_alloc_index: MemoryAllocationIndex) -> Option<Box<dyn GcHeap>> {
20 match self {
21 HeapSlot::Free(gc_heap) => {
22 let gc_heap = gc_heap.take();
23 *self = HeapSlot::InUse(memory_alloc_index);
24 gc_heap
25 }
26 HeapSlot::InUse(_) => panic!("already in use"),
27 }
28 }
29
30 fn dealloc(&mut self, heap: Box<dyn GcHeap>) -> MemoryAllocationIndex {
31 match *self {
32 HeapSlot::Free(_) => panic!("already free"),
33 HeapSlot::InUse(memory_alloc_index) => {
34 *self = HeapSlot::Free(Some(heap));
35 memory_alloc_index
36 }
37 }
38 }
39}
40
41pub struct GcHeapPool {
43 max_gc_heaps: usize,
44 index_allocator: SimpleIndexAllocator,
45 heaps: Mutex<Box<[HeapSlot]>>,
46}
47
48impl std::fmt::Debug for GcHeapPool {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 f.debug_struct("GcHeapPool")
51 .field("max_gc_heaps", &self.max_gc_heaps)
52 .field("index_allocator", &self.index_allocator)
53 .field("heaps", &"..")
54 .finish()
55 }
56}
57
58impl GcHeapPool {
59 pub fn new(config: &PoolingInstanceAllocatorConfig) -> Result<Self> {
61 let index_allocator = SimpleIndexAllocator::new(config.limits.total_gc_heaps);
62 let max_gc_heaps = usize::try_from(config.limits.total_gc_heaps).unwrap();
63
64 let heaps = Mutex::new((0..max_gc_heaps).map(|_| HeapSlot::Free(None)).collect());
67
68 Ok(Self {
69 max_gc_heaps,
70 index_allocator,
71 heaps,
72 })
73 }
74
75 #[allow(unused)] pub fn is_empty(&self) -> bool {
78 self.index_allocator.is_empty()
79 }
80
81 pub fn allocate(
83 &self,
84 engine: &Engine,
85 gc_runtime: &dyn GcRuntime,
86 memory_alloc_index: MemoryAllocationIndex,
87 memory: Memory,
88 ) -> Result<(GcHeapAllocationIndex, Box<dyn GcHeap>)> {
89 let allocation_index = self
90 .index_allocator
91 .alloc()
92 .map(|slot| GcHeapAllocationIndex(slot.0))
93 .ok_or_else(|| {
94 anyhow!(
95 "maximum concurrent GC heap limit of {} reached",
96 self.max_gc_heaps
97 )
98 })?;
99 debug_assert_ne!(allocation_index, GcHeapAllocationIndex::default());
100
101 let mut heap = match {
102 let mut heaps = self.heaps.lock().unwrap();
103 heaps[allocation_index.index()].alloc(memory_alloc_index)
104 } {
105 Some(heap) => heap,
107 None => gc_runtime.new_gc_heap(engine)?,
110 };
111
112 debug_assert!(!heap.is_attached());
113 heap.attach(memory);
114
115 Ok((allocation_index, heap))
116 }
117
118 pub fn deallocate(
120 &self,
121 allocation_index: GcHeapAllocationIndex,
122 mut heap: Box<dyn GcHeap>,
123 ) -> (MemoryAllocationIndex, Memory) {
124 debug_assert_ne!(allocation_index, GcHeapAllocationIndex::default());
125
126 let memory = heap.detach();
127
128 let memory_alloc_index = {
133 let mut heaps = self.heaps.lock().unwrap();
134 heaps[allocation_index.index()].dealloc(heap)
135 };
136
137 self.index_allocator.free(SlotId(allocation_index.0));
138
139 (memory_alloc_index, memory)
140 }
141}