1 use super::GcHeapAllocationIndex; 2 use super::index_allocator::{SimpleIndexAllocator, SlotId}; 3 use crate::runtime::vm::{GcHeap, GcRuntime, PoolingInstanceAllocatorConfig, Result}; 4 use crate::vm::{Memory, MemoryAllocationIndex}; 5 use crate::{Engine, prelude::*}; 6 use std::sync::Mutex; 7 8 enum HeapSlot { 9 /// The is available for use, and we may or may not have lazily allocated 10 /// its associated GC heap yet. 11 Free(Option<Box<dyn GcHeap>>), 12 13 /// The slot's heap is currently in use, and it is backed by this memory 14 /// allocation index. 15 InUse(MemoryAllocationIndex), 16 } 17 18 impl HeapSlot { alloc(&mut self, memory_alloc_index: MemoryAllocationIndex) -> Option<Box<dyn GcHeap>>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 dealloc(&mut self, heap: Box<dyn GcHeap>) -> MemoryAllocationIndex30 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 41 /// A pool of reusable GC heaps. 42 pub struct GcHeapPool { 43 max_gc_heaps: usize, 44 index_allocator: SimpleIndexAllocator, 45 heaps: Mutex<Box<[HeapSlot]>>, 46 } 47 48 impl std::fmt::Debug for GcHeapPool { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result49 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 58 impl GcHeapPool { 59 /// Create a new `GcHeapPool` with the given configuration. new(config: &PoolingInstanceAllocatorConfig) -> Result<Self>60 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 // Each individual GC heap in the pool is lazily allocated. See the 65 // `allocate` method. 66 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 /// Are there zero slots in use right now? is_empty(&self) -> bool76 pub fn is_empty(&self) -> bool { 77 self.index_allocator.is_empty() 78 } 79 80 /// Allocate a single table for the given instance allocation request. allocate( &self, engine: &Engine, gc_runtime: &dyn GcRuntime, memory_alloc_index: MemoryAllocationIndex, memory: Memory, ) -> Result<(GcHeapAllocationIndex, Box<dyn GcHeap>)>81 pub fn allocate( 82 &self, 83 engine: &Engine, 84 gc_runtime: &dyn GcRuntime, 85 memory_alloc_index: MemoryAllocationIndex, 86 memory: Memory, 87 ) -> Result<(GcHeapAllocationIndex, Box<dyn GcHeap>)> { 88 let allocation_index = self 89 .index_allocator 90 .alloc() 91 .map(|slot| GcHeapAllocationIndex(slot.0)) 92 .ok_or_else(|| { 93 format_err!( 94 "maximum concurrent GC heap limit of {} reached", 95 self.max_gc_heaps 96 ) 97 })?; 98 debug_assert_ne!(allocation_index, GcHeapAllocationIndex::default()); 99 100 let mut heap = match { 101 let mut heaps = self.heaps.lock().unwrap(); 102 heaps[allocation_index.index()].alloc(memory_alloc_index) 103 } { 104 // If we already have a heap at this slot, reuse it. 105 Some(heap) => heap, 106 // Otherwise, we haven't forced this slot's lazily allocated heap 107 // yet. So do that now. 108 None => gc_runtime.new_gc_heap(engine)?, 109 }; 110 111 debug_assert!(!heap.is_attached()); 112 heap.attach(memory); 113 114 Ok((allocation_index, heap)) 115 } 116 117 /// Deallocate a previously-allocated GC heap. deallocate( &self, allocation_index: GcHeapAllocationIndex, mut heap: Box<dyn GcHeap>, ) -> (MemoryAllocationIndex, Memory)118 pub fn deallocate( 119 &self, 120 allocation_index: GcHeapAllocationIndex, 121 mut heap: Box<dyn GcHeap>, 122 ) -> (MemoryAllocationIndex, Memory) { 123 debug_assert_ne!(allocation_index, GcHeapAllocationIndex::default()); 124 125 let memory = heap.detach(); 126 127 // NB: Replace the heap before freeing the index. If we did it in the 128 // opposite order, a concurrent allocation request could reallocate the 129 // index before we have replaced the heap. 130 131 let memory_alloc_index = { 132 let mut heaps = self.heaps.lock().unwrap(); 133 heaps[allocation_index.index()].dealloc(heap) 134 }; 135 136 self.index_allocator.free(SlotId(allocation_index.0), 0); 137 138 (memory_alloc_index, memory) 139 } 140 } 141