1 use core::sync::atomic::Ordering; 2 3 use crate::{Engine, vm::PoolingInstanceAllocator}; 4 5 /// `PoolingAllocatorMetrics` provides access to runtime metrics of a pooling 6 /// allocator configured with [`crate::InstanceAllocationStrategy::Pooling`]. 7 /// 8 /// This is a cheap cloneable handle which can be obtained with 9 /// [`Engine::pooling_allocator_metrics`]. 10 #[derive(Clone)] 11 pub struct PoolingAllocatorMetrics { 12 engine: Engine, 13 } 14 15 impl PoolingAllocatorMetrics { 16 pub(crate) fn new(engine: &Engine) -> Option<Self> { 17 engine.allocator().as_pooling().map(|_| Self { 18 engine: engine.clone(), 19 }) 20 } 21 22 /// Returns the number of core (module) instances currently allocated. 23 pub fn core_instances(&self) -> u64 { 24 self.allocator().live_core_instances.load(Ordering::Relaxed) 25 } 26 27 /// Returns the number of component instances currently allocated. 28 pub fn component_instances(&self) -> u64 { 29 self.allocator() 30 .live_component_instances 31 .load(Ordering::Relaxed) 32 } 33 34 /// Returns the number of WebAssembly memories currently allocated. 35 pub fn memories(&self) -> usize { 36 self.allocator().live_memories.load(Ordering::Relaxed) 37 } 38 39 /// Returns the number of WebAssembly tables currently allocated. 40 pub fn tables(&self) -> usize { 41 self.allocator().live_tables.load(Ordering::Relaxed) 42 } 43 44 fn allocator(&self) -> &PoolingInstanceAllocator { 45 self.engine 46 .allocator() 47 .as_pooling() 48 .expect("engine should have pooling allocator") 49 } 50 } 51 52 #[cfg(test)] 53 mod tests { 54 use crate::{ 55 Config, InstanceAllocationStrategy, Store, 56 component::{Component, Linker}, 57 }; 58 59 use super::*; 60 61 // A component with 1 core instance, 1 memory, 1 table 62 const TEST_COMPONENT: &[u8] = b" 63 (component 64 (core module $m 65 (memory 1) 66 (table 1 funcref) 67 ) 68 (core instance (instantiate (module $m))) 69 ) 70 "; 71 72 #[test] 73 #[cfg_attr(miri, ignore)] 74 fn smoke_test() { 75 // Start with nothing 76 let engine = 77 Engine::new(&Config::new().allocation_strategy(InstanceAllocationStrategy::pooling())) 78 .unwrap(); 79 let metrics = engine.pooling_allocator_metrics().unwrap(); 80 81 assert_eq!(metrics.core_instances(), 0); 82 assert_eq!(metrics.component_instances(), 0); 83 assert_eq!(metrics.memories(), 0); 84 assert_eq!(metrics.tables(), 0); 85 86 // Instantiate one of each 87 let mut store = Store::new(&engine, ()); 88 let component = Component::new(&engine, TEST_COMPONENT).unwrap(); 89 let linker = Linker::new(&engine); 90 let instance = linker.instantiate(&mut store, &component).unwrap(); 91 92 assert_eq!(metrics.core_instances(), 1); 93 assert_eq!(metrics.component_instances(), 1); 94 assert_eq!(metrics.memories(), 1); 95 assert_eq!(metrics.tables(), 1); 96 97 // Back to nothing 98 let _ = (instance, store); 99 100 assert_eq!(metrics.core_instances(), 0); 101 assert_eq!(metrics.component_instances(), 0); 102 assert_eq!(metrics.memories(), 0); 103 assert_eq!(metrics.tables(), 0); 104 } 105 106 #[test] 107 fn test_non_pooling_allocator() { 108 let engine = 109 Engine::new(&Config::new().allocation_strategy(InstanceAllocationStrategy::OnDemand)) 110 .unwrap(); 111 112 let maybe_metrics = engine.pooling_allocator_metrics(); 113 assert!(maybe_metrics.is_none()); 114 } 115 } 116