Add async host functions

This commit is contained in:
Isaac Clayton 2022-06-13 16:06:39 +02:00
parent a5a0abb895
commit 4565f1a976
6 changed files with 96 additions and 91 deletions

View file

@ -115,11 +115,8 @@ pub fn import(args: TokenStream, function: TokenStream) -> TokenStream {
}) })
.unzip(); .unzip();
dbg!("hello");
let body = TokenStream::from(quote! { let body = TokenStream::from(quote! {
{ {
// dbg!("executing imported function");
// setup // setup
let data: (#( #tys ),*) = (#( #args ),*); let data: (#( #tys ),*) = (#( #args ),*);
let data = ::plugin::bincode::serialize(&data).unwrap(); let data = ::plugin::bincode::serialize(&data).unwrap();
@ -137,12 +134,8 @@ pub fn import(args: TokenStream, function: TokenStream) -> TokenStream {
} }
}); });
dbg!("hello2");
let block = parse_macro_input!(body as Block); let block = parse_macro_input!(body as Block);
dbg!("hello {:?}", &block);
let inner_fn = ItemFn { let inner_fn = ItemFn {
attrs: fn_declare.attrs, attrs: fn_declare.attrs,
vis: fn_declare.vis, vis: fn_declare.vis,

View file

@ -10,7 +10,7 @@ fn main() {
let _ = let _ =
std::fs::create_dir_all(base.join("bin")).expect("Could not make plugins bin directory"); std::fs::create_dir_all(base.join("bin")).expect("Could not make plugins bin directory");
std::process::Command::new("cargo") let build_successful = std::process::Command::new("cargo")
.args([ .args([
"build", "build",
"--release", "--release",
@ -20,7 +20,9 @@ fn main() {
base.join("Cargo.toml").to_str().unwrap(), base.join("Cargo.toml").to_str().unwrap(),
]) ])
.status() .status()
.expect("Could not build plugins"); .expect("Could not build plugins")
.success();
assert!(build_successful);
let binaries = std::fs::read_dir(base.join("target/wasm32-wasi/release")) let binaries = std::fs::read_dir(base.join("target/wasm32-wasi/release"))
.expect("Could not find compiled plugins in target"); .expect("Could not find compiled plugins in target");

View file

@ -18,12 +18,9 @@ mod tests {
print: WasiFn<String, ()>, print: WasiFn<String, ()>,
and_back: WasiFn<u32, u32>, and_back: WasiFn<u32, u32>,
imports: WasiFn<u32, u32>, imports: WasiFn<u32, u32>,
half_async: WasiFn<u32, u32>,
} }
// async fn half(a: u32) -> u32 {
// a / 2
// }
async { async {
let mut runtime = PluginBuilder::new_with_default_ctx() let mut runtime = PluginBuilder::new_with_default_ctx()
.unwrap() .unwrap()
@ -35,8 +32,8 @@ mod tests {
.unwrap() .unwrap()
.host_function("import_swap", |(a, b): (u32, u32)| (b, a)) .host_function("import_swap", |(a, b): (u32, u32)| (b, a))
.unwrap() .unwrap()
// .host_function_async("import_half", half) .host_function_async("import_half", |a: u32| async move { a / 2 })
// .unwrap() .unwrap()
.init(include_bytes!("../../../plugins/bin/test_plugin.wasm")) .init(include_bytes!("../../../plugins/bin/test_plugin.wasm"))
.await .await
.unwrap(); .unwrap();
@ -51,6 +48,7 @@ mod tests {
print: runtime.function("print").unwrap(), print: runtime.function("print").unwrap(),
and_back: runtime.function("and_back").unwrap(), and_back: runtime.function("and_back").unwrap(),
imports: runtime.function("imports").unwrap(), imports: runtime.function("imports").unwrap(),
half_async: runtime.function("half_async").unwrap(),
}; };
let unsorted = vec![1, 3, 4, 2, 5]; let unsorted = vec![1, 3, 4, 2, 5];
@ -65,6 +63,7 @@ mod tests {
assert_eq!(runtime.call(&plugin.print, "Hi!".into()).await.unwrap(), ()); assert_eq!(runtime.call(&plugin.print, "Hi!".into()).await.unwrap(), ());
assert_eq!(runtime.call(&plugin.and_back, 1).await.unwrap(), 8); assert_eq!(runtime.call(&plugin.and_back, 1).await.unwrap(), 8);
assert_eq!(runtime.call(&plugin.imports, 1).await.unwrap(), 8); assert_eq!(runtime.call(&plugin.imports, 1).await.unwrap(), 8);
assert_eq!(runtime.call(&plugin.half_async, 4).await.unwrap(), 2);
// dbg!("{}", runtime.call(&plugin.and_back, 1).await.unwrap()); // dbg!("{}", runtime.call(&plugin.and_back, 1).await.unwrap());
} }

View file

@ -1,3 +1,5 @@
use std::future::Future;
use std::pin::Pin;
use std::{fs::File, marker::PhantomData, path::Path}; use std::{fs::File, marker::PhantomData, path::Path};
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
@ -142,7 +144,7 @@ impl PluginBuilder {
// move |_: Caller<'_, WasiCtxAlloc>, _: u64| { // move |_: Caller<'_, WasiCtxAlloc>, _: u64| {
// // let function = &function; // // let function = &function;
// Box::new(async { // Box::new(async {
// let function = function; // // let function = function;
// // Call the Host-side function // // Call the Host-side function
// let result: u64 = function(7).await; // let result: u64 = function(7).await;
// Ok(result) // Ok(result)
@ -152,68 +154,69 @@ impl PluginBuilder {
// Ok(self) // Ok(self)
// } // }
// pub fn host_function_async<F, A, R>(mut self, name: &str, function: F) -> Result<Self, Error> pub fn host_function_async<F, A, R, Fut>(
// where mut self,
// F: Fn(A) -> Pin<Box<dyn Future<Output = R> + Send + 'static>> + Send + Sync + 'static, name: &str,
// A: DeserializeOwned + Send, function: F,
// R: Serialize + Send + Sync, ) -> Result<Self, Error>
// { where
// self.linker.func_wrap1_async( F: Fn(A) -> Fut + Send + Sync + 'static,
// "env", Fut: Future<Output = R> + Send + 'static,
// &format!("__{}", name), A: DeserializeOwned + Send + 'static,
// move |mut caller: Caller<'_, WasiCtxAlloc>, packed_buffer: u64| { R: Serialize + Send + Sync + 'static,
// let function = |args: Vec<u8>| { {
// let args = args; self.linker.func_wrap1_async(
// let args: A = Wasi::deserialize_to_type(&args)?; "env",
// Ok(async { &format!("__{}", name),
// let result = function(args); move |mut caller: Caller<'_, WasiCtxAlloc>, packed_buffer: u64| {
// Wasi::serialize_to_bytes(result.await).map_err(|_| { // TODO: use try block once avaliable
// Trap::new("Could not serialize value returned from function").into() let result: Result<(WasiBuffer, Memory, _), Trap> = (|| {
// }) // grab a handle to the memory
// }) let mut plugin_memory = match caller.get_export("memory") {
// }; Some(Extern::Memory(mem)) => mem,
_ => return Err(Trap::new("Could not grab slice of plugin memory"))?,
};
// // TODO: use try block once avaliable let buffer = WasiBuffer::from_u64(packed_buffer);
// let result: Result<(WasiBuffer, Memory, _), Trap> = (|| {
// // grab a handle to the memory
// let mut plugin_memory = match caller.get_export("memory") {
// Some(Extern::Memory(mem)) => mem,
// _ => return Err(Trap::new("Could not grab slice of plugin memory"))?,
// };
// let buffer = WasiBuffer::from_u64(packed_buffer); // get the args passed from Guest
let args = Plugin::buffer_to_bytes(&mut plugin_memory, &mut caller, &buffer)?;
// // get the args passed from Guest let args: A = Plugin::deserialize_to_type(&args)?;
// let args = Wasi::buffer_to_bytes(&mut plugin_memory, &mut caller, &buffer)?;
// // Call the Host-side function // Call the Host-side function
// let result = function(args); let result = function(args);
// Ok((buffer, plugin_memory, result)) Ok((buffer, plugin_memory, result))
// })(); })();
// Box::new(async move { Box::new(async move {
// let (buffer, mut plugin_memory, thingo) = result?; let (buffer, mut plugin_memory, future) = result?;
// let thingo: Result<_, Error> = thingo;
// let result: Result<Vec<u8>, Error> = thingo?.await;
// // Wasi::buffer_to_free(caller.data().free_buffer(), &mut caller, buffer).await?; let result: R = future.await;
let result: Result<Vec<u8>, Error> = Plugin::serialize_to_bytes(result)
.map_err(|_| {
Trap::new("Could not serialize value returned from function").into()
});
let result = result?;
// // let buffer = Wasi::bytes_to_buffer( Plugin::buffer_to_free(caller.data().free_buffer(), &mut caller, buffer)
// // caller.data().alloc_buffer(), .await?;
// // &mut plugin_memory,
// // &mut caller,
// // result,
// // )
// // .await?;
// // Ok(buffer.into_u64()) let buffer = Plugin::bytes_to_buffer(
// Ok(27) caller.data().alloc_buffer(),
// }) &mut plugin_memory,
// }, &mut caller,
// )?; result,
// Ok(self) )
// } .await?;
Ok(buffer.into_u64())
})
},
)?;
Ok(self)
}
pub fn host_function<A, R>( pub fn host_function<A, R>(
mut self, mut self,

View file

@ -4,8 +4,8 @@ use serde_json::json;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
// #[import] #[import]
// fn command(string: &str) -> Option<String>; fn command(string: &str) -> Option<String>;
// #[no_mangle] // #[no_mangle]
// // TODO: switch len from usize to u32? // // TODO: switch len from usize to u32?
@ -28,29 +28,29 @@ use std::path::PathBuf;
// return new_buffer.leak_to_heap(); // return new_buffer.leak_to_heap();
// } // }
extern "C" { // extern "C" {
fn __command(buffer: u64) -> u64; // fn __command(buffer: u64) -> u64;
} // }
#[no_mangle] // #[no_mangle]
fn command(string: &str) -> Option<Vec<u8>> { // fn command(string: &str) -> Option<Vec<u8>> {
dbg!("executing command: {}", string); // dbg!("executing command: {}", string);
// setup // // setup
let data = string; // let data = string;
let data = ::plugin::bincode::serialize(&data).unwrap(); // let data = ::plugin::bincode::serialize(&data).unwrap();
let buffer = unsafe { ::plugin::__Buffer::from_vec(data) }; // let buffer = unsafe { ::plugin::__Buffer::from_vec(data) };
// operation // // operation
let new_buffer = unsafe { __command(buffer.into_u64()) }; // let new_buffer = unsafe { __command(buffer.into_u64()) };
let new_data = unsafe { ::plugin::__Buffer::from_u64(new_buffer).to_vec() }; // let new_data = unsafe { ::plugin::__Buffer::from_u64(new_buffer).to_vec() };
let new_data: Option<Vec<u8>> = match ::plugin::bincode::deserialize(&new_data) { // let new_data: Option<Vec<u8>> = match ::plugin::bincode::deserialize(&new_data) {
Ok(d) => d, // Ok(d) => d,
Err(e) => panic!("Data returned from function not deserializable."), // Err(e) => panic!("Data returned from function not deserializable."),
}; // };
// teardown // // teardown
return new_data; // return new_data;
} // }
// TODO: some sort of macro to generate ABI bindings // TODO: some sort of macro to generate ABI bindings
// extern "C" { // extern "C" {

View file

@ -61,3 +61,11 @@ pub fn imports(x: u32) -> u32 {
assert_eq!(x, b); assert_eq!(x, b);
a + b // should be 7 + x a + b // should be 7 + x
} }
#[import]
fn import_half(a: u32) -> u32;
#[export]
pub fn half_async(a: u32) -> u32 {
import_half(a)
}