Skip to content

Commit 260a6dd

Browse files
committed
Enable wasmtime async support
This is needed to allow for some future features such as cooperative multi-tasking via fuel or epoch interruption. Signed-off-by: Lann Martin <lann.martin@fermyon.com>
1 parent 6674085 commit 260a6dd

File tree

25 files changed

+161
-148
lines changed

25 files changed

+161
-148
lines changed

Cargo.lock

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/config/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ authors = [ "Fermyon Engineering <engineering@fermyon.com>" ]
88
anyhow = "1.0"
99
serde = { version = "1.0", features = [ "derive" ] }
1010
thiserror = "1"
11-
wit-bindgen-wasmtime = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba" }
11+
tokio = { version = "1", features = [ "rt-multi-thread" ] }
12+
13+
[dependencies.wit-bindgen-wasmtime]
14+
git = "https://github.com/bytecodealliance/wit-bindgen"
15+
rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba"
16+
features = ["async"]
1217

1318
[dev-dependencies]
1419
toml = "0.5"

crates/config/src/host_component.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ use std::sync::Arc;
33
use crate::{Error, Key, Resolver, TreePath};
44

55
mod wit {
6-
wit_bindgen_wasmtime::export!("../../wit/ephemeral/spin-config.wit");
6+
wit_bindgen_wasmtime::export!({paths: ["../../wit/ephemeral/spin-config.wit"], async: *});
77
}
88
pub use wit::spin_config::add_to_linker;
9+
use wit_bindgen_wasmtime::async_trait;
910

1011
/// A component configuration interface implementation.
1112
pub struct ComponentConfig {
@@ -26,11 +27,13 @@ impl ComponentConfig {
2627
}
2728
}
2829

30+
#[async_trait]
2931
impl wit::spin_config::SpinConfig for ComponentConfig {
30-
fn get_config(&mut self, key: &str) -> Result<String, wit::spin_config::Error> {
32+
async fn get_config(&mut self, key: &str) -> Result<String, wit::spin_config::Error> {
3133
let key = Key::new(key)?;
3234
let path = &self.component_root + key;
33-
Ok(self.resolver.resolve(&path)?)
35+
// TODO(lann): Make resolve async
36+
tokio::task::block_in_place(|| Ok(self.resolver.resolve(&path)?))
3437
}
3538
}
3639

crates/engine/Cargo.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ tracing = { version = "0.1", features = [ "log" ] }
1717
tracing-futures = "0.2"
1818
wasi-cap-std-sync = "0.39.1"
1919
wasi-common = "0.39.1"
20-
wasmtime = "0.39.1"
20+
wasmtime = { version = "0.39.1", features = [ "async" ] }
2121
wasmtime-wasi = "0.39.1"
2222
cap-std = "0.24.1"
2323

24-
[dev-dependencies]
25-
wit-bindgen-wasmtime = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba" }
24+
[dev-dependencies.wit-bindgen-wasmtime]
25+
git = "https://github.com/bytecodealliance/wit-bindgen"
26+
rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba"
27+
features = [ "async" ]

crates/engine/src/host_component.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub trait HostComponent: Send + Sync {
1212
type State: Any + Send;
1313

1414
/// Add this component to the given Linker, using the given runtime state-getting handle.
15-
fn add_to_linker<T>(
15+
fn add_to_linker<T: Send>(
1616
linker: &mut Linker<RuntimeContext<T>>,
1717
state_handle: HostComponentsStateHandle<Self::State>,
1818
) -> Result<()>;
@@ -30,7 +30,7 @@ pub(crate) struct HostComponents {
3030
}
3131

3232
impl HostComponents {
33-
pub(crate) fn insert<T: 'static, Component: HostComponent + 'static>(
33+
pub(crate) fn insert<T: Send + 'static, Component: HostComponent + 'static>(
3434
&mut self,
3535
linker: &mut Linker<RuntimeContext<T>>,
3636
host_component: Component,

crates/engine/src/lib.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ pub struct Engine(wasmtime::Engine);
5959

6060
impl Engine {
6161
/// Create a new engine and initialize it with the given config.
62-
pub fn new(config: wasmtime::Config) -> Result<Self> {
62+
pub fn new(mut config: wasmtime::Config) -> Result<Self> {
63+
config.async_support(true);
6364
Ok(Self(wasmtime::Engine::new(&config)?))
6465
}
6566

@@ -80,7 +81,7 @@ pub struct Builder<T: Default> {
8081
host_components: HostComponents,
8182
}
8283

83-
impl<T: Default + 'static> Builder<T> {
84+
impl<T: Default + Send + 'static> Builder<T> {
8485
/// Creates a new instance of the execution builder.
8586
pub fn new(config: ExecutionContextConfiguration) -> Result<Builder<T>> {
8687
Self::with_engine(config, Engine::new(Default::default())?)
@@ -216,10 +217,10 @@ pub struct ExecutionContext<T: Default> {
216217
host_components: Arc<HostComponents>,
217218
}
218219

219-
impl<T: Default> ExecutionContext<T> {
220+
impl<T: Default + Send> ExecutionContext<T> {
220221
/// Creates a store for a given component given its configuration and runtime data.
221222
#[instrument(skip(self, data, io))]
222-
pub fn prepare_component(
223+
pub async fn prepare_component(
223224
&self,
224225
component: &str,
225226
data: Option<T>,
@@ -234,7 +235,7 @@ impl<T: Default> ExecutionContext<T> {
234235
};
235236

236237
let mut store = self.store(component, data, io, env, args)?;
237-
let instance = component.pre.instantiate(&mut store)?;
238+
let instance = component.pre.instantiate_async(&mut store).await?;
238239

239240
Ok((store, instance))
240241
}

crates/http/Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,13 @@ tracing-subscriber = { version = "0.3.7", features = ["env-filter"] }
3939
url = "2.2"
4040
wasi-cap-std-sync = "0.39.1"
4141
wasi-common = "0.39.1"
42-
wasmtime = "0.39.1"
42+
wasmtime = { version = "0.39.1", features = ["async"] }
4343
wasmtime-wasi = "0.39.1"
44-
wit-bindgen-wasmtime = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba" }
44+
45+
[dependencies.wit-bindgen-wasmtime]
46+
git = "https://github.com/bytecodealliance/wit-bindgen"
47+
rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba"
48+
features = ["async"]
4549

4650
[dev-dependencies]
4751
criterion = { version = "0.3.5", features = ["async_tokio"] }

crates/http/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use crate::{
3333
wagi::WagiHttpExecutor,
3434
};
3535

36-
wit_bindgen_wasmtime::import!("../../wit/ephemeral/spin-http.wit");
36+
wit_bindgen_wasmtime::import!({paths: ["../../wit/ephemeral/spin-http.wit"], async: *});
3737

3838
type ExecutionContext = spin_engine::ExecutionContext<SpinHttpData>;
3939
type RuntimeContext = spin_engine::RuntimeContext<SpinHttpData>;

crates/http/src/spin.rs

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use async_trait::async_trait;
77
use hyper::{Body, Request, Response};
88
use spin_engine::io::ModuleIoRedirects;
99
use std::{net::SocketAddr, str, str::FromStr};
10-
use tokio::task::spawn_blocking;
1110
use tracing::log;
1211
use wasmtime::{Instance, Store};
1312

@@ -33,8 +32,9 @@ impl HttpExecutor for SpinHttpExecutor {
3332

3433
let mior = ModuleIoRedirects::new(follow);
3534

36-
let (store, instance) =
37-
engine.prepare_component(component, None, Some(mior.pipes), None, None)?;
35+
let (store, instance) = engine
36+
.prepare_component(component, None, Some(mior.pipes), None, None)
37+
.await?;
3838

3939
let resp_result = Self::execute_impl(store, instance, base, raw_route, req)
4040
.await
@@ -76,50 +76,47 @@ impl SpinHttpExecutor {
7676
let (parts, bytes) = req.into_parts();
7777
let bytes = hyper::body::to_bytes(bytes).await?.to_vec();
7878

79-
let res = spawn_blocking(move || -> Result<crate::spin_http::Response> {
80-
let method = Self::method(&parts.method);
81-
82-
let headers: Vec<(&str, &str)> = headers
83-
.iter()
84-
.map(|(k, v)| (k.as_str(), v.as_str()))
85-
.collect();
86-
87-
// Preparing to remove the params field. We are leaving it in place for now
88-
// to avoid breaking the ABI, but no longer pass or accept values in it.
89-
// https://github.com/fermyon/spin/issues/663
90-
let params = vec![];
91-
92-
let body = Some(&bytes[..]);
93-
let uri = match parts.uri.path_and_query() {
94-
Some(u) => u.to_string(),
95-
None => parts.uri.to_string(),
96-
};
97-
98-
let req = crate::spin_http::Request {
99-
method,
100-
uri: &uri,
101-
headers: &headers,
102-
params: &params,
103-
body,
104-
};
105-
106-
Ok(engine.handle_http_request(&mut store, req)?)
107-
})
108-
.await??;
109-
110-
if res.status < 100 || res.status > 600 {
79+
let method = Self::method(&parts.method);
80+
81+
let headers: Vec<(&str, &str)> = headers
82+
.iter()
83+
.map(|(k, v)| (k.as_str(), v.as_str()))
84+
.collect();
85+
86+
// Preparing to remove the params field. We are leaving it in place for now
87+
// to avoid breaking the ABI, but no longer pass or accept values in it.
88+
// https://github.com/fermyon/spin/issues/663
89+
let params = vec![];
90+
91+
let body = Some(&bytes[..]);
92+
let uri = match parts.uri.path_and_query() {
93+
Some(u) => u.to_string(),
94+
None => parts.uri.to_string(),
95+
};
96+
97+
let req = crate::spin_http::Request {
98+
method,
99+
uri: &uri,
100+
headers: &headers,
101+
params: &params,
102+
body,
103+
};
104+
105+
let resp = engine.handle_http_request(&mut store, req).await?;
106+
107+
if resp.status < 100 || resp.status > 600 {
111108
log::error!("malformed HTTP status code");
112109
return Ok(Response::builder()
113110
.status(http::StatusCode::INTERNAL_SERVER_ERROR)
114111
.body(Body::empty())?);
115112
};
116113

117-
let mut response = http::Response::builder().status(res.status);
114+
let mut response = http::Response::builder().status(resp.status);
118115
if let Some(headers) = response.headers_mut() {
119-
Self::append_headers(headers, res.headers)?;
116+
Self::append_headers(headers, resp.headers)?;
120117
}
121118

122-
let body = match res.body {
119+
let body = match resp.body {
123120
Some(b) => Body::from(b),
124121
None => Body::empty(),
125122
};

crates/http/src/wagi.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use std::{
1212
net::SocketAddr,
1313
sync::{Arc, RwLock, RwLockReadGuard},
1414
};
15-
use tokio::task::spawn_blocking;
1615
use tracing::log;
1716
use wasi_common::pipe::{ReadPipe, WritePipe};
1817

@@ -84,13 +83,15 @@ impl HttpExecutor for WagiHttpExecutor {
8483
headers.insert(keys[1].to_string(), val);
8584
}
8685

87-
let (mut store, instance) = engine.prepare_component(
88-
component,
89-
None,
90-
Some(redirects),
91-
Some(headers),
92-
Some(argv.split(' ').map(|s| s.to_owned()).collect()),
93-
)?;
86+
let (mut store, instance) = engine
87+
.prepare_component(
88+
component,
89+
None,
90+
Some(redirects),
91+
Some(headers),
92+
Some(argv.split(' ').map(|s| s.to_owned()).collect()),
93+
)
94+
.await?;
9495

9596
let start = instance
9697
.get_func(&mut store, &self.wagi_config.entrypoint)
@@ -102,7 +103,7 @@ impl HttpExecutor for WagiHttpExecutor {
102103
)
103104
})?;
104105
tracing::trace!("Calling Wasm entry point");
105-
let guest_result = spawn_blocking(move || start.call(&mut store, &[], &mut [])).await;
106+
let guest_result = start.call_async(&mut store, &[], &mut []).await;
106107
tracing::info!("Module execution complete");
107108

108109
let log_result = engine.save_output_to_logs(outputs.read(), component, false, true);
@@ -111,7 +112,7 @@ impl HttpExecutor for WagiHttpExecutor {
111112
// even if the guest code fails. (And when checking, check the guest
112113
// result first, so that guest failures are returned in preference to
113114
// log failures.)
114-
guest_result?.or_else(ignore_successful_proc_exit_trap)?;
115+
guest_result.or_else(ignore_successful_proc_exit_trap)?;
115116
log_result?;
116117

117118
let stdout = outputs.stdout.read().unwrap();

0 commit comments

Comments
 (0)