Skip to content

Commit e13563e

Browse files
committed
enforce resource borrow requirements for async calls
Signed-off-by: Joel Dice <joel.dice@fermyon.com>
1 parent 0d23beb commit e13563e

File tree

10 files changed

+355
-27
lines changed

10 files changed

+355
-27
lines changed

Cargo.lock

Lines changed: 5 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,10 @@ io-lifetimes = { version = "2.0.3", default-features = false }
295295
io-extras = "0.18.1"
296296
rustix = "0.38.43"
297297
# wit-bindgen:
298-
wit-bindgen = { version = "0.37.0", default-features = false }
299-
wit-bindgen-rt = { version = "0.37.0", default-features = false }
300-
wit-bindgen-rust-macro = { version = "0.37.0", default-features = false }
298+
# TODO: switch to crates.io once https://github.com/bytecodealliance/wit-bindgen/pull/1124 has been released
299+
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", default-features = false }
300+
wit-bindgen-rt = { git = "https://github.com/bytecodealliance/wit-bindgen", default-features = false }
301+
wit-bindgen-rust-macro = { git = "https://github.com/bytecodealliance/wit-bindgen", default-features = false }
301302

302303
# wasm-tools family:
303304
wasmparser = { version = "0.223.0", default-features = false, features = ['simd'] }

crates/misc/component-async-tests/src/lib.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,26 @@ mod test {
9494
}
9595
}
9696

97+
pub struct MyX;
98+
99+
impl borrowing_host::local::local::borrowing_types::HostX for Ctx {
100+
fn new(&mut self) -> Result<Resource<MyX>> {
101+
Ok(WasiView::table(self).push(MyX)?)
102+
}
103+
104+
fn foo(&mut self, x: Resource<MyX>) -> Result<()> {
105+
_ = WasiView::table(self).get(&x)?;
106+
Ok(())
107+
}
108+
109+
fn drop(&mut self, x: Resource<MyX>) -> Result<()> {
110+
WasiView::table(self).delete(x)?;
111+
Ok(())
112+
}
113+
}
114+
115+
impl borrowing_host::local::local::borrowing_types::Host for Ctx {}
116+
97117
async fn test_round_trip(component: &[u8], input: &str, expected_output: &str) -> Result<()> {
98118
init_logger();
99119

@@ -401,6 +421,22 @@ mod test {
401421
});
402422
}
403423

424+
mod borrowing_host {
425+
wasmtime::component::bindgen!({
426+
path: "wit",
427+
world: "borrowing-host",
428+
trappable_imports: true,
429+
concurrent_imports: true,
430+
concurrent_exports: true,
431+
async: {
432+
only_imports: []
433+
},
434+
with: {
435+
"local:local/borrowing-types/x": super::MyX,
436+
}
437+
});
438+
}
439+
404440
impl yield_host::local::local::continue_::Host for Ctx {
405441
fn set_continue(&mut self, v: bool) {
406442
self.continue_ = v;
@@ -555,6 +591,100 @@ mod test {
555591
test_run(&compose(caller, callee).await?).await
556592
}
557593

594+
async fn test_run_bool(component: &[u8], v: bool) -> Result<()> {
595+
init_logger();
596+
597+
let mut config = Config::new();
598+
config.debug_info(true);
599+
config.cranelift_debug_verifier(true);
600+
config.wasm_component_model(true);
601+
config.wasm_component_model_async(true);
602+
config.async_support(true);
603+
config.epoch_interruption(true);
604+
605+
let engine = Engine::new(&config)?;
606+
607+
let component = Component::new(&engine, component)?;
608+
609+
let mut linker = Linker::new(&engine);
610+
611+
wasmtime_wasi::add_to_linker_async(&mut linker)?;
612+
borrowing_host::BorrowingHost::add_to_linker(&mut linker, |ctx| ctx)?;
613+
614+
let mut store = Store::new(
615+
&engine,
616+
Ctx {
617+
wasi: WasiCtxBuilder::new().inherit_stdio().build(),
618+
table: ResourceTable::default(),
619+
drop_count: 0,
620+
continue_: false,
621+
wakers: Arc::new(Mutex::new(None)),
622+
},
623+
);
624+
store.set_epoch_deadline(1);
625+
626+
std::thread::spawn(move || {
627+
std::thread::sleep(Duration::from_secs(10));
628+
engine.increment_epoch();
629+
});
630+
631+
let borrowing_host =
632+
borrowing_host::BorrowingHost::instantiate_async(&mut store, &component, &linker)
633+
.await?;
634+
635+
// Start three concurrent calls and then join them all:
636+
let mut promises = PromisesUnordered::new();
637+
for _ in 0..3 {
638+
promises.push(
639+
borrowing_host
640+
.local_local_run_bool()
641+
.call_run(&mut store, v)
642+
.await?,
643+
);
644+
}
645+
646+
while let Some(()) = promises.next(&mut store).await? {
647+
// continue
648+
}
649+
650+
Ok(())
651+
}
652+
653+
#[tokio::test]
654+
async fn async_borrowing_caller() -> Result<()> {
655+
let caller = &fs::read(test_programs_artifacts::ASYNC_BORROWING_CALLER_COMPONENT).await?;
656+
let callee = &fs::read(test_programs_artifacts::ASYNC_BORROWING_CALLEE_COMPONENT).await?;
657+
test_run_bool(&compose(caller, callee).await?, false).await
658+
}
659+
660+
#[tokio::test]
661+
async fn async_borrowing_caller_misbehave() -> Result<()> {
662+
let caller = &fs::read(test_programs_artifacts::ASYNC_BORROWING_CALLER_COMPONENT).await?;
663+
let callee = &fs::read(test_programs_artifacts::ASYNC_BORROWING_CALLEE_COMPONENT).await?;
664+
let error = format!(
665+
"{:?}",
666+
test_run_bool(&compose(caller, callee).await?, true)
667+
.await
668+
.unwrap_err()
669+
);
670+
assert!(error.contains("unknown handle index"), "{error}");
671+
Ok(())
672+
}
673+
674+
#[tokio::test]
675+
async fn async_borrowing_callee() -> Result<()> {
676+
let callee = &fs::read(test_programs_artifacts::ASYNC_BORROWING_CALLEE_COMPONENT).await?;
677+
test_run_bool(callee, false).await
678+
}
679+
680+
#[tokio::test]
681+
async fn async_borrowing_callee_misbehave() -> Result<()> {
682+
let callee = &fs::read(test_programs_artifacts::ASYNC_BORROWING_CALLEE_COMPONENT).await?;
683+
let error = format!("{:?}", test_run_bool(callee, true).await.unwrap_err());
684+
assert!(error.contains("unknown handle index"), "{error}");
685+
Ok(())
686+
}
687+
558688
mod transmit {
559689
wasmtime::component::bindgen!({
560690
path: "wit",

crates/misc/component-async-tests/wit/test.wit

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,23 @@ interface post-return {
4646
get-post-return-value: func() -> string;
4747
}
4848

49+
interface borrowing-types {
50+
resource x {
51+
constructor();
52+
foo: func();
53+
}
54+
}
55+
56+
interface borrowing {
57+
use borrowing-types.{x};
58+
59+
foo: func(x: borrow<x>, misbehave: bool);
60+
}
61+
62+
interface run-bool {
63+
run: func(v: bool);
64+
}
65+
4966
world yield-caller {
5067
import continue;
5168
import ready;
@@ -97,3 +114,18 @@ world post-return-caller {
97114
world post-return-callee {
98115
export post-return;
99116
}
117+
118+
world borrowing-caller {
119+
import borrowing;
120+
export run-bool;
121+
}
122+
123+
world borrowing-callee {
124+
export borrowing;
125+
export run-bool;
126+
}
127+
128+
world borrowing-host {
129+
import borrowing-types;
130+
export run-bool;
131+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
mod bindings {
2+
wit_bindgen::generate!({
3+
path: "../misc/component-async-tests/wit",
4+
world: "borrowing-callee",
5+
async: {
6+
exports: [
7+
"local:local/borrowing#foo",
8+
"local:local/run-bool#run"
9+
]
10+
}
11+
});
12+
13+
use super::Component;
14+
export!(Component);
15+
}
16+
17+
use {
18+
bindings::{
19+
exports::local::local::{borrowing::Guest as Borrowing, run_bool::Guest as RunBool},
20+
local::local::borrowing_types::X,
21+
},
22+
futures::future,
23+
std::future::Future,
24+
wit_bindgen_rt::async_support,
25+
};
26+
27+
struct Component;
28+
29+
impl Borrowing for Component {
30+
fn foo(x: &X, misbehave: bool) -> impl Future<Output = ()> + 'static {
31+
let handle = x.handle();
32+
async_support::spawn(async move {
33+
if misbehave {
34+
unsafe { X::from_handle(handle) }.foo();
35+
}
36+
});
37+
x.foo();
38+
future::ready(())
39+
}
40+
}
41+
42+
impl RunBool for Component {
43+
async fn run(misbehave: bool) {
44+
Self::foo(&X::new(), misbehave).await
45+
}
46+
}
47+
48+
// Unused function; required since this file is built as a `bin`:
49+
fn main() {}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
mod bindings {
2+
wit_bindgen::generate!({
3+
path: "../misc/component-async-tests/wit",
4+
world: "borrowing-caller",
5+
async: {
6+
imports: [
7+
"local:local/borrowing#foo"
8+
],
9+
exports: [
10+
"local:local/run-bool#run"
11+
]
12+
}
13+
});
14+
15+
use super::Component;
16+
export!(Component);
17+
}
18+
19+
use bindings::{
20+
exports::local::local::run_bool::Guest,
21+
local::local::{borrowing::foo, borrowing_types::X},
22+
};
23+
24+
struct Component;
25+
26+
impl Guest for Component {
27+
async fn run(misbehave: bool) {
28+
foo(&X::new(), misbehave).await
29+
}
30+
}
31+
32+
// Unused function; required since this file is built as a `bin`:
33+
fn main() {}

0 commit comments

Comments
 (0)