Browse Source
* Option for host managed memory * Rename Allocator to MemoryCreator * Create LinearMemory and MemoryCreator traits in api * Leave only one as_ptr function in LinearMemory trait * Memory creator test * Update comments/docs for LinearMemory and MemoryCreator traits * Add guard page to the custom memory example * Remove mut from LinearMemory trait as_ptr * Host_memory_grow testpull/1477/head
Maciej Kot
5 years ago
committed by
GitHub
11 changed files with 356 additions and 17 deletions
@ -0,0 +1,192 @@ |
|||||
|
#[cfg(not(target_os = "windows"))] |
||||
|
mod not_for_windows { |
||||
|
use wasmtime::*; |
||||
|
use wasmtime_environ::{WASM_MAX_PAGES, WASM_PAGE_SIZE}; |
||||
|
|
||||
|
use libc::c_void; |
||||
|
use libc::MAP_FAILED; |
||||
|
use libc::{mmap, mprotect, munmap}; |
||||
|
use libc::{sysconf, _SC_PAGESIZE}; |
||||
|
use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; |
||||
|
|
||||
|
use std::cell::RefCell; |
||||
|
use std::io::Error; |
||||
|
use std::ptr::null_mut; |
||||
|
use std::sync::{Arc, Mutex}; |
||||
|
|
||||
|
struct CustomMemory { |
||||
|
mem: *mut c_void, |
||||
|
size: usize, |
||||
|
used_wasm_pages: RefCell<u32>, |
||||
|
glob_page_counter: Arc<Mutex<u64>>, |
||||
|
} |
||||
|
|
||||
|
impl CustomMemory { |
||||
|
unsafe fn new( |
||||
|
num_wasm_pages: u32, |
||||
|
max_wasm_pages: u32, |
||||
|
glob_counter: Arc<Mutex<u64>>, |
||||
|
) -> Self { |
||||
|
let page_size = sysconf(_SC_PAGESIZE) as usize; |
||||
|
let guard_size = page_size; |
||||
|
let size = max_wasm_pages as usize * WASM_PAGE_SIZE as usize + guard_size; |
||||
|
let used_size = num_wasm_pages as usize * WASM_PAGE_SIZE as usize; |
||||
|
assert_eq!(size % page_size, 0); // we rely on WASM_PAGE_SIZE being multiple of host page size
|
||||
|
|
||||
|
let mem = mmap(null_mut(), size, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); |
||||
|
assert_ne!(mem, MAP_FAILED, "mmap failed: {}", Error::last_os_error()); |
||||
|
|
||||
|
let r = mprotect(mem, used_size, PROT_READ | PROT_WRITE); |
||||
|
assert_eq!(r, 0, "mprotect failed: {}", Error::last_os_error()); |
||||
|
*glob_counter.lock().unwrap() += num_wasm_pages as u64; |
||||
|
|
||||
|
Self { |
||||
|
mem, |
||||
|
size, |
||||
|
used_wasm_pages: RefCell::new(num_wasm_pages), |
||||
|
glob_page_counter: glob_counter, |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
impl Drop for CustomMemory { |
||||
|
fn drop(&mut self) { |
||||
|
let n = *self.used_wasm_pages.borrow() as u64; |
||||
|
*self.glob_page_counter.lock().unwrap() -= n; |
||||
|
let r = unsafe { munmap(self.mem, self.size) }; |
||||
|
assert_eq!(r, 0, "munmap failed: {}", Error::last_os_error()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
unsafe impl LinearMemory for CustomMemory { |
||||
|
fn size(&self) -> u32 { |
||||
|
*self.used_wasm_pages.borrow() |
||||
|
} |
||||
|
|
||||
|
fn grow(&self, delta: u32) -> Option<u32> { |
||||
|
let delta_size = (delta as usize).checked_mul(WASM_PAGE_SIZE as usize)?; |
||||
|
|
||||
|
let prev_pages = *self.used_wasm_pages.borrow(); |
||||
|
let prev_size = (prev_pages as usize).checked_mul(WASM_PAGE_SIZE as usize)?; |
||||
|
|
||||
|
let new_pages = prev_pages.checked_add(delta)?; |
||||
|
let new_size = (new_pages as usize).checked_mul(WASM_PAGE_SIZE as usize)?; |
||||
|
|
||||
|
let guard_size = unsafe { sysconf(_SC_PAGESIZE) as usize }; |
||||
|
|
||||
|
if new_size > self.size - guard_size { |
||||
|
return None; |
||||
|
} |
||||
|
unsafe { |
||||
|
let start = (self.mem as *mut u8).add(prev_size) as _; |
||||
|
let r = mprotect(start, delta_size, PROT_READ | PROT_WRITE); |
||||
|
assert_eq!(r, 0, "mprotect failed: {}", Error::last_os_error()); |
||||
|
} |
||||
|
|
||||
|
*self.glob_page_counter.lock().unwrap() += delta as u64; |
||||
|
*self.used_wasm_pages.borrow_mut() = new_pages; |
||||
|
Some(prev_pages) |
||||
|
} |
||||
|
|
||||
|
fn as_ptr(&self) -> *mut u8 { |
||||
|
self.mem as *mut u8 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct CustomMemoryCreator { |
||||
|
pub num_created_memories: Mutex<usize>, |
||||
|
pub num_total_pages: Arc<Mutex<u64>>, |
||||
|
} |
||||
|
|
||||
|
impl CustomMemoryCreator { |
||||
|
pub fn new() -> Self { |
||||
|
Self { |
||||
|
num_created_memories: Mutex::new(0), |
||||
|
num_total_pages: Arc::new(Mutex::new(0)), |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
unsafe impl MemoryCreator for CustomMemoryCreator { |
||||
|
fn new_memory(&self, ty: MemoryType) -> Result<Box<dyn LinearMemory>, String> { |
||||
|
let max = ty.limits().max().unwrap_or(WASM_MAX_PAGES); |
||||
|
unsafe { |
||||
|
let mem = Box::new(CustomMemory::new( |
||||
|
ty.limits().min(), |
||||
|
max, |
||||
|
self.num_total_pages.clone(), |
||||
|
)); |
||||
|
*self.num_created_memories.lock().unwrap() += 1; |
||||
|
Ok(mem) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn host_memory() -> anyhow::Result<()> { |
||||
|
let mem_creator = Arc::new(CustomMemoryCreator::new()); |
||||
|
let mut config = Config::default(); |
||||
|
config.with_host_memory(mem_creator.clone()); |
||||
|
let engine = Engine::new(&config); |
||||
|
let store = Store::new(&engine); |
||||
|
|
||||
|
let module = Module::new( |
||||
|
&store, |
||||
|
r#" |
||||
|
(module |
||||
|
(memory (export "memory") 1) |
||||
|
) |
||||
|
"#, |
||||
|
)?; |
||||
|
Instance::new(&module, &[])?; |
||||
|
|
||||
|
assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 1); |
||||
|
|
||||
|
Ok(()) |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn host_memory_grow() -> anyhow::Result<()> { |
||||
|
let mem_creator = Arc::new(CustomMemoryCreator::new()); |
||||
|
let mut config = Config::default(); |
||||
|
config.with_host_memory(mem_creator.clone()); |
||||
|
let engine = Engine::new(&config); |
||||
|
let store = Store::new(&engine); |
||||
|
|
||||
|
let module = Module::new( |
||||
|
&store, |
||||
|
r#" |
||||
|
(module |
||||
|
(func $f (drop (memory.grow (i32.const 1)))) |
||||
|
(memory (export "memory") 1 2) |
||||
|
(start $f) |
||||
|
) |
||||
|
"#, |
||||
|
)?; |
||||
|
|
||||
|
let instance1 = Instance::new(&module, &[])?; |
||||
|
let instance2 = Instance::new(&module, &[])?; |
||||
|
|
||||
|
assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 2); |
||||
|
|
||||
|
assert_eq!( |
||||
|
instance2 |
||||
|
.get_export("memory") |
||||
|
.unwrap() |
||||
|
.memory() |
||||
|
.unwrap() |
||||
|
.size(), |
||||
|
2 |
||||
|
); |
||||
|
|
||||
|
// we take the lock outside the assert, so it won't get poisoned on assert failure
|
||||
|
let tot_pages = *mem_creator.num_total_pages.lock().unwrap(); |
||||
|
assert_eq!(tot_pages, 4); |
||||
|
|
||||
|
drop(instance1); |
||||
|
let tot_pages = *mem_creator.num_total_pages.lock().unwrap(); |
||||
|
assert_eq!(tot_pages, 2); |
||||
|
|
||||
|
Ok(()) |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue