Skip to content

Commit 7a4bf88

Browse files
committed
Add some async tests
1 parent c90abf1 commit 7a4bf88

File tree

4 files changed

+226
-2
lines changed

4 files changed

+226
-2
lines changed

design/mvp/CanonicalABI.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3818,10 +3818,11 @@ given waitable is already in one set, it will be transferred.
38183818

38193819
For a canonical definition:
38203820
```wat
3821-
(canon subtask.cancel (core func $f))
3821+
(canon subtask.cancel async? (core func $f))
38223822
```
38233823
validation specifies:
38243824
* `$f` is given type `(func (param i32))`
3825+
* 🚝 - `async` is allowed (otherwise it must be absent)
38253826

38263827
Calling `$f` sends a request to the subtask at the given index to cancel its
38273828
execution ASAP. This request is cooperative and the subtask may take arbitrarily
@@ -4134,7 +4135,7 @@ For canonical definitions:
41344135
validation specifies:
41354136
* `$f` is given type `(func (param i32) (result i32))`
41364137
* `$stream_t`/`$future_t` must be a type of the form `(stream $t?)`/`(future $t?)`
4137-
* 🚝 - `async` is allowed (otherwise it must be `false`)
4138+
* 🚝 - `async` is allowed (otherwise it must be absent)
41384139

41394140
The implementation of these four built-ins all funnel down to a single
41404141
parameterized `cancel_copy` function:

design/mvp/Explainer.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,6 +1796,10 @@ Otherwise, `subtask.cancel` returns the `subtask-state` that the subtask
17961796
resolved to (which is one of `returned`, `cancelled-before-started` or
17971797
`cancelled-before-returned`).
17981798

1799+
The `async` immediate is gated on 🚝. Without `async`, the `none` case is not
1800+
possible and `subtask.cancel` synchronously waits until the callee is
1801+
resolved.
1802+
17991803
For details, see [`canon_subtask_cancel`] in the Canonical ABI explainer.
18001804

18011805
###### 🔀 `subtask.drop`

test/async/reject-gated-features.wast

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
(assert_invalid
2+
(component
3+
(type $RT (resource (rep i32)))
4+
(canon resource.drop $RT async (core func $drop))
5+
)
6+
"failed to parse WebAssembly module"
7+
)
8+
(assert_invalid
9+
(component
10+
(canon subtask.cancel async (core func $subtask-cancel))
11+
)
12+
"async `subtask.cancel` requires the component model async builtins feature"
13+
)
14+
(assert_invalid
15+
(component
16+
(type $ST (stream u8))
17+
(canon stream.cancel-read $ST async (core func $cancel-read))
18+
)
19+
"async `stream.cancel-read` requires the component model async builtins feature"
20+
)
21+
(assert_invalid
22+
(component
23+
(type $ST (stream u8))
24+
(canon stream.cancel-write $ST async (core func $cancel-read))
25+
)
26+
"async `stream.cancel-write` requires the component model async builtins feature"
27+
)
28+
(assert_invalid
29+
(component
30+
(type $FT (future u8))
31+
(canon future.cancel-read $FT async (core func $cancel-read))
32+
)
33+
"async `future.cancel-read` requires the component model async builtins feature"
34+
)
35+
(assert_invalid
36+
(component
37+
(type $FT (future u8))
38+
(canon future.cancel-write $FT async (core func $cancel-read))
39+
)
40+
"async `future.cancel-write` requires the component model async builtins feature"
41+
)

test/async/sync-streams.wast

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
;; This test calls sync stream.write in $C.get and sync stream.read in $C.set.
2+
;; Both of these calls block because $C is first to the rendezvous. But since
3+
;; they are synchronous, control flow switches to $D.run which will do
4+
;; a complementary read/write that rendezvous, and then control flow will
5+
;; switch back to $C.get/set where the synchronous read/write will return
6+
;; without blocking.
7+
(component
8+
(component $C
9+
(core module $Memory (memory (export "mem") 1))
10+
(core instance $memory (instantiate $Memory))
11+
(core module $CM
12+
(import "" "mem" (memory 1))
13+
(import "" "task.return0" (func $task.return0))
14+
(import "" "task.return1" (func $task.return1 (param i32)))
15+
(import "" "stream.new" (func $stream.new (result i64)))
16+
(import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32)))
17+
(import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32)))
18+
(import "" "stream.drop-readable" (func $stream.drop-readable (param i32)))
19+
(import "" "stream.drop-writable" (func $stream.drop-writable (param i32)))
20+
21+
(func (export "get") (result i32)
22+
(local $ret i32) (local $ret64 i64)
23+
(local $tx i32) (local $rx i32)
24+
(local $bufp i32)
25+
26+
;; ($rx, $tx) = stream.new
27+
(local.set $ret64 (call $stream.new))
28+
(local.set $rx (i32.wrap_i64 (local.get $ret64)))
29+
(local.set $tx (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32))))
30+
31+
;; return $rx
32+
(call $task.return1 (local.get $rx))
33+
34+
;; (stream.write $tx $bufp 4) will block and, because called
35+
;; synchronously, switch to the caller who will read and rendezvous
36+
(local.set $bufp (i32.const 16))
37+
(i32.store (local.get $bufp) (i32.const 0x01234567))
38+
(local.set $ret (call $stream.write (local.get $tx) (local.get $bufp) (i32.const 4)))
39+
(if (i32.ne (i32.const 0x40 (; COMPLETED=0 | (4<<4) ;)) (local.get $ret))
40+
(then unreachable))
41+
42+
(call $stream.drop-writable (local.get $tx))
43+
(return (i32.const 0 (; EXIT ;)))
44+
)
45+
(func (export "get_cb") (param i32 i32 i32) (result i32)
46+
unreachable
47+
)
48+
49+
(func (export "set") (param $rx i32) (result i32)
50+
(local $ret i32) (local $ret64 i64)
51+
(local $bufp i32)
52+
53+
;; return immediately so that the caller can just call synchronously
54+
(call $task.return0)
55+
56+
;; (stream.read $tx $bufp 4) will block and, because called
57+
;; synchronously, switch to the caller who will write and rendezvous
58+
(local.set $bufp (i32.const 16))
59+
(local.set $ret (call $stream.read (local.get $rx) (local.get $bufp) (i32.const 4)))
60+
(if (i32.ne (i32.const 0x40 (; COMPLETED=0 | (4<<4) ;)) (local.get $ret))
61+
(then unreachable))
62+
(if (i32.ne (i32.const 0x89abcdef) (i32.load (local.get $bufp)))
63+
(then unreachable))
64+
65+
(call $stream.drop-readable (local.get $rx))
66+
(return (i32.const 0 (; EXIT ;)))
67+
)
68+
(func (export "set_cb") (param i32 i32 i32) (result i32)
69+
unreachable
70+
)
71+
)
72+
(type $ST (stream u8))
73+
(canon task.return (memory $memory "mem") (core func $task.return0))
74+
(canon task.return (result $ST) (memory $memory "mem") (core func $task.return1))
75+
(canon stream.new $ST (core func $stream.new))
76+
(canon stream.read $ST (memory $memory "mem") (core func $stream.read))
77+
(canon stream.write $ST (memory $memory "mem") (core func $stream.write))
78+
(canon stream.drop-readable $ST (core func $stream.drop-readable))
79+
(canon stream.drop-writable $ST (core func $stream.drop-writable))
80+
(core instance $cm (instantiate $CM (with "" (instance
81+
(export "mem" (memory $memory "mem"))
82+
(export "task.return0" (func $task.return0))
83+
(export "task.return1" (func $task.return1))
84+
(export "stream.new" (func $stream.new))
85+
(export "stream.read" (func $stream.read))
86+
(export "stream.write" (func $stream.write))
87+
(export "stream.drop-readable" (func $stream.drop-readable))
88+
(export "stream.drop-writable" (func $stream.drop-writable))
89+
))))
90+
(func (export "get") (result (stream u8)) (canon lift
91+
(core func $cm "get")
92+
async (memory $memory "mem") (callback (func $cm "get_cb"))
93+
))
94+
(func (export "set") (param "in" (stream u8)) (canon lift
95+
(core func $cm "set")
96+
async (memory $memory "mem") (callback (func $cm "set_cb"))
97+
))
98+
)
99+
(component $D
100+
(import "get" (func $get (result (stream u8))))
101+
(import "set" (func $set (param "in" (stream u8))))
102+
103+
(core module $Memory (memory (export "mem") 1))
104+
(core instance $memory (instantiate $Memory))
105+
(core module $DM
106+
(import "" "mem" (memory 1))
107+
(import "" "stream.new" (func $stream.new (result i64)))
108+
(import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32)))
109+
(import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32)))
110+
(import "" "stream.drop-readable" (func $stream.drop-readable (param i32)))
111+
(import "" "stream.drop-writable" (func $stream.drop-writable (param i32)))
112+
(import "" "get" (func $get (result i32)))
113+
(import "" "set" (func $set (param i32)))
114+
115+
(func (export "run") (result i32)
116+
(local $ret i32) (local $ret64 i64)
117+
(local $rx i32) (local $tx i32)
118+
(local $bufp i32)
119+
120+
;; $rx = $C.get()
121+
(local.set $rx (call $get))
122+
123+
;; (stream.read $tx $bufp 4) will succeed without blocking
124+
(local.set $bufp (i32.const 20))
125+
(local.set $ret (call $stream.read (local.get $rx) (local.get $bufp) (i32.const 4)))
126+
(if (i32.ne (i32.const 0x40 (; COMPLETED=0 | (4<<4) ;)) (local.get $ret))
127+
(then unreachable))
128+
(if (i32.ne (i32.const 0x01234567) (i32.load (local.get $bufp)))
129+
(then unreachable))
130+
131+
(call $stream.drop-readable (local.get $rx))
132+
133+
;; ($rx, $tx) = stream.new
134+
;; $C.set($rx)
135+
(local.set $ret64 (call $stream.new))
136+
(local.set $rx (i32.wrap_i64 (local.get $ret64)))
137+
(local.set $tx (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32))))
138+
(call $set (local.get $rx))
139+
140+
;; (stream.write $tx $bufp 4) will succeed without blocking
141+
(local.set $bufp (i32.const 16))
142+
(local.set $ret (call $stream.write (local.get $tx) (local.get $bufp) (i32.const 4)))
143+
(if (i32.ne (i32.const 0x40 (; COMPLETED=0 | (4<<4) ;)) (local.get $ret))
144+
(then unreachable))
145+
146+
(call $stream.drop-writable (local.get $tx))
147+
(i32.const 42)
148+
)
149+
)
150+
(type $ST (stream u8))
151+
(canon stream.new $ST (core func $stream.new))
152+
(canon stream.read $ST async (memory $memory "mem") (core func $stream.read))
153+
(canon stream.write $ST async (memory $memory "mem") (core func $stream.write))
154+
(canon stream.drop-readable $ST (core func $stream.drop-readable))
155+
(canon stream.drop-writable $ST (core func $stream.drop-writable))
156+
(canon lower (func $get) (core func $get'))
157+
(canon lower (func $set) (core func $set'))
158+
(core instance $dm (instantiate $DM (with "" (instance
159+
(export "mem" (memory $memory "mem"))
160+
(export "stream.new" (func $stream.new))
161+
(export "stream.read" (func $stream.read))
162+
(export "stream.write" (func $stream.write))
163+
(export "stream.drop-readable" (func $stream.drop-readable))
164+
(export "stream.drop-writable" (func $stream.drop-writable))
165+
(export "get" (func $get'))
166+
(export "set" (func $set'))
167+
))))
168+
(func (export "run") (result u32) (canon lift (core func $dm "run")))
169+
)
170+
171+
(instance $c (instantiate $C))
172+
(instance $d (instantiate $D
173+
(with "get" (func $c "get"))
174+
(with "set" (func $c "set"))
175+
))
176+
(func (export "run") (alias export $d "run"))
177+
)
178+
(assert_return (invoke "run") (u32.const 42))

0 commit comments

Comments
 (0)