1becdee57SLann use core::sync::atomic::Ordering; 2becdee57SLann 3becdee57SLann use crate::{Engine, vm::PoolingInstanceAllocator}; 4becdee57SLann 5becdee57SLann /// `PoolingAllocatorMetrics` provides access to runtime metrics of a pooling 6becdee57SLann /// allocator configured with [`crate::InstanceAllocationStrategy::Pooling`]. 7becdee57SLann /// 8becdee57SLann /// This is a cheap cloneable handle which can be obtained with 9becdee57SLann /// [`Engine::pooling_allocator_metrics`]. 10becdee57SLann #[derive(Clone)] 11becdee57SLann pub struct PoolingAllocatorMetrics { 12becdee57SLann engine: Engine, 13becdee57SLann } 14becdee57SLann 15becdee57SLann impl PoolingAllocatorMetrics { new(engine: &Engine) -> Option<Self>16becdee57SLann pub(crate) fn new(engine: &Engine) -> Option<Self> { 17becdee57SLann engine.allocator().as_pooling().map(|_| Self { 18becdee57SLann engine: engine.clone(), 19becdee57SLann }) 20becdee57SLann } 21becdee57SLann 22becdee57SLann /// Returns the number of core (module) instances currently allocated. core_instances(&self) -> u6423becdee57SLann pub fn core_instances(&self) -> u64 { 24becdee57SLann self.allocator().live_core_instances.load(Ordering::Relaxed) 25becdee57SLann } 26becdee57SLann 27becdee57SLann /// Returns the number of component instances currently allocated. component_instances(&self) -> u6428becdee57SLann pub fn component_instances(&self) -> u64 { 29becdee57SLann self.allocator() 30becdee57SLann .live_component_instances 31becdee57SLann .load(Ordering::Relaxed) 32becdee57SLann } 33becdee57SLann 34becdee57SLann /// Returns the number of WebAssembly memories currently allocated. memories(&self) -> usize35becdee57SLann pub fn memories(&self) -> usize { 36becdee57SLann self.allocator().live_memories.load(Ordering::Relaxed) 37becdee57SLann } 38becdee57SLann 39becdee57SLann /// Returns the number of WebAssembly tables currently allocated. tables(&self) -> usize40becdee57SLann pub fn tables(&self) -> usize { 41becdee57SLann self.allocator().live_tables.load(Ordering::Relaxed) 42becdee57SLann } 43becdee57SLann 44*c33c8b8dSAlex Crichton /// Returns the number of WebAssembly stacks currently allocated. 45*c33c8b8dSAlex Crichton #[cfg(feature = "async")] stacks(&self) -> usize46*c33c8b8dSAlex Crichton pub fn stacks(&self) -> usize { 47*c33c8b8dSAlex Crichton self.allocator().live_stacks.load(Ordering::Relaxed) 48*c33c8b8dSAlex Crichton } 49*c33c8b8dSAlex Crichton 50*c33c8b8dSAlex Crichton /// Returns the number of WebAssembly GC heaps currently allocated. 51*c33c8b8dSAlex Crichton #[cfg(feature = "gc")] gc_heaps(&self) -> usize52*c33c8b8dSAlex Crichton pub fn gc_heaps(&self) -> usize { 53*c33c8b8dSAlex Crichton self.allocator().live_gc_heaps.load(Ordering::Relaxed) 54*c33c8b8dSAlex Crichton } 55*c33c8b8dSAlex Crichton 56*c33c8b8dSAlex Crichton /// Returns the number of slots for linear memories in this allocator which 57*c33c8b8dSAlex Crichton /// are not currently in use but were previously used. 58*c33c8b8dSAlex Crichton /// 59*c33c8b8dSAlex Crichton /// A "warm" slot means that there was a previous instantiation of a memory 60*c33c8b8dSAlex Crichton /// in that slot. Warm slots are favored in general for allocating new 61*c33c8b8dSAlex Crichton /// memories over using a slot that has never been used before. unused_warm_memories(&self) -> u3262*c33c8b8dSAlex Crichton pub fn unused_warm_memories(&self) -> u32 { 63*c33c8b8dSAlex Crichton self.allocator().memories.unused_warm_slots() 64*c33c8b8dSAlex Crichton } 65*c33c8b8dSAlex Crichton 66*c33c8b8dSAlex Crichton /// Returns the number of bytes in this pooling allocator which are not part 67*c33c8b8dSAlex Crichton /// of any in-used linear memory slot but were previously used and are kept 68*c33c8b8dSAlex Crichton /// resident via the `*_keep_resident` configuration options. unused_memory_bytes_resident(&self) -> usize69*c33c8b8dSAlex Crichton pub fn unused_memory_bytes_resident(&self) -> usize { 70*c33c8b8dSAlex Crichton self.allocator().memories.unused_bytes_resident() 71*c33c8b8dSAlex Crichton } 72*c33c8b8dSAlex Crichton 73*c33c8b8dSAlex Crichton /// Returns the number of slots for tables in this allocator which are not 74*c33c8b8dSAlex Crichton /// currently in use but were previously used. 75*c33c8b8dSAlex Crichton /// 76*c33c8b8dSAlex Crichton /// A "warm" slot means that there was a previous instantiation of a table 77*c33c8b8dSAlex Crichton /// in that slot. Warm slots are favored in general for allocating new 78*c33c8b8dSAlex Crichton /// tables over using a slot that has never been used before. unused_warm_tables(&self) -> u3279*c33c8b8dSAlex Crichton pub fn unused_warm_tables(&self) -> u32 { 80*c33c8b8dSAlex Crichton self.allocator().tables.unused_warm_slots() 81*c33c8b8dSAlex Crichton } 82*c33c8b8dSAlex Crichton 83*c33c8b8dSAlex Crichton /// Returns the number of bytes in this pooling allocator which are not part 84*c33c8b8dSAlex Crichton /// of any in-used linear table slot but were previously used and are kept 85*c33c8b8dSAlex Crichton /// resident via the `*_keep_resident` configuration options. unused_table_bytes_resident(&self) -> usize86*c33c8b8dSAlex Crichton pub fn unused_table_bytes_resident(&self) -> usize { 87*c33c8b8dSAlex Crichton self.allocator().tables.unused_bytes_resident() 88*c33c8b8dSAlex Crichton } 89*c33c8b8dSAlex Crichton 90*c33c8b8dSAlex Crichton /// Returns the number of slots for stacks in this allocator which are not 91*c33c8b8dSAlex Crichton /// currently in use but were previously used. 92*c33c8b8dSAlex Crichton /// 93*c33c8b8dSAlex Crichton /// A "warm" slot means that there was a previous use of a stack 94*c33c8b8dSAlex Crichton /// in that slot. Warm slots are favored in general for allocating new 95*c33c8b8dSAlex Crichton /// stacks over using a slot that has never been used before. 96*c33c8b8dSAlex Crichton #[cfg(feature = "async")] unused_warm_stacks(&self) -> u3297*c33c8b8dSAlex Crichton pub fn unused_warm_stacks(&self) -> u32 { 98*c33c8b8dSAlex Crichton self.allocator().stacks.unused_warm_slots() 99*c33c8b8dSAlex Crichton } 100*c33c8b8dSAlex Crichton 101*c33c8b8dSAlex Crichton /// Returns the number of bytes in this pooling allocator which are not part 102*c33c8b8dSAlex Crichton /// of any in-used linear stack slot but were previously used and are kept 103*c33c8b8dSAlex Crichton /// resident via the `*_keep_resident` configuration options. 104*c33c8b8dSAlex Crichton /// 105*c33c8b8dSAlex Crichton /// This returns `None` if the `async_stack_zeroing` option is disabled or 106*c33c8b8dSAlex Crichton /// if the platform doesn't manage stacks (e.g. Windows returns `None`). 107*c33c8b8dSAlex Crichton #[cfg(feature = "async")] unused_stack_bytes_resident(&self) -> Option<usize>108*c33c8b8dSAlex Crichton pub fn unused_stack_bytes_resident(&self) -> Option<usize> { 109*c33c8b8dSAlex Crichton self.allocator().stacks.unused_bytes_resident() 110*c33c8b8dSAlex Crichton } 111*c33c8b8dSAlex Crichton allocator(&self) -> &PoolingInstanceAllocator112becdee57SLann fn allocator(&self) -> &PoolingInstanceAllocator { 113becdee57SLann self.engine 114becdee57SLann .allocator() 115becdee57SLann .as_pooling() 116becdee57SLann .expect("engine should have pooling allocator") 117becdee57SLann } 118becdee57SLann } 119becdee57SLann 120becdee57SLann #[cfg(test)] 121becdee57SLann mod tests { 122*c33c8b8dSAlex Crichton use crate::vm::instance::allocator::pooling::StackPool; 123becdee57SLann use crate::{ 124*c33c8b8dSAlex Crichton Config, Enabled, InstanceAllocationStrategy, Module, PoolingAllocationConfig, Result, 125*c33c8b8dSAlex Crichton Store, 126becdee57SLann component::{Component, Linker}, 127becdee57SLann }; 128*c33c8b8dSAlex Crichton use std::vec::Vec; 129becdee57SLann 130becdee57SLann use super::*; 131becdee57SLann 132becdee57SLann // A component with 1 core instance, 1 memory, 1 table 133becdee57SLann const TEST_COMPONENT: &[u8] = b" 134becdee57SLann (component 135becdee57SLann (core module $m 136becdee57SLann (memory 1) 137becdee57SLann (table 1 funcref) 138becdee57SLann ) 139becdee57SLann (core instance (instantiate (module $m))) 140becdee57SLann ) 141becdee57SLann "; 142becdee57SLann small_pool_config() -> PoolingAllocationConfig143*c33c8b8dSAlex Crichton pub(crate) fn small_pool_config() -> PoolingAllocationConfig { 144*c33c8b8dSAlex Crichton let mut config = PoolingAllocationConfig::new(); 145*c33c8b8dSAlex Crichton 146*c33c8b8dSAlex Crichton config.total_memories(10); 147*c33c8b8dSAlex Crichton config.max_memory_size(2 << 16); 148*c33c8b8dSAlex Crichton config.total_tables(10); 149*c33c8b8dSAlex Crichton config.table_elements(10); 150*c33c8b8dSAlex Crichton config.total_stacks(1); 151*c33c8b8dSAlex Crichton 152*c33c8b8dSAlex Crichton config 153*c33c8b8dSAlex Crichton } 154*c33c8b8dSAlex Crichton 155becdee57SLann #[test] 156becdee57SLann #[cfg_attr(miri, ignore)] smoke_test()157becdee57SLann fn smoke_test() { 158becdee57SLann // Start with nothing 159*c33c8b8dSAlex Crichton let engine = Engine::new(&Config::new().allocation_strategy(small_pool_config())).unwrap(); 160becdee57SLann let metrics = engine.pooling_allocator_metrics().unwrap(); 161becdee57SLann 162becdee57SLann assert_eq!(metrics.core_instances(), 0); 163becdee57SLann assert_eq!(metrics.component_instances(), 0); 164becdee57SLann assert_eq!(metrics.memories(), 0); 165becdee57SLann assert_eq!(metrics.tables(), 0); 166becdee57SLann 167becdee57SLann // Instantiate one of each 168becdee57SLann let mut store = Store::new(&engine, ()); 169becdee57SLann let component = Component::new(&engine, TEST_COMPONENT).unwrap(); 170becdee57SLann let linker = Linker::new(&engine); 171becdee57SLann let instance = linker.instantiate(&mut store, &component).unwrap(); 172becdee57SLann 173becdee57SLann assert_eq!(metrics.core_instances(), 1); 174becdee57SLann assert_eq!(metrics.component_instances(), 1); 175becdee57SLann assert_eq!(metrics.memories(), 1); 176becdee57SLann assert_eq!(metrics.tables(), 1); 177becdee57SLann 178becdee57SLann // Back to nothing 179becdee57SLann let _ = (instance, store); 180becdee57SLann 181becdee57SLann assert_eq!(metrics.core_instances(), 0); 182becdee57SLann assert_eq!(metrics.component_instances(), 0); 183becdee57SLann assert_eq!(metrics.memories(), 0); 184becdee57SLann assert_eq!(metrics.tables(), 0); 185becdee57SLann } 186becdee57SLann 187becdee57SLann #[test] test_non_pooling_allocator()188becdee57SLann fn test_non_pooling_allocator() { 189becdee57SLann let engine = 190becdee57SLann Engine::new(&Config::new().allocation_strategy(InstanceAllocationStrategy::OnDemand)) 191becdee57SLann .unwrap(); 192becdee57SLann 193becdee57SLann let maybe_metrics = engine.pooling_allocator_metrics(); 194becdee57SLann assert!(maybe_metrics.is_none()); 195becdee57SLann } 196*c33c8b8dSAlex Crichton 197*c33c8b8dSAlex Crichton #[test] 198*c33c8b8dSAlex Crichton #[cfg_attr(any(miri, not(target_os = "linux")), ignore)] unused_memories_tables_and_more() -> Result<()>199*c33c8b8dSAlex Crichton fn unused_memories_tables_and_more() -> Result<()> { 200*c33c8b8dSAlex Crichton let mut pool = small_pool_config(); 201*c33c8b8dSAlex Crichton pool.linear_memory_keep_resident(65536); 202*c33c8b8dSAlex Crichton pool.table_keep_resident(65536); 203*c33c8b8dSAlex Crichton pool.pagemap_scan(Enabled::Auto); 204*c33c8b8dSAlex Crichton let mut config = Config::new(); 205*c33c8b8dSAlex Crichton config.allocation_strategy(pool); 206*c33c8b8dSAlex Crichton let engine = Engine::new(&config)?; 207*c33c8b8dSAlex Crichton 208*c33c8b8dSAlex Crichton let metrics = engine.pooling_allocator_metrics().unwrap(); 209*c33c8b8dSAlex Crichton let host_page_size = crate::vm::host_page_size(); 210*c33c8b8dSAlex Crichton 211*c33c8b8dSAlex Crichton assert_eq!(metrics.memories(), 0); 212*c33c8b8dSAlex Crichton assert_eq!(metrics.core_instances(), 0); 213*c33c8b8dSAlex Crichton assert_eq!(metrics.component_instances(), 0); 214*c33c8b8dSAlex Crichton assert_eq!(metrics.memories(), 0); 215*c33c8b8dSAlex Crichton assert_eq!(metrics.tables(), 0); 216*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_memories(), 0); 217*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_memory_bytes_resident(), 0); 218*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_tables(), 0); 219*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_table_bytes_resident(), 0); 220*c33c8b8dSAlex Crichton 221*c33c8b8dSAlex Crichton let m1 = Module::new( 222*c33c8b8dSAlex Crichton &engine, 223*c33c8b8dSAlex Crichton r#" 224*c33c8b8dSAlex Crichton (module (memory (export "m") 1) (table 1 funcref)) 225*c33c8b8dSAlex Crichton "#, 226*c33c8b8dSAlex Crichton )?; 227*c33c8b8dSAlex Crichton 228*c33c8b8dSAlex Crichton let mut store = Store::new(&engine, ()); 229*c33c8b8dSAlex Crichton crate::Instance::new(&mut store, &m1, &[])?; 230*c33c8b8dSAlex Crichton assert_eq!(metrics.memories(), 1); 231*c33c8b8dSAlex Crichton assert_eq!(metrics.tables(), 1); 232*c33c8b8dSAlex Crichton assert_eq!(metrics.core_instances(), 1); 233*c33c8b8dSAlex Crichton assert_eq!(metrics.component_instances(), 0); 234*c33c8b8dSAlex Crichton drop(store); 235*c33c8b8dSAlex Crichton 236*c33c8b8dSAlex Crichton assert_eq!(metrics.memories(), 0); 237*c33c8b8dSAlex Crichton assert_eq!(metrics.tables(), 0); 238*c33c8b8dSAlex Crichton assert_eq!(metrics.core_instances(), 0); 239*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_memories(), 1); 240*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_tables(), 1); 241*c33c8b8dSAlex Crichton if PoolingAllocationConfig::is_pagemap_scan_available() { 242*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_memory_bytes_resident(), 0); 243*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_table_bytes_resident(), host_page_size); 244*c33c8b8dSAlex Crichton } else { 245*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_memory_bytes_resident(), 65536); 246*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_table_bytes_resident(), host_page_size); 247*c33c8b8dSAlex Crichton } 248*c33c8b8dSAlex Crichton 249*c33c8b8dSAlex Crichton let mut store = Store::new(&engine, ()); 250*c33c8b8dSAlex Crichton let i = crate::Instance::new(&mut store, &m1, &[])?; 251*c33c8b8dSAlex Crichton assert_eq!(metrics.memories(), 1); 252*c33c8b8dSAlex Crichton assert_eq!(metrics.tables(), 1); 253*c33c8b8dSAlex Crichton assert_eq!(metrics.core_instances(), 1); 254*c33c8b8dSAlex Crichton assert_eq!(metrics.component_instances(), 0); 255*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_memories(), 0); 256*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_tables(), 0); 257*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_memory_bytes_resident(), 0); 258*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_table_bytes_resident(), 0); 259*c33c8b8dSAlex Crichton let m = i.get_memory(&mut store, "m").unwrap(); 260*c33c8b8dSAlex Crichton m.data_mut(&mut store)[0] = 1; 261*c33c8b8dSAlex Crichton m.grow(&mut store, 1)?; 262*c33c8b8dSAlex Crichton drop(store); 263*c33c8b8dSAlex Crichton 264*c33c8b8dSAlex Crichton assert_eq!(metrics.memories(), 0); 265*c33c8b8dSAlex Crichton assert_eq!(metrics.tables(), 0); 266*c33c8b8dSAlex Crichton assert_eq!(metrics.core_instances(), 0); 267*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_memories(), 1); 268*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_tables(), 1); 269*c33c8b8dSAlex Crichton if PoolingAllocationConfig::is_pagemap_scan_available() { 270*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_memory_bytes_resident(), host_page_size); 271*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_table_bytes_resident(), host_page_size); 272*c33c8b8dSAlex Crichton } else { 273*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_memory_bytes_resident(), 65536); 274*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_table_bytes_resident(), host_page_size); 275*c33c8b8dSAlex Crichton } 276*c33c8b8dSAlex Crichton 277*c33c8b8dSAlex Crichton let stores = (0..10) 278*c33c8b8dSAlex Crichton .map(|_| { 279*c33c8b8dSAlex Crichton let mut store = Store::new(&engine, ()); 280*c33c8b8dSAlex Crichton crate::Instance::new(&mut store, &m1, &[]).unwrap(); 281*c33c8b8dSAlex Crichton store 282*c33c8b8dSAlex Crichton }) 283*c33c8b8dSAlex Crichton .collect::<Vec<_>>(); 284*c33c8b8dSAlex Crichton 285*c33c8b8dSAlex Crichton assert_eq!(metrics.memories(), 10); 286*c33c8b8dSAlex Crichton assert_eq!(metrics.tables(), 10); 287*c33c8b8dSAlex Crichton assert_eq!(metrics.core_instances(), 10); 288*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_memories(), 0); 289*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_tables(), 0); 290*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_memory_bytes_resident(), 0); 291*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_table_bytes_resident(), 0); 292*c33c8b8dSAlex Crichton 293*c33c8b8dSAlex Crichton drop(stores); 294*c33c8b8dSAlex Crichton 295*c33c8b8dSAlex Crichton assert_eq!(metrics.memories(), 00); 296*c33c8b8dSAlex Crichton assert_eq!(metrics.tables(), 00); 297*c33c8b8dSAlex Crichton assert_eq!(metrics.core_instances(), 00); 298*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_memories(), 10); 299*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_tables(), 10); 300*c33c8b8dSAlex Crichton if PoolingAllocationConfig::is_pagemap_scan_available() { 301*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_memory_bytes_resident(), host_page_size); 302*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_table_bytes_resident(), 10 * host_page_size); 303*c33c8b8dSAlex Crichton } else { 304*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_memory_bytes_resident(), 10 * 65536); 305*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_table_bytes_resident(), 10 * host_page_size); 306*c33c8b8dSAlex Crichton } 307*c33c8b8dSAlex Crichton 308*c33c8b8dSAlex Crichton Ok(()) 309*c33c8b8dSAlex Crichton } 310*c33c8b8dSAlex Crichton 311*c33c8b8dSAlex Crichton #[test] 312*c33c8b8dSAlex Crichton #[cfg_attr(miri, ignore)] gc_heaps() -> Result<()>313*c33c8b8dSAlex Crichton fn gc_heaps() -> Result<()> { 314*c33c8b8dSAlex Crichton let pool = small_pool_config(); 315*c33c8b8dSAlex Crichton let mut config = Config::new(); 316*c33c8b8dSAlex Crichton config.allocation_strategy(pool); 317*c33c8b8dSAlex Crichton let engine = Engine::new(&config)?; 318*c33c8b8dSAlex Crichton 319*c33c8b8dSAlex Crichton let metrics = engine.pooling_allocator_metrics().unwrap(); 320*c33c8b8dSAlex Crichton 321*c33c8b8dSAlex Crichton assert_eq!(metrics.gc_heaps(), 0); 322*c33c8b8dSAlex Crichton let mut store = Store::new(&engine, ()); 323*c33c8b8dSAlex Crichton crate::ExternRef::new(&mut store, ())?; 324*c33c8b8dSAlex Crichton assert_eq!(metrics.gc_heaps(), 1); 325*c33c8b8dSAlex Crichton drop(store); 326*c33c8b8dSAlex Crichton assert_eq!(metrics.gc_heaps(), 0); 327*c33c8b8dSAlex Crichton 328*c33c8b8dSAlex Crichton Ok(()) 329*c33c8b8dSAlex Crichton } 330*c33c8b8dSAlex Crichton 331*c33c8b8dSAlex Crichton #[tokio::test] 332*c33c8b8dSAlex Crichton #[cfg_attr(miri, ignore)] stacks() -> Result<()>333*c33c8b8dSAlex Crichton async fn stacks() -> Result<()> { 334*c33c8b8dSAlex Crichton let pool = small_pool_config(); 335*c33c8b8dSAlex Crichton let mut config = Config::new(); 336*c33c8b8dSAlex Crichton config.allocation_strategy(pool); 337*c33c8b8dSAlex Crichton let engine = Engine::new(&config)?; 338*c33c8b8dSAlex Crichton 339*c33c8b8dSAlex Crichton let metrics = engine.pooling_allocator_metrics().unwrap(); 340*c33c8b8dSAlex Crichton 341*c33c8b8dSAlex Crichton assert_eq!(metrics.stacks(), 0); 342*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_stacks(), 0); 343*c33c8b8dSAlex Crichton let mut store = Store::new(&engine, ()); 344*c33c8b8dSAlex Crichton 345*c33c8b8dSAlex Crichton crate::Func::wrap(&mut store, || {}) 346*c33c8b8dSAlex Crichton .call_async(&mut store, &[], &mut []) 347*c33c8b8dSAlex Crichton .await?; 348*c33c8b8dSAlex Crichton assert_eq!(metrics.stacks(), 1); 349*c33c8b8dSAlex Crichton drop(store); 350*c33c8b8dSAlex Crichton assert_eq!(metrics.stacks(), 0); 351*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_stack_bytes_resident(), None); 352*c33c8b8dSAlex Crichton if StackPool::enabled() { 353*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_stacks(), 1); 354*c33c8b8dSAlex Crichton } else { 355*c33c8b8dSAlex Crichton assert_eq!(metrics.unused_warm_stacks(), 0); 356*c33c8b8dSAlex Crichton } 357*c33c8b8dSAlex Crichton 358*c33c8b8dSAlex Crichton Ok(()) 359*c33c8b8dSAlex Crichton } 360becdee57SLann } 361