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

Commit 33f0ea2

Browse files
committed
add another deadlock test; add Trap::AsyncDeadlock variant
This creates a new `AsyncDeadlock` variant to the `Trap` enum, allowing application code to downcast to `Trap` rather than do a string match to handle the error programmatically. Thanks to Luke Wagner for the additional test case. Signed-off-by: Joel Dice <joel.dice@fermyon.com>
1 parent 915b3ee commit 33f0ea2

3 files changed

Lines changed: 86 additions & 4 deletions

File tree

crates/environ/src/trap_encoding.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ pub enum Trap {
9292
/// Async-lifted export failed to produce a result by calling `task.return`
9393
/// before returning `STATUS_DONE` and/or after all host tasks completed.
9494
NoAsyncResult,
95+
96+
/// Async event loop deadlocked; i.e. it cannot make further progress given
97+
/// that all host tasks have completed and any/all host-owned stream/future
98+
/// handles have been dropped.
99+
AsyncDeadlock,
95100
// if adding a variant here be sure to update the `check!` macro below
96101
}
97102

@@ -129,6 +134,7 @@ impl Trap {
129134
CastFailure
130135
CannotEnterComponent
131136
NoAsyncResult
137+
AsyncDeadlock
132138
}
133139

134140
None
@@ -160,6 +166,7 @@ impl fmt::Display for Trap {
160166
CastFailure => "cast failure",
161167
CannotEnterComponent => "cannot enter component instance",
162168
NoAsyncResult => "async-lifted export failed to produce a result",
169+
AsyncDeadlock => "deadlock detected: event loop cannot make further progress",
163170
};
164171
write!(f, "wasm trap: {desc}")
165172
}

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,10 +1734,9 @@ impl ComponentInstance {
17341734
// chance to intervene by cancelling one or more
17351735
// tasks and/or starting new tasks capable of
17361736
// waking the existing ones.
1737-
Poll::Ready(false) => Poll::Ready(Err(anyhow!(
1738-
"deadlock detected: event loop cannot \
1739-
make further progress"
1740-
))),
1737+
Poll::Ready(false) => {
1738+
Poll::Ready(Err(anyhow!(crate::Trap::AsyncDeadlock)))
1739+
}
17411740
Poll::Pending => Poll::Pending,
17421741
};
17431742
} else {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
;;! component_model_async = true
2+
;;! reference_types = true
3+
;;! gc_types = true
4+
;;! multi_memory = true
5+
6+
(component
7+
(component $C
8+
(core module $Memory (memory (export "mem") 1))
9+
(core instance $memory (instantiate $Memory))
10+
(core module $CM
11+
(import "" "mem" (memory 1))
12+
(import "" "waitable-set.new" (func $waitable-set.new (result i32)))
13+
14+
(func (export "f") (result i32)
15+
(local $ws i32)
16+
;; return WAIT on an empty waitable set
17+
(local.set $ws (call $waitable-set.new))
18+
(i32.or (i32.const 2 (; WAIT ;)) (i32.shl (local.get $ws) (i32.const 4)))
19+
)
20+
(func (export "cb") (param $event_code i32) (param $index i32) (param $payload i32) (result i32)
21+
unreachable
22+
)
23+
)
24+
(canon waitable-set.new (core func $waitable-set.new))
25+
(core instance $cm (instantiate $CM (with "" (instance
26+
(export "mem" (memory $memory "mem"))
27+
(export "waitable-set.new" (func $waitable-set.new))
28+
))))
29+
(func (export "f") (result u32) (canon lift
30+
(core func $cm "f")
31+
async (memory $memory "mem") (callback (func $cm "cb"))
32+
))
33+
)
34+
35+
(component $D
36+
(import "f" (func $f (result u32)))
37+
38+
(core module $Memory (memory (export "mem") 1))
39+
(core instance $memory (instantiate $Memory))
40+
(core module $DM
41+
(import "" "mem" (memory 1))
42+
(import "" "waitable.join" (func $waitable.join (param i32 i32)))
43+
(import "" "waitable-set.new" (func $waitable-set.new (result i32)))
44+
(import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32)))
45+
(import "" "f" (func $f (param i32 i32) (result i32)))
46+
47+
(func (export "g") (result i32)
48+
(local $ws i32) (local $ret i32) (local $subtaski i32)
49+
(local.set $ws (call $waitable-set.new))
50+
(local.set $ret (call $f (i32.const 0) (i32.const 0)))
51+
(local.set $subtaski (i32.shr_u (local.get $ret) (i32.const 4)))
52+
(call $waitable.join (local.get $subtaski) (local.get $ws))
53+
(call $waitable-set.wait (local.get $ws) (i32.const 0))
54+
unreachable
55+
)
56+
)
57+
(canon waitable.join (core func $waitable.join))
58+
(canon waitable-set.new (core func $waitable-set.new))
59+
(canon waitable-set.wait (memory $memory "mem") (core func $waitable-set.wait))
60+
(canon lower (func $f) async (memory $memory "mem") (core func $f'))
61+
(core instance $dm (instantiate $DM (with "" (instance
62+
(export "mem" (memory $memory "mem"))
63+
(export "waitable.join" (func $waitable.join))
64+
(export "waitable-set.new" (func $waitable-set.new))
65+
(export "waitable-set.wait" (func $waitable-set.wait))
66+
(export "f" (func $f'))
67+
))))
68+
(func (export "f") (result u32) (canon lift (core func $dm "g")))
69+
)
70+
71+
(instance $c (instantiate $C))
72+
(instance $d (instantiate $D (with "f" (func $c "f"))))
73+
(func (export "f") (alias export $d "f"))
74+
)
75+
76+
(assert_trap (invoke "f") "deadlock detected")

0 commit comments

Comments
 (0)