diff --git a/crates/c-api/include/wasmtime/config.h b/crates/c-api/include/wasmtime/config.h index 6ecebe7c04c7..38f1edc1b504 100644 --- a/crates/c-api/include/wasmtime/config.h +++ b/crates/c-api/include/wasmtime/config.h @@ -367,6 +367,99 @@ WASM_API_EXTERN void wasmtime_config_cranelift_flag_enable(wasm_config_t*, const */ WASM_API_EXTERN void wasmtime_config_cranelift_flag_set(wasm_config_t*, const char *key, const char *value); + +/** + * Return the data from a LinearMemory instance. + * + * The size in bytes as well as the maximum number of bytes that can be allocated should be + * returned as well. + * + * For more information about see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.LinearMemory.html + */ +typedef uint8_t *(*wasmtime_memory_get_callback_t)( + void *env, + size_t *byte_size, + size_t *maximum_byte_size); + +/** + * Grow the memory to the `new_size` in bytes. + * + * For more information about the parameters see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.LinearMemory.html#tymethod.grow_to + */ +typedef wasmtime_error_t *(*wasmtime_memory_grow_callback_t)( + void *env, + size_t new_size); + +/** + * A LinearMemory instance created from a #wasmtime_new_memory_callback_t. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.LinearMemory.html + */ +typedef struct { + /// User provided value to be passed to get_memory and grow_memory + void *env; + /// Callback to get the memory and size of this LinearMemory + wasmtime_memory_get_callback_t get_memory; + /// Callback to request growing the memory + wasmtime_memory_grow_callback_t grow_memory; + /// An optional finalizer for env + void (*finalizer)(void*); +} wasmtime_linear_memory_t; + +/** + * A callback to create a new LinearMemory from the specified parameters. + * + * The result should be written to `memory_ret` and wasmtime will own the values written + * into that struct. + * + * This callback must be thread-safe. + * + * For more information about the parameters see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.MemoryCreator.html#tymethod.new_memory + */ +typedef wasmtime_error_t *(*wasmtime_new_memory_callback_t)( + void *env, + const wasm_memorytype_t *ty, + size_t minimum, + size_t maximum, + size_t reserved_size_in_bytes, + size_t guard_size_in_bytes, + wasmtime_linear_memory_t *memory_ret); + +/** + * A representation of custom memory creator and methods for an instance of LinearMemory. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.MemoryCreator.html + */ +typedef struct { + /// User provided value to be passed to new_memory + void* env; + /// The callback to create new memory, must be thread safe + wasmtime_new_memory_callback_t new_memory; + /// An optional finalizer for env. + void (*finalizer)(void*); +} wasmtime_memory_creator_t; + +/** + * Sets a custom memory creator. + * + * Custom memory creators are used when creating host Memory objects or when creating instance + * linear memories for the on-demand instance allocation strategy. + * + * The config does **not** take ownership of the #wasmtime_memory_creator_t passed in, but + * instead copies all the values in the struct. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.with_host_memory + */ +WASM_API_EXTERN void wasmtime_config_host_memory_creator_set( + wasm_config_t*, + wasmtime_memory_creator_t*); + #ifdef __cplusplus } // extern "C" #endif diff --git a/crates/c-api/src/config.rs b/crates/c-api/src/config.rs index fcd9fb4166f3..76fe4546b69b 100644 --- a/crates/c-api/src/config.rs +++ b/crates/c-api/src/config.rs @@ -2,10 +2,14 @@ // them with the default set of features enabled. #![cfg_attr(not(feature = "cache"), allow(unused_imports))] -use crate::{handle_result, wasmtime_error_t}; -use std::ffi::CStr; +use crate::{handle_result, wasm_memorytype_t, wasmtime_error_t}; +use std::mem::MaybeUninit; +use std::ops::Range; use std::os::raw::c_char; -use wasmtime::{Config, OptLevel, ProfilingStrategy, Strategy}; +use std::{ffi::CStr, sync::Arc}; +use wasmtime::{ + Config, LinearMemory, MemoryCreator, OptLevel, ProfilingStrategy, Result, Strategy, +}; #[repr(C)] #[derive(Clone)] @@ -255,3 +259,151 @@ pub unsafe extern "C" fn wasmtime_config_cranelift_flag_set( let value = CStr::from_ptr(value).to_str().expect("not valid utf-8"); c.config.cranelift_flag_set(flag, value); } + +pub type wasmtime_memory_get_callback_t = extern "C" fn( + env: *mut std::ffi::c_void, + byte_size: &mut usize, + maximum_byte_size: &mut usize, +) -> *mut u8; + +pub type wasmtime_memory_grow_callback_t = + extern "C" fn(env: *mut std::ffi::c_void, new_size: usize) -> Option>; + +#[repr(C)] +pub struct wasmtime_linear_memory_t { + env: *mut std::ffi::c_void, + get_memory: wasmtime_memory_get_callback_t, + grow_memory: wasmtime_memory_grow_callback_t, + finalizer: Option, +} + +pub type wasmtime_new_memory_callback_t = extern "C" fn( + env: *mut std::ffi::c_void, + ty: &wasm_memorytype_t, + minimum: usize, + maximum: usize, + reserved_size_in_bytes: usize, + guard_size_in_bytes: usize, + memory_ret: *mut wasmtime_linear_memory_t, +) -> Option>; + +struct CHostLinearMemory { + foreign: crate::ForeignData, + get_memory: wasmtime_memory_get_callback_t, + grow_memory: wasmtime_memory_grow_callback_t, +} + +unsafe impl LinearMemory for CHostLinearMemory { + fn byte_size(&self) -> usize { + let mut byte_size = 0; + let mut maximum_byte_size = 0; + let cb = self.get_memory; + cb(self.foreign.data, &mut byte_size, &mut maximum_byte_size); + return byte_size; + } + fn maximum_byte_size(&self) -> Option { + let mut byte_size = 0; + let mut maximum_byte_size = 0; + let cb = self.get_memory; + cb(self.foreign.data, &mut byte_size, &mut maximum_byte_size); + if maximum_byte_size == 0 { + None + } else { + Some(maximum_byte_size) + } + } + fn as_ptr(&self) -> *mut u8 { + let mut byte_size = 0; + let mut maximum_byte_size = 0; + let cb = self.get_memory; + cb(self.foreign.data, &mut byte_size, &mut maximum_byte_size) + } + fn wasm_accessible(&self) -> Range { + let mut byte_size = 0; + let mut maximum_byte_size = 0; + let cb = self.get_memory; + let ptr = cb(self.foreign.data, &mut byte_size, &mut maximum_byte_size); + Range { + start: ptr as usize, + end: ptr as usize + byte_size, + } + } + fn grow_to(&mut self, new_size: usize) -> Result<()> { + let cb = self.grow_memory; + let error = cb(self.foreign.data, new_size); + if let Some(err) = error { + Err((*err).into()) + } else { + Ok(()) + } + } +} + +#[repr(C)] +pub struct wasmtime_memory_creator_t { + env: *mut std::ffi::c_void, + new_memory: wasmtime_new_memory_callback_t, + finalizer: Option, +} + +struct CHostMemoryCreator { + foreign: crate::ForeignData, + new_memory: wasmtime_new_memory_callback_t, +} +unsafe impl Send for CHostMemoryCreator {} +unsafe impl Sync for CHostMemoryCreator {} + +unsafe impl MemoryCreator for CHostMemoryCreator { + fn new_memory( + &self, + ty: wasmtime::MemoryType, + minimum: usize, + maximum: Option, + reserved_size_in_bytes: Option, + guard_size_in_bytes: usize, + ) -> Result, String> { + let mut memory = MaybeUninit::uninit(); + let cb = self.new_memory; + let error = cb( + self.foreign.data, + &wasm_memorytype_t::new(ty), + minimum, + maximum.unwrap_or(usize::MAX), + reserved_size_in_bytes.unwrap_or(0), + guard_size_in_bytes, + memory.as_mut_ptr(), + ); + match error { + None => { + let memory = unsafe { memory.assume_init() }; + let foreign = crate::ForeignData { + data: memory.env, + finalizer: memory.finalizer, + }; + Ok(Box::new(CHostLinearMemory { + foreign, + get_memory: memory.get_memory, + grow_memory: memory.grow_memory, + })) + } + Some(err) => { + let err: anyhow::Error = (*err).into(); + Err(format!("{}", err)) + } + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_config_host_memory_creator_set( + c: &mut wasm_config_t, + creator: &wasmtime_memory_creator_t, +) { + c.config.with_host_memory(Arc::new(CHostMemoryCreator { + foreign: crate::ForeignData { + data: creator.env, + finalizer: creator.finalizer, + }, + new_memory: creator.new_memory, + })); +}