Skip to content

Commit 59b742e

Browse files
committed
Add some wast tests for async
1 parent 935c478 commit 59b742e

18 files changed

Lines changed: 1561 additions & 0 deletions
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TODO: async calls async which then does a sync thing
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
(component
2+
(component $AsyncInner
3+
(core module $CoreAsyncInner
4+
(import "" "context.set" (func $context.set (param i32)))
5+
(import "" "context.get" (func $context.get (result i32)))
6+
(import "" "task.return0" (func $task.return0))
7+
(import "" "task.return1" (func $task.return1 (param i32)))
8+
9+
(memory 1)
10+
(global $blocked (mut i32) (i32.const 1))
11+
(global $counter (mut i32) (i32.const 2))
12+
13+
(func $blocking-call (export "blocking-call") (result i32)
14+
(call $context.set (global.get $counter))
15+
(global.set $counter (i32.add (i32.const 1) (global.get $counter)))
16+
(i32.const 1 (; YIELD ;))
17+
)
18+
(func $blocking-call-cb (export "blocking-call-cb") (param i32 i32 i32) (result i32)
19+
(if (i32.eqz (global.get $blocked)) (then
20+
(call $task.return1 (call $context.get)) ;; TODO: check if getting this wrong traps
21+
(return (i32.const 0 (; EXIT ;)))
22+
))
23+
(i32.const 1 (; YIELD ;))
24+
)
25+
(func $unblock (export "unblock") (result i32)
26+
(global.set $blocked (i32.const 0))
27+
(call $task.return0) ;; TODO: check if getting this wrong traps
28+
(i32.const 0 (; EXIT ;))
29+
)
30+
(func $unblock-cb (export "unblock-cb") (param i32 i32 i32) (result i32)
31+
unreachable
32+
)
33+
)
34+
(canon task.return (core func $task.return0))
35+
(canon task.return (result u32) (core func $task.return1))
36+
(canon context.set i32 0 (core func $context.set))
37+
(canon context.get i32 0 (core func $context.get))
38+
(core instance $core_async_inner (instantiate $CoreAsyncInner (with "" (instance
39+
(export "task.return0" (func $task.return0))
40+
(export "task.return1" (func $task.return1))
41+
(export "context.set" (func $context.set))
42+
(export "context.get" (func $context.get))
43+
))))
44+
(func (export "blocking-call") (result u32) (canon lift
45+
(core func $core_async_inner "blocking-call")
46+
async (callback (func $core_async_inner "blocking-call-cb"))
47+
))
48+
(func (export "unblock") (canon lift
49+
(core func $core_async_inner "unblock")
50+
async (callback (func $core_async_inner "unblock-cb"))
51+
))
52+
)
53+
54+
(component $SyncMiddle
55+
(import "blocking-call" (func $blocking-call (result u32)))
56+
(core module $CoreSyncMiddle
57+
(import "" "blocking-call" (func $blocking-call (result i32)))
58+
(func $sync-func (export "sync-func") (result i32)
59+
(local $i i32)
60+
(call $blocking-call)
61+
)
62+
)
63+
(canon lower (func $blocking-call) (core func $blocking-call'))
64+
(core instance $sync_middle (instantiate $CoreSyncMiddle (with "" (instance
65+
(export "blocking-call" (func $blocking-call'))
66+
))))
67+
(func (export "sync-func") (result u32) (canon lift
68+
(core func $sync_middle "sync-func")
69+
))
70+
)
71+
72+
(component $AsyncOuter
73+
(import "unblock" (func $unblock))
74+
(import "sync-func1" (func $sync-func1 (result u32)))
75+
(import "sync-func2" (func $sync-func2 (result u32)))
76+
77+
(core module $Memory (memory (export "mem") 1))
78+
(core instance $memory (instantiate $Memory))
79+
(core module $CoreAsyncOuter
80+
(import "" "mem" (memory 1))
81+
(import "" "task.return" (func $task.return (param i32)))
82+
(import "" "subtask.drop" (func $subtask.drop (param i32)))
83+
(import "" "waitable.join" (func $waitable.join (param i32 i32)))
84+
(import "" "waitable-set.new" (func $waitable-set.new (result i32)))
85+
(import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32)))
86+
(import "" "unblock" (func $unblock))
87+
(import "" "sync-func1" (func $sync-func1 (param i32 i32) (result i32)))
88+
(import "" "sync-func2" (func $sync-func2 (param i32 i32) (result i32)))
89+
90+
(global $ws (mut i32) (i32.const 0))
91+
(func $start (global.set $ws (call $waitable-set.new)))
92+
(start $start)
93+
94+
(global $remain (mut i32) (i32.const -1))
95+
96+
(func $run (export "run") (result i32)
97+
(local $ret i32)
98+
99+
;; call 'sync-func1' and 'sync-func2' asynchronously, both of which will block
100+
;; (on $AsyncInner.blocking-call). because 'sync-func1/2' are in different instances,
101+
;; both calls will reach the STARTED state.
102+
(local.set $ret (call $sync-func1 (i32.const 0xdeadbeef) (i32.const 8)))
103+
(if (i32.ne (i32.const 0x21 (; STARTED=1 | (subtask=2 << 4) ;)) (local.get $ret))
104+
(then unreachable))
105+
(call $waitable.join (i32.const 2) (global.get $ws))
106+
(local.set $ret (call $sync-func2 (i32.const 0xdeadbeef) (i32.const 12)))
107+
(if (i32.ne (i32.const 0x31 (; STARTED=1 | (subtask=3 << 4) ;)) (local.get $ret))
108+
(then unreachable))
109+
(call $waitable.join (i32.const 3) (global.get $ws))
110+
111+
;; now start another pair of 'sync-func1/2' calls, both of which should see auto
112+
;; backpressure and get stuck in the STARTING state.
113+
(local.set $ret (call $sync-func1 (i32.const 0xdeadbeef) (i32.const 16)))
114+
(if (i32.ne (i32.const 0x40 (; STARTING=0 | (subtask=4 << 4) ;)) (local.get $ret))
115+
(then unreachable))
116+
(call $waitable.join (i32.const 4) (global.get $ws))
117+
(local.set $ret (call $sync-func2 (i32.const 0xdeadbeef) (i32.const 20)))
118+
(if (i32.ne (i32.const 0x50 (; STARTING=0 | (subtask=5 << 4) ;)) (local.get $ret))
119+
(then unreachable))
120+
(call $waitable.join (i32.const 5) (global.get $ws))
121+
122+
;; this POLL should return that nothing is ready
123+
(i32.or (i32.const 3 (; POLL ;)) (i32.shl (global.get $ws) (i32.const 4)))
124+
)
125+
(func $run-cb (export "run-cb") (param $event_code i32) (param $index i32) (param $payload i32) (result i32)
126+
(local $ret i32)
127+
128+
;; $remain is initially -1, so confirm that POLL found nothing was ready and then
129+
;; unblock all the subtasks and set $remain to 4 to count how many to wait for.
130+
(if (i32.eq (global.get $remain) (i32.const -1)) (then
131+
(if (i32.ne (local.get $event_code) (i32.const 0 (; NONE ;)))
132+
(then unreachable))
133+
(call $unblock)
134+
(global.set $remain (i32.const 4))
135+
(return (i32.or (i32.const 2 (; WAIT ;)) (i32.shl (global.get $ws) (i32.const 4))))
136+
))
137+
138+
;; confirm we only receive SUBTASK events after the first NONE event.
139+
(if (i32.ne (local.get $event_code) (i32.const 1 (; SUBTASK ;)))
140+
(then unreachable))
141+
142+
;; if we receive a SUBTASK STARTED event, then keep waiting for completion
143+
(if (i32.eq (local.get $payload) (i32.const 1 (; STARTED ;))) (then
144+
(return (i32.or (i32.const 2 (; WAIT ;)) (i32.shl (global.get $ws) (i32.const 4))))
145+
))
146+
147+
;; when we receive a SUBTASK RETURNED event, check the return value is equal to the
148+
;; subtask index (which we've ensured by having $AsyncInner.$counter start at 2, the
149+
;; first subtask index. The address of the return buffer is the index*4.
150+
(if (i32.ne (local.get $payload) (i32.const 2 (; RETURNED ;)))
151+
(then unreachable))
152+
(if (i32.ne (local.get $index) (i32.load (i32.mul (local.get $index) (i32.const 4))))
153+
(then unreachable))
154+
155+
;; decrement $remain and exit if 0
156+
(call $subtask.drop (local.get $index))
157+
(global.set $remain (i32.sub (global.get $remain) (i32.const 1)))
158+
(if (i32.gt_u (global.get $remain) (i32.const 0)) (then
159+
(return (i32.or (i32.const 2 (; WAIT ;)) (i32.shl (global.get $ws) (i32.const 4))))
160+
))
161+
(call $task.return (i32.const 42))
162+
(i32.const 0 (; EXIT ;))
163+
)
164+
)
165+
(canon task.return (result u32) (core func $task.return))
166+
(canon subtask.drop (core func $subtask.drop))
167+
(canon waitable.join (core func $waitable.join))
168+
(canon waitable-set.new (core func $waitable-set.new))
169+
(canon waitable-set.wait (memory $memory "mem") (core func $waitable-set.wait))
170+
(canon lower (func $unblock) (core func $unblock))
171+
(canon lower (func $sync-func1) async (memory $memory "mem") (core func $sync-func1'))
172+
(canon lower (func $sync-func2) async (memory $memory "mem") (core func $sync-func2'))
173+
(core instance $em (instantiate $CoreAsyncOuter (with "" (instance
174+
(export "mem" (memory $memory "mem"))
175+
(export "task.return" (func $task.return))
176+
(export "subtask.drop" (func $subtask.drop))
177+
(export "waitable.join" (func $waitable.join))
178+
(export "waitable-set.new" (func $waitable-set.new))
179+
(export "waitable-set.wait" (func $waitable-set.wait))
180+
(export "unblock" (func $unblock))
181+
(export "sync-func1" (func $sync-func1'))
182+
(export "sync-func2" (func $sync-func2'))
183+
))))
184+
(func (export "run") (result u32) (canon lift
185+
(core func $em "run")
186+
async (callback (func $em "run-cb"))
187+
))
188+
)
189+
190+
(instance $async_inner (instantiate $AsyncInner))
191+
(instance $sync_middle1 (instantiate $SyncMiddle
192+
(with "blocking-call" (func $async_inner "blocking-call"))
193+
))
194+
(instance $sync_middle2 (instantiate $SyncMiddle
195+
(with "blocking-call" (func $async_inner "blocking-call"))
196+
))
197+
(instance $async_outer (instantiate $AsyncOuter
198+
(with "unblock" (func $async_inner "unblock"))
199+
(with "sync-func1" (func $sync_middle1 "sync-func"))
200+
(with "sync-func2" (func $sync_middle2 "sync-func"))
201+
))
202+
(func (export "run") (alias export $async_outer "run"))
203+
)
204+
(assert_return (invoke "run") (u32.const 42))

test/concurrency/backpressure.wast

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TODO: if multiple pending async calls, only start 1 at a time

0 commit comments

Comments
 (0)