diff --git a/ext/src/ruby_api/externals.rs b/ext/src/ruby_api/externals.rs index afbe07dc..34742a6e 100644 --- a/ext/src/ruby_api/externals.rs +++ b/ext/src/ruby_api/externals.rs @@ -221,7 +221,7 @@ impl<'a> WrapWasmtimeType<'a, Extern<'a>> for wasmtime::Extern { ruby.obj_wrap(Global::from_inner(store, *global)), )), wasmtime::Extern::Memory(mem) => Ok(Extern::Memory( - ruby.obj_wrap(Memory::from_inner(store, *mem)?), + ruby.obj_wrap(Memory::from_inner(store, *mem)), )), wasmtime::Extern::Table(table) => Ok(Extern::Table( ruby.obj_wrap(Table::from_inner(store, *table)), diff --git a/ext/src/ruby_api/memory.rs b/ext/src/ruby_api/memory.rs index f7417e2a..52b560ac 100644 --- a/ext/src/ruby_api/memory.rs +++ b/ext/src/ruby_api/memory.rs @@ -11,11 +11,8 @@ use magnus::{ DataTypeFunctions, Error, Module as _, Object, Ruby, TypedData, Value, }; -use rb_sys::tracking_allocator::ManuallyTracked; use wasmtime::{Extern, Memory as MemoryImpl}; -const WASM_PAGE_SIZE: u32 = wasmtime_environ::Memory::DEFAULT_PAGE_SIZE; - define_rb_intern!( MIN_SIZE => "min_size", MAX_SIZE => "max_size", @@ -68,7 +65,7 @@ impl From<&MemoryType> for wasmtime::ExternType { #[magnus(class = "Wasmtime::Memory", free_immediately, mark, unsafe_generics)] pub struct Memory<'a> { store: StoreContextValue<'a>, - inner: ManuallyTracked, + inner: MemoryImpl, } impl DataTypeFunctions for Memory<'_> { @@ -98,21 +95,15 @@ impl<'a> Memory<'a> { let memtype = wasmtime::MemoryType::new(min, max); let inner = MemoryImpl::new(store.context_mut(), memtype).map_err(|e| error!("{}", e))?; - let memsize = inner.data_size(store.context_mut()); Ok(Self { store: store.into(), - inner: ManuallyTracked::wrap(inner, memsize), + inner, }) } - pub fn from_inner(store: StoreContextValue<'a>, inner: MemoryImpl) -> Result { - let memsize = inner.data_size(store.context()?); - - Ok(Self { - store, - inner: ManuallyTracked::wrap(inner, memsize), - }) + pub fn from_inner(store: StoreContextValue<'a>, inner: MemoryImpl) -> Self { + Self { store, inner } } /// @yard @@ -411,15 +402,9 @@ impl<'a> Memory<'a> { /// @param delta [Integer] The number of pages to grow by. /// @return [Integer] The number of pages the memory had before being resized. pub fn grow(&self, delta: usize) -> Result { - let ret = self - .get_wasmtime_memory() + self.get_wasmtime_memory() .grow(self.store.context_mut()?, delta as _) - .map_err(|e| error!("{}", e)); - - self.inner - .increase_memory_usage(delta * (WASM_PAGE_SIZE as usize)); - - ret + .map_err(|e| error!("{}", e)) } /// @yard @@ -435,7 +420,7 @@ impl<'a> Memory<'a> { } pub fn get_wasmtime_memory(&self) -> &MemoryImpl { - self.inner.get() + &self.inner } fn data(&self) -> Result<&[u8], Error> { diff --git a/ext/src/ruby_api/store.rs b/ext/src/ruby_api/store.rs index 261b3d3a..822e22c2 100644 --- a/ext/src/ruby_api/store.rs +++ b/ext/src/ruby_api/store.rs @@ -509,16 +509,9 @@ impl ResourceLimiter for TrackingResourceLimiter { // Update max_linear_memory_consumed self.max_linear_memory_consumed = self.max_linear_memory_consumed.max(desired); - if res.is_ok() { - self.tracker.increase_memory_usage(desired - current); - } else { - self.linear_memory_limit_hit = true; - } - - if let Ok(allowed) = res { - if !allowed { - self.linear_memory_limit_hit = true; - } + match res { + Ok(true) => self.tracker.increase_memory_usage(desired - current), + Ok(false) | Err(_) => self.linear_memory_limit_hit = true, } res diff --git a/spec/unit/store_spec.rb b/spec/unit/store_spec.rb index 91b716bb..fc5eba94 100644 --- a/spec/unit/store_spec.rb +++ b/spec/unit/store_spec.rb @@ -144,6 +144,24 @@ module Wasmtime expect(store.max_linear_memory_consumed).to be >= 65536 * 2 end end + + describe "GC memory accounting" do + let(:pages) { 200 } + let(:bytes) { pages * 65_536 } + + it "reports linear memory to the GC exactly once (no double counting)" do + store = Store.new(engine, limits: {memory_size: bytes * 4}) + + GC.disable + before = GC.stat(:malloc_increase_bytes) + Memory.new(store, min_size: pages) + delta = GC.stat(:malloc_increase_bytes) - before + + expect(delta).to be_within(bytes / 2).of(bytes) + ensure + GC.enable + end + end end end end