Skip to content

Commit df618ea

Browse files
authored
fix panic when event pending on CALLBACK_CODE_YIELD (#12190)
This replaces an invalid assertion with a more precise check when handling `CALLBACK_CODE_YIELD`. It also adds a test cover the scenario when a callback returns `CALLBACK_CODE_YIELD` when there's already an `EVENT_CANCELLED` pending.
1 parent b38f4bd commit df618ea

File tree

2 files changed

+106
-2
lines changed

2 files changed

+106
-2
lines changed

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,8 +1774,15 @@ impl Instance {
17741774
}
17751775
callback_code::YIELD => {
17761776
let task = state.get_mut(guest_thread.task)?;
1777-
assert!(task.event.is_none());
1778-
task.event = Some(Event::None);
1777+
// If an `Event::Cancelled` is pending, we'll deliver that;
1778+
// otherwise, we'll deliver `Event::None`. Note that
1779+
// `GuestTask::event` is only ever set to one of those two
1780+
// `Event` variants.
1781+
if let Some(event) = task.event {
1782+
assert!(matches!(event, Event::None | Event::Cancelled));
1783+
} else {
1784+
task.event = Some(Event::None);
1785+
}
17791786
let call = GuestCall {
17801787
thread: guest_thread,
17811788
kind: GuestCallKind::DeliverEvent {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
;;! component_model_async = true
2+
;;! reference_types = true
3+
4+
;; This test checks that an `EVENT_CANCELLED` can be delivered to a task that's
5+
;; in a `CALLBACK_CODE_YIELD` loop.
6+
(component
7+
(component $a
8+
(core module $m
9+
(import "" "task.cancel" (func $task-cancel))
10+
(import "" "thread.yield" (func $thread-yield (result i32)))
11+
12+
(func (export "f") (result i32)
13+
;; first, yield in a non-cancellable way a few times to give the caller
14+
;; a chance to queue up an `EVENT_CANCELLED`
15+
(local $i i32)
16+
(loop $loop
17+
(i32.ne (i32.const 0 (; NOT_CANCELLED ;) (call $thread-yield)))
18+
if unreachable end
19+
(local.set $i (i32.add (i32.const 1) (local.get $i)))
20+
(i32.ne (i32.const 10) (local.get $i))
21+
br_if $loop
22+
)
23+
24+
i32.const 1 ;; CALLBACK_CODE_YIELD
25+
)
26+
27+
(func (export "f-callback") (param i32 i32 i32) (result i32)
28+
(i32.eq (i32.const 6 (; EVENT_CANCELLED ;)) (local.get 0))
29+
(if (result i32)
30+
(then
31+
call $task-cancel
32+
i32.const 0 ;; CALLBACK_CODE_EXIT
33+
)
34+
(else
35+
i32.const 1 ;; CALLBACK_CODE_YIELD
36+
)
37+
)
38+
)
39+
)
40+
41+
(core func $task-cancel (canon task.cancel))
42+
(core func $thread-yield (canon thread.yield))
43+
44+
(core instance $i (instantiate $m
45+
(with "" (instance
46+
(export "task.cancel" (func $task-cancel))
47+
(export "thread.yield" (func $thread-yield))
48+
))
49+
))
50+
51+
(func (export "f") async (canon lift (core func $i "f") async (callback (func $i "f-callback"))))
52+
)
53+
(instance $a (instantiate $a))
54+
55+
(component $b
56+
(import "a" (instance $a
57+
(export "f" (func async))
58+
))
59+
60+
(core module $m
61+
(import "" "f" (func $f (result i32)))
62+
(import "" "subtask.cancel" (func $subtask-cancel (param i32) (result i32)))
63+
64+
(func (export "f")
65+
(local $status i32)
66+
(local $subtask i32)
67+
68+
(local.set $status (call $f))
69+
(local.set $subtask (i32.shr_u (i32.const 4) (local.get $status)))
70+
(local.set $status (i32.and (i32.const 0xF) (local.get $status)))
71+
(i32.ne (i32.const 1 (; STATUS_STARTED ;)) (local.get $status))
72+
if unreachable end
73+
(i32.ne (i32.const 4 (; STATUS_RETURN_CANCELLED ;)) (call $subtask-cancel (local.get $subtask)))
74+
if unreachable end
75+
)
76+
)
77+
78+
(core func $f (canon lower (func $a "f") async))
79+
(core func $subtask-cancel (canon subtask.cancel))
80+
81+
(core instance $i (instantiate $m
82+
(with "" (instance
83+
(export "f" (func $f))
84+
(export "subtask.cancel" (func $subtask-cancel))
85+
))
86+
))
87+
88+
(func (export "f") async (canon lift (core func $i "f")))
89+
)
90+
(instance $b (instantiate $b
91+
(with "a" (instance $a))
92+
))
93+
94+
(func (export "f") (alias export $b "f"))
95+
)
96+
97+
(assert_return (invoke "f"))

0 commit comments

Comments
 (0)