Skip to content

Commit f1450b8

Browse files
committed
lower host stream/future writes in background task
This avoids unsoundness due to guest realloc calls while there are host embedder frames on the stack. Signed-off-by: Joel Dice <joel.dice@fermyon.com>
1 parent 4194e0a commit f1450b8

File tree

1 file changed

+53
-26
lines changed

1 file changed

+53
-26
lines changed

crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use super::{
33
Event, GlobalErrorContextRefCount, LocalErrorContextRefCount, StateTable, Waitable,
44
WaitableCommon, WaitableState,
55
};
6-
use crate::component::concurrent::ConcurrentState;
6+
use crate::component::concurrent::{ConcurrentState, HostTaskOutput, tls};
77
use crate::component::func::{self, LiftContext, LowerContext, Options};
88
use crate::component::matching::InstanceType;
99
use crate::component::values::{ErrorContextAny, FutureAny, StreamAny};
@@ -1746,34 +1746,61 @@ impl Instance {
17461746
}
17471747

17481748
let read_handle = transmit.read_handle;
1749-
let (result, code) = accept_reader::<T, B, U>(
1750-
store.as_context_mut(),
1751-
self,
1752-
Reader::Guest {
1753-
options: &options,
1754-
ty,
1755-
address,
1756-
count,
1757-
},
1758-
buffer,
1759-
kind,
1760-
)?;
1761-
1762-
self.concurrent_state_mut(store.0).set_event(
1763-
read_handle.rep(),
1764-
match ty {
1765-
TableIndex::Future(ty) => Event::FutureRead {
1766-
code,
1767-
pending: Some((ty, handle)),
1749+
let accept = move |mut store: StoreContextMut<U>| {
1750+
let (result, code) = accept_reader::<T, B, U>(
1751+
store.as_context_mut(),
1752+
self,
1753+
Reader::Guest {
1754+
options: &options,
1755+
ty,
1756+
address,
1757+
count,
17681758
},
1769-
TableIndex::Stream(ty) => Event::StreamRead {
1770-
code,
1771-
pending: Some((ty, handle)),
1759+
buffer,
1760+
kind,
1761+
)?;
1762+
1763+
self.concurrent_state_mut(store.0).set_event(
1764+
read_handle.rep(),
1765+
match ty {
1766+
TableIndex::Future(ty) => Event::FutureRead {
1767+
code,
1768+
pending: Some((ty, handle)),
1769+
},
1770+
TableIndex::Stream(ty) => Event::StreamRead {
1771+
code,
1772+
pending: Some((ty, handle)),
1773+
},
17721774
},
1773-
},
1774-
)?;
1775+
)?;
17751776

1776-
Ok(result)
1777+
anyhow::Ok(result)
1778+
};
1779+
1780+
if
1781+
// TODO: Check if payload is "flat"
1782+
false {
1783+
// Optimize flat payloads (i.e. those which do not require
1784+
// calling the guest's realloc function) by lowering
1785+
// directly instead of using a oneshot::channel and
1786+
// background task.
1787+
Ok(accept(store)?)
1788+
} else {
1789+
// Otherwise, for payloads which may require a realloc call,
1790+
// use a oneshot::channel and background task. This is
1791+
// necessary because calling the guest while there are host
1792+
// embedder frames on the stack is unsound.
1793+
let (tx, rx) = oneshot::channel();
1794+
let token = StoreToken::new(store.as_context_mut());
1795+
self.concurrent_state_mut(store.0)
1796+
.push_future(Box::pin(async move {
1797+
HostTaskOutput::Result(tls::get(|store| {
1798+
_ = tx.send(accept(token.as_context_mut(store))?);
1799+
Ok(())
1800+
}))
1801+
}));
1802+
Err(rx)
1803+
}
17771804
}
17781805

17791806
ReadState::HostReady { accept } => {

0 commit comments

Comments
 (0)