ZetaNumbers commented on issue #2583:
I tried to replicate this bug on 0.32.0, but it seems to be fixed! I had to update example above, but i am sure logic didn't change:
use wasmtime::*; struct MyMemory {} unsafe impl LinearMemory for MyMemory { fn byte_size(&self) -> usize { 0 } fn grow_to(&mut self, _new: usize) -> anyhow::Result<()> { anyhow::bail!("cannot grow memory") } fn as_ptr(&self) -> *mut u8 { std::ptr::null_mut() } fn maximum_byte_size(&self) -> Option<usize> { Some(0) } } impl Drop for MyMemory { fn drop(&mut self) { println!("Dropped!") } } struct MyMemoryCreator {} unsafe impl MemoryCreator for MyMemoryCreator { fn new_memory( &self, _ty: MemoryType, _minimum: usize, _maximum: Option<usize>, _reserved_size_in_bytes: Option<usize>, _guard_size_in_bytes: usize, ) -> Result<Box<dyn LinearMemory>, String> { Ok(Box::new(MyMemory {})) } } fn main() { let mut config = Config::new(); config.with_host_memory(std::sync::Arc::new(MyMemoryCreator {})); let engine = Engine::new(&config).unwrap(); let mut store = Store::new(&engine, ()); let mut linker = Linker::<()>::new(&engine); let memory_ty = MemoryType::new(1, None); let memory = Memory::new(&mut store, memory_ty).unwrap(); let captured_memory = memory.clone(); linker .func_wrap("", "", move || { let _ = captured_memory; }) .unwrap(); }
alexcrichton commented on issue #2583:
Ah yes indeed, this has been fixed!
alexcrichton closed issue #2583:
Hi, I'm using
wasmtime = 0.21and run into a bug where theMemoryis never dropped if moved inside a closure passed toLinker::func.Here is a minimal example:
use wasmtime::*; struct MyMemory {} unsafe impl LinearMemory for MyMemory { fn size(&self) -> u32 { 0 } fn grow(&self, _delta: u32) -> Option<u32> { None } fn as_ptr(&self) -> *mut u8 { std::ptr::null_mut() } } impl Drop for MyMemory { fn drop(&mut self) { println!("Dropped!") } } struct MyMemoryCreator {} unsafe impl MemoryCreator for MyMemoryCreator { fn new_memory( &self, _ty: MemoryType, _reserved_size_in_bytes: Option<u64>, _guard_size_in_bytes: u64, ) -> Result<Box<dyn LinearMemory>, String> { Ok(Box::new(MyMemory {})) } } fn main() { let mut config = Config::new(); config.with_host_memory(std::sync::Arc::new(MyMemoryCreator {})); let engine = Engine::new(&config); let store = Store::new(&engine); let mut linker = Linker::new(&store); let memory_ty = MemoryType::new(Limits::new(1, None)); let memory = Memory::new(&store, memory_ty); let captured_memory = memory.clone(); linker .func("", "", move || { let _ = captured_memory; }) .unwrap(); }
MyMemoryandMyMemoryCreatorare added here only for the purpose of printing out on drop, but the same behaviour is observed with the defaultMemory.In this example "Dropped!" is never printed out, but if I comment out
let _ = captured_memory;it is. I assume there is somewhere a cycle in reference counting.I'm also aware that the instance memory can be accessed through the first argument (
Caller), but because of some other design decisions I prefer capturing some memories this way. Would this be considered a bug or aMemoryshould never be captured like this?
Last updated: Dec 13 2025 at 19:03 UTC