Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

c-api: Expose host memory creation #7115

Merged
merged 4 commits into from Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
86 changes: 86 additions & 0 deletions crates/c-api/include/wasmtime/config.h
Expand Up @@ -367,6 +367,92 @@ 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 created from a #wasmtime_new_memory_t callback.
*
* 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 {
void *env;
wasmtime_memory_get_callback_t get_memory;
wasmtime_memory_grow_callback_t grow_memory;
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 {
void* env;
wasmtime_new_memory_callback_t new_memory;
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
Expand Down
158 changes: 155 additions & 3 deletions crates/c-api/src/config.rs
Expand Up @@ -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)]
Expand Down Expand Up @@ -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<Box<wasmtime_error_t>>;

#[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<extern "C" fn(arg1: *mut std::ffi::c_void)>,
}

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<Box<wasmtime_error_t>>;

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<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);
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<usize> {
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<extern "C" fn(arg1: *mut std::ffi::c_void)>,
}

struct CHostMemoryCreator {
foreign: crate::ForeignData,
new_memory: wasmtime_new_memory_callback_t,
}
unsafe impl Send for CHostMemoryCreator {}
unsafe impl Sync for CHostMemoryCreator {}
rockwotj marked this conversation as resolved.
Show resolved Hide resolved

unsafe impl MemoryCreator for CHostMemoryCreator {
fn new_memory(
&self,
ty: wasmtime::MemoryType,
minimum: usize,
maximum: Option<usize>,
reserved_size_in_bytes: Option<usize>,
guard_size_in_bytes: usize,
) -> Result<Box<dyn wasmtime::LinearMemory>, 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,
}));
}