Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit b4841cd

Browse files
authored
trap if sync->async call fails to produce a result (#92)
If the callee returns `CallbackCode.WAIT` and the event loop runs out of futures without producing a ready waitable, we should trap. We were doing that in other contexts, but not for sync->async calls. Fixes #73 Signed-off-by: Joel Dice <joel.dice@fermyon.com>
1 parent b1fb18e commit b4841cd

3 files changed

Lines changed: 81 additions & 26 deletions

File tree

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

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,7 @@ impl ComponentInstance {
890890
Ok(())
891891
} else {
892892
// In this case, the fiber suspended while holding on to its
893-
// `*mut dyn VMStoret` instead of returning it to us. That
893+
// `*mut dyn VMStore` instead of returning it to us. That
894894
// means we can't do anything else with the store (or self)
895895
// for now; the only thing we can do is suspend _our_ fiber
896896
// back up to the top level executor, which will resume us
@@ -1485,6 +1485,8 @@ impl ComponentInstance {
14851485
},
14861486
};
14871487

1488+
let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
1489+
14881490
let start = SendSyncPtr::new(NonNull::new(start).unwrap());
14891491
let return_ = SendSyncPtr::new(NonNull::new(return_).unwrap());
14901492
let old_task = self.guest_task().take();
@@ -1541,14 +1543,17 @@ impl ComponentInstance {
15411543
)?;
15421544
}
15431545
let task = instance.guest_task().unwrap();
1544-
if let ResultInfo::Stack { result_count } = &result_info {
1545-
match result_count {
1546-
0 => {}
1547-
1 => {
1548-
instance.get_mut(task)?.sync_result = Some(my_src[0]);
1549-
}
1550-
_ => unreachable!(),
1551-
}
1546+
if sync_caller {
1547+
instance.get_mut(task)?.sync_result =
1548+
Some(if let ResultInfo::Stack { result_count } = &result_info {
1549+
match result_count {
1550+
0 => None,
1551+
1 => Some(my_src[0]),
1552+
_ => unreachable!(),
1553+
}
1554+
} else {
1555+
None
1556+
});
15521557
}
15531558
if old_task_rep.is_some() {
15541559
let waitable = Waitable::Guest(task);
@@ -1576,11 +1581,6 @@ impl ComponentInstance {
15761581

15771582
if let Some(old_task) = old_task {
15781583
self.get_mut(old_task)?.subtasks.insert(guest_task);
1579-
log::trace!(
1580-
"new guest task child of {}: {}",
1581-
old_task.rep(),
1582-
guest_task.rep()
1583-
);
15841584
};
15851585

15861586
*self.guest_task() = Some(guest_task);
@@ -1714,7 +1714,11 @@ impl ComponentInstance {
17141714

17151715
if let Some(storage) = storage {
17161716
if let Some(result) = self.get_mut(guest_task)?.sync_result.take() {
1717-
storage[0] = MaybeUninit::new(result);
1717+
if let Some(result) = result {
1718+
storage[0] = MaybeUninit::new(result);
1719+
}
1720+
} else {
1721+
return Err(anyhow!(crate::Trap::NoAsyncResult));
17181722
}
17191723
Ok(0)
17201724
} else {
@@ -2892,7 +2896,7 @@ struct GuestTask {
28922896
deferred: Deferred,
28932897
should_yield: bool,
28942898
call_context: Option<CallContext>,
2895-
sync_result: Option<ValRaw>,
2899+
sync_result: Option<Option<ValRaw>>,
28962900
has_suspended: bool,
28972901
context: [u32; 2],
28982902
subtasks: HashSet<TableId<GuestTask>>,

tests/misc_testsuite/component-model-async/fused.wast

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@
5151
(func (export "run") (alias export $lowerer "run"))
5252
)
5353

54-
;; TODO: this requires async support in `wasmtime-wast`:
55-
;;(assert_return (invoke "run"))
54+
(assert_return (invoke "run"))
5655

5756
;; async lower -> async lift with callback
5857
(component
@@ -105,8 +104,7 @@
105104
(func (export "run") (alias export $lowerer "run"))
106105
)
107106

108-
;; TODO: this requires async support in `wasmtime-wast`:
109-
;;(assert_return (invoke "run"))
107+
(assert_return (invoke "run"))
110108

111109
;; async lower -> sync lift
112110
(component
@@ -152,8 +150,7 @@
152150
(func (export "run") (alias export $lowerer "run"))
153151
)
154152

155-
;; TODO: this requires async support in `wasmtime-wast`:
156-
;;(assert_return (invoke "run"))
153+
(assert_return (invoke "run"))
157154

158155
;; sync lower -> async lift without callback
159156
(component
@@ -196,8 +193,7 @@
196193
(func (export "run") (alias export $lowerer "run"))
197194
)
198195

199-
;; TODO: this requires async support in `wasmtime-wast`:
200-
;;(assert_return (invoke "run"))
196+
(assert_return (invoke "run"))
201197

202198
;; sync lower -> async lift with callback
203199
(component
@@ -244,5 +240,4 @@
244240
(func (export "run") (alias export $lowerer "run"))
245241
)
246242

247-
;; TODO: this requires async support in `wasmtime-wast`:
248-
;;(assert_return (invoke "run"))
243+
(assert_return (invoke "run"))
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
;;! cm_async = true
2+
;;! reference_types = true
3+
;;! gc_types = true
4+
;;! multi_memory = true
5+
6+
(component
7+
(component $child
8+
(core module $libc (memory (export "memory") 1))
9+
(core instance $libc (instantiate $libc))
10+
11+
(core module $m
12+
(import "" "waitable-set-new" (func $waitable-set-new (result i32)))
13+
(func (export "run") (result i32)
14+
call $waitable-set-new
15+
i32.const 4
16+
i32.shl
17+
i32.const 2 ;; CallbackCode.WAIT
18+
i32.or
19+
)
20+
21+
(func (export "cb") (param i32 i32 i32) (result i32)
22+
unreachable)
23+
)
24+
25+
(core func $waitable-set-new (canon waitable-set.new))
26+
27+
(core instance $i (instantiate $m
28+
(with "" (instance
29+
(export "waitable-set-new" (func $waitable-set-new))
30+
))
31+
))
32+
33+
(func (export "run")
34+
(canon lift (core func $i "run") async (callback (func $i "cb"))))
35+
)
36+
(instance $child (instantiate $child))
37+
38+
(core func $child-run (canon lower (func $child "run")))
39+
40+
(core module $m
41+
(import "" "child-run" (func $child-run))
42+
43+
(func (export "run")
44+
(call $child-run))
45+
)
46+
(core instance $i (instantiate $m
47+
(with "" (instance
48+
(export "child-run" (func $child-run))
49+
))
50+
))
51+
52+
(func (export "run")
53+
(canon lift (core func $i "run")))
54+
)
55+
56+
(assert_trap (invoke "run") "async-lifted export failed to produce a result")

0 commit comments

Comments
 (0)