bkolobara labeled Issue #2583:
Hi, I'm using
wasmtime = 0.21
and run into a bug where theMemory
is 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(); }
MyMemory
andMyMemoryCreator
are 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 aMemory
should never be captured like this?
bkolobara opened Issue #2583:
Hi, I'm using
wasmtime = 0.21
and run into a bug where theMemory
is 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(); }
MyMemory
andMyMemoryCreator
are 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 aMemory
should never be captured like this?
alexcrichton commented on Issue #2583:
This is unfortunately expected behavior right now, albeit one we may want to investigate fixing with additional APIs. The problem is that you're creating a cycle of
Rc
(Store
ownsFunc
data which ownsMemory
which owns the sameStore
) which never gets freed. This is a known drawback ofRc
in Rust, but currently a design decision in Wasmtime.Ideally if you can the best fix would be to use the
Caller
structure which handles this correctly. Otherwise we may need to provide "weak" forms of things likeMemory
so you can close over the weak verisons without inducing a reference cycle.
Last updated: Dec 23 2024 at 12:05 UTC