Skip to content

Commit c7176a5

Browse files
committed
Add test/async/cancellable.wast
1 parent 8f1ea16 commit c7176a5

File tree

1 file changed

+361
-0
lines changed

1 file changed

+361
-0
lines changed

test/async/cancellable.wast

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
;; This test exercises the 'cancellable' immediate on waitable-set.wait,
2+
;; waitable-set.poll, and thread.yield.
3+
;;
4+
;; Component $C exports five async callback-lifted functions that block in
5+
;; their initial core function (the callbacks are never invoked):
6+
;; wait-cancel: blocks on cancellable waitable-set.wait, expects TASK_CANCELLED
7+
;; poll-cancel: blocks on cancellable waitable-set.poll, expects TASK_CANCELLED
8+
;; yield-cancel: yields with cancellable, caller cancels during yield
9+
;; poll-cancel-pending: blocks on non-cancellable wait, then polls with cancellable
10+
;; yield-cancel-pending: blocks on non-cancellable wait, then yields with cancellable
11+
;;
12+
;; Component $D calls each function and cancels it, verifying the cancel is
13+
;; delivered correctly through the cancellable built-in in each case.
14+
(component
15+
(component $C
16+
(core module $Memory (memory (export "mem") 1))
17+
(core instance $memory (instantiate $Memory))
18+
(core module $CM
19+
(import "" "mem" (memory 1))
20+
(import "" "task.cancel" (func $task.cancel))
21+
(import "" "future.read" (func $future.read (param i32 i32) (result i32)))
22+
(import "" "waitable.join" (func $waitable.join (param i32 i32)))
23+
(import "" "waitable-set.new" (func $waitable-set.new (result i32)))
24+
(import "" "waitable-set.wait-cancellable" (func $waitable-set.wait-cancellable (param i32 i32) (result i32)))
25+
(import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32)))
26+
(import "" "waitable-set.poll-cancellable" (func $waitable-set.poll-cancellable (param i32 i32) (result i32)))
27+
(import "" "thread.yield-cancellable" (func $thread.yield-cancellable (result i32)))
28+
29+
;; Test 1: direct cancel delivery through cancellable waitable-set.wait
30+
(func $wait-cancel (export "wait-cancel") (result i32)
31+
(local $event_code i32)
32+
(local $ws i32)
33+
(local.set $ws (call $waitable-set.new))
34+
;; wait on empty waitable set with cancellable; blocks until cancelled
35+
(local.set $event_code (call $waitable-set.wait-cancellable (local.get $ws) (i32.const 0)))
36+
(if (i32.ne (local.get $event_code) (i32.const 6 (; TASK_CANCELLED ;)))
37+
(then unreachable))
38+
(call $task.cancel)
39+
(i32.const 0 (; EXIT ;))
40+
)
41+
42+
;; Test 2: direct cancel delivery through cancellable waitable-set.poll
43+
(func $poll-cancel (export "poll-cancel") (result i32)
44+
(local $ws i32)
45+
(local $event_code i32)
46+
(local.set $ws (call $waitable-set.new))
47+
;; poll on empty waitable set with cancellable; blocks until cancelled
48+
(local.set $event_code (call $waitable-set.poll-cancellable (local.get $ws) (i32.const 0)))
49+
(if (i32.ne (local.get $event_code) (i32.const 6 (; TASK_CANCELLED ;)))
50+
(then unreachable))
51+
(call $task.cancel)
52+
(i32.const 0 (; EXIT ;))
53+
)
54+
55+
;; Test 3: direct cancel delivery through cancellable thread.yield
56+
(func $yield-cancel (export "yield-cancel") (result i32)
57+
(local $ret i32)
58+
;; yield with cancellable; suspends with cancellable=true, caller cancels
59+
(local.set $ret (call $thread.yield-cancellable))
60+
(if (i32.ne (i32.const 1 (; CANCELLED ;)) (local.get $ret))
61+
(then unreachable))
62+
(call $task.cancel)
63+
(i32.const 0 (; EXIT ;))
64+
)
65+
66+
;; Test 4: deferred cancel delivered through cancellable waitable-set.poll
67+
(func $poll-cancel-pending (export "poll-cancel-pending") (param $futr i32) (result i32)
68+
(local $ws i32)
69+
(local $ret i32)
70+
(local $event_code i32)
71+
(local.set $ws (call $waitable-set.new))
72+
;; read future - blocks (caller hasn't written yet)
73+
(local.set $ret (call $future.read (local.get $futr) (i32.const 0)))
74+
(if (i32.ne (i32.const -1 (; BLOCKED ;)) (local.get $ret))
75+
(then unreachable))
76+
(call $waitable.join (local.get $futr) (local.get $ws))
77+
;; wait WITHOUT cancellable - cancel will be deferred as PENDING_CANCEL
78+
(local.set $event_code (call $waitable-set.wait (local.get $ws) (i32.const 0)))
79+
(if (i32.ne (i32.const 4 (; FUTURE_READ ;)) (local.get $event_code))
80+
(then unreachable))
81+
;; poll WITH cancellable - delivers the pending cancel
82+
(local.set $event_code (call $waitable-set.poll-cancellable (local.get $ws) (i32.const 0)))
83+
(if (i32.ne (i32.const 6 (; TASK_CANCELLED ;)) (local.get $event_code))
84+
(then unreachable))
85+
(call $task.cancel)
86+
(i32.const 0 (; EXIT ;))
87+
)
88+
89+
;; Test 5: deferred cancel delivered through cancellable thread.yield
90+
(func $yield-cancel-pending (export "yield-cancel-pending") (param $futr i32) (result i32)
91+
(local $ws i32)
92+
(local $ret i32)
93+
(local $event_code i32)
94+
(local.set $ws (call $waitable-set.new))
95+
;; read future - blocks (caller hasn't written yet)
96+
(local.set $ret (call $future.read (local.get $futr) (i32.const 0)))
97+
(if (i32.ne (i32.const -1 (; BLOCKED ;)) (local.get $ret))
98+
(then unreachable))
99+
(call $waitable.join (local.get $futr) (local.get $ws))
100+
;; wait WITHOUT cancellable - cancel will be deferred as PENDING_CANCEL
101+
(local.set $event_code (call $waitable-set.wait (local.get $ws) (i32.const 0)))
102+
(if (i32.ne (i32.const 4 (; FUTURE_READ ;)) (local.get $event_code))
103+
(then unreachable))
104+
;; yield WITH cancellable - delivers the pending cancel
105+
(local.set $ret (call $thread.yield-cancellable))
106+
(if (i32.ne (i32.const 1 (; CANCELLED ;)) (local.get $ret))
107+
(then unreachable))
108+
(call $task.cancel)
109+
(i32.const 0 (; EXIT ;))
110+
)
111+
112+
;; callback that should never be called
113+
(func (export "unreachable-cb") (param i32 i32 i32) (result i32)
114+
unreachable
115+
)
116+
)
117+
(type $FT (future))
118+
(canon task.cancel (core func $task.cancel))
119+
(canon future.read $FT async (memory $memory "mem") (core func $future.read))
120+
(canon waitable.join (core func $waitable.join))
121+
(canon waitable-set.new (core func $waitable-set.new))
122+
(canon waitable-set.wait cancellable (memory $memory "mem") (core func $waitable-set.wait-cancellable))
123+
(canon waitable-set.wait (memory $memory "mem") (core func $waitable-set.wait))
124+
(canon waitable-set.poll cancellable (memory $memory "mem") (core func $waitable-set.poll-cancellable))
125+
(canon thread.yield cancellable (core func $thread.yield-cancellable))
126+
(core instance $cm (instantiate $CM (with "" (instance
127+
(export "mem" (memory $memory "mem"))
128+
(export "task.cancel" (func $task.cancel))
129+
(export "future.read" (func $future.read))
130+
(export "waitable.join" (func $waitable.join))
131+
(export "waitable-set.new" (func $waitable-set.new))
132+
(export "waitable-set.wait-cancellable" (func $waitable-set.wait-cancellable))
133+
(export "waitable-set.wait" (func $waitable-set.wait))
134+
(export "waitable-set.poll-cancellable" (func $waitable-set.poll-cancellable))
135+
(export "thread.yield-cancellable" (func $thread.yield-cancellable))
136+
))))
137+
(func (export "wait-cancel") async (result u32) (canon lift
138+
(core func $cm "wait-cancel")
139+
async (callback (func $cm "unreachable-cb"))
140+
))
141+
(func (export "poll-cancel") async (result u32) (canon lift
142+
(core func $cm "poll-cancel")
143+
async (callback (func $cm "unreachable-cb"))
144+
))
145+
(func (export "yield-cancel") async (result u32) (canon lift
146+
(core func $cm "yield-cancel")
147+
async (callback (func $cm "unreachable-cb"))
148+
))
149+
(func (export "poll-cancel-pending") async (param "fut" $FT) (result u32) (canon lift
150+
(core func $cm "poll-cancel-pending")
151+
async (callback (func $cm "unreachable-cb"))
152+
))
153+
(func (export "yield-cancel-pending") async (param "fut" $FT) (result u32) (canon lift
154+
(core func $cm "yield-cancel-pending")
155+
async (callback (func $cm "unreachable-cb"))
156+
))
157+
)
158+
159+
(component $D
160+
(type $FT (future))
161+
(import "wait-cancel" (func $wait-cancel async (result u32)))
162+
(import "poll-cancel" (func $poll-cancel async (result u32)))
163+
(import "yield-cancel" (func $yield-cancel async (result u32)))
164+
(import "poll-cancel-pending" (func $poll-cancel-pending async (param "fut" $FT) (result u32)))
165+
(import "yield-cancel-pending" (func $yield-cancel-pending async (param "fut" $FT) (result u32)))
166+
167+
(core module $Memory (memory (export "mem") 1))
168+
(core instance $memory (instantiate $Memory))
169+
(core module $DM
170+
(import "" "mem" (memory 1))
171+
(import "" "subtask.cancel" (func $subtask.cancel (param i32) (result i32)))
172+
(import "" "subtask.drop" (func $subtask.drop (param i32)))
173+
(import "" "future.new" (func $future.new (result i64)))
174+
(import "" "future.write" (func $future.write (param i32 i32) (result i32)))
175+
(import "" "waitable.join" (func $waitable.join (param i32 i32)))
176+
(import "" "waitable-set.new" (func $waitable-set.new (result i32)))
177+
(import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32)))
178+
(import "" "wait-cancel" (func $wait-cancel (param i32) (result i32)))
179+
(import "" "poll-cancel" (func $poll-cancel (param i32) (result i32)))
180+
(import "" "yield-cancel" (func $yield-cancel (param i32) (result i32)))
181+
(import "" "poll-cancel-pending" (func $poll-cancel-pending (param i32 i32) (result i32)))
182+
(import "" "yield-cancel-pending" (func $yield-cancel-pending (param i32 i32) (result i32)))
183+
184+
(func $run (export "run") (result i32)
185+
(local $ret i32) (local $ret64 i64)
186+
(local $retp i32) (local $retp2 i32)
187+
(local $subtask i32)
188+
(local $event_code i32)
189+
(local $futr i32) (local $futw i32)
190+
(local $ws i32)
191+
192+
;; ==========================================
193+
;; Test 1: waitable-set.wait cancellable
194+
;; ==========================================
195+
196+
;; call wait-cancel; it should block in cancellable wait
197+
(local.set $retp (i32.const 4))
198+
(local.set $ret (call $wait-cancel (local.get $retp)))
199+
(if (i32.ne (i32.const 1 (; STARTED ;)) (i32.and (local.get $ret) (i32.const 0xf)))
200+
(then unreachable))
201+
(local.set $subtask (i32.shr_u (local.get $ret) (i32.const 4)))
202+
203+
;; cancel; completes immediately (C is in cancellable wait)
204+
(local.set $ret (call $subtask.cancel (local.get $subtask)))
205+
(if (i32.ne (i32.const 4 (; CANCELLED_BEFORE_RETURNED ;)) (local.get $ret))
206+
(then unreachable))
207+
(call $subtask.drop (local.get $subtask))
208+
209+
;; ==========================================
210+
;; Test 2: waitable-set.poll cancellable
211+
;; ==========================================
212+
213+
;; call poll-cancel; it should block in cancellable poll
214+
(local.set $ret (call $poll-cancel (local.get $retp)))
215+
(if (i32.ne (i32.const 1 (; STARTED ;)) (i32.and (local.get $ret) (i32.const 0xf)))
216+
(then unreachable))
217+
(local.set $subtask (i32.shr_u (local.get $ret) (i32.const 4)))
218+
219+
;; cancel; completes immediately (C is in cancellable poll)
220+
(local.set $ret (call $subtask.cancel (local.get $subtask)))
221+
(if (i32.ne (i32.const 4 (; CANCELLED_BEFORE_RETURNED ;)) (local.get $ret))
222+
(then unreachable))
223+
(call $subtask.drop (local.get $subtask))
224+
225+
;; ==========================================
226+
;; Test 3: thread.yield cancellable
227+
;; ==========================================
228+
229+
;; call yield-cancel; it should suspend in cancellable yield
230+
(local.set $ret (call $yield-cancel (local.get $retp)))
231+
(if (i32.ne (i32.const 1 (; STARTED ;)) (i32.and (local.get $ret) (i32.const 0xf)))
232+
(then unreachable))
233+
(local.set $subtask (i32.shr_u (local.get $ret) (i32.const 4)))
234+
235+
;; cancel; completes immediately (C is in cancellable yield)
236+
(local.set $ret (call $subtask.cancel (local.get $subtask)))
237+
(if (i32.ne (i32.const 4 (; CANCELLED_BEFORE_RETURNED ;)) (local.get $ret))
238+
(then unreachable))
239+
(call $subtask.drop (local.get $subtask))
240+
241+
;; ==========================================
242+
;; Test 4: waitable-set.poll cancellable (pending)
243+
;; ==========================================
244+
245+
;; create future for poll-cancel-pending to read
246+
(local.set $ret64 (call $future.new))
247+
(local.set $futr (i32.wrap_i64 (local.get $ret64)))
248+
(local.set $futw (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32))))
249+
250+
;; call poll-cancel-pending; it should block in non-cancellable wait
251+
(local.set $retp (i32.const 4))
252+
(local.set $retp2 (i32.const 8))
253+
(local.set $ret (call $poll-cancel-pending (local.get $futr) (local.get $retp)))
254+
(if (i32.ne (i32.const 1 (; STARTED ;)) (i32.and (local.get $ret) (i32.const 0xf)))
255+
(then unreachable))
256+
(local.set $subtask (i32.shr_u (local.get $ret) (i32.const 4)))
257+
258+
;; cancel; blocks because C's wait is not cancellable
259+
(local.set $ret (call $subtask.cancel (local.get $subtask)))
260+
(if (i32.ne (i32.const -1 (; BLOCKED ;)) (local.get $ret))
261+
(then unreachable))
262+
263+
;; write to future; unblocks C's non-cancellable wait
264+
(local.set $ret (call $future.write (local.get $futw) (i32.const 0)))
265+
(if (i32.ne (i32.const 0 (; COMPLETED ;)) (local.get $ret))
266+
(then unreachable))
267+
268+
;; wait for subtask to complete
269+
(local.set $ws (call $waitable-set.new))
270+
(call $waitable.join (local.get $subtask) (local.get $ws))
271+
(local.set $event_code (call $waitable-set.wait (local.get $ws) (local.get $retp2)))
272+
(if (i32.ne (i32.const 1 (; SUBTASK ;)) (local.get $event_code))
273+
(then unreachable))
274+
(if (i32.ne (local.get $subtask) (i32.load (local.get $retp2)))
275+
(then unreachable))
276+
(if (i32.ne (i32.const 4 (; CANCELLED_BEFORE_RETURNED ;)) (i32.load offset=4 (local.get $retp2)))
277+
(then unreachable))
278+
(call $subtask.drop (local.get $subtask))
279+
280+
;; ==========================================
281+
;; Test 5: thread.yield cancellable (pending)
282+
;; ==========================================
283+
284+
;; create future for yield-cancel-pending to read
285+
(local.set $ret64 (call $future.new))
286+
(local.set $futr (i32.wrap_i64 (local.get $ret64)))
287+
(local.set $futw (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32))))
288+
289+
;; call yield-cancel-pending; it should block in non-cancellable wait
290+
(local.set $ret (call $yield-cancel-pending (local.get $futr) (local.get $retp)))
291+
(if (i32.ne (i32.const 1 (; STARTED ;)) (i32.and (local.get $ret) (i32.const 0xf)))
292+
(then unreachable))
293+
(local.set $subtask (i32.shr_u (local.get $ret) (i32.const 4)))
294+
295+
;; cancel; blocks because C's wait is not cancellable
296+
(local.set $ret (call $subtask.cancel (local.get $subtask)))
297+
(if (i32.ne (i32.const -1 (; BLOCKED ;)) (local.get $ret))
298+
(then unreachable))
299+
300+
;; write to future; unblocks C's non-cancellable wait
301+
(local.set $ret (call $future.write (local.get $futw) (i32.const 0)))
302+
(if (i32.ne (i32.const 0 (; COMPLETED ;)) (local.get $ret))
303+
(then unreachable))
304+
305+
;; wait for subtask to complete
306+
(local.set $ws (call $waitable-set.new))
307+
(call $waitable.join (local.get $subtask) (local.get $ws))
308+
(local.set $event_code (call $waitable-set.wait (local.get $ws) (local.get $retp2)))
309+
(if (i32.ne (i32.const 1 (; SUBTASK ;)) (local.get $event_code))
310+
(then unreachable))
311+
(if (i32.ne (local.get $subtask) (i32.load (local.get $retp2)))
312+
(then unreachable))
313+
(if (i32.ne (i32.const 4 (; CANCELLED_BEFORE_RETURNED ;)) (i32.load offset=4 (local.get $retp2)))
314+
(then unreachable))
315+
(call $subtask.drop (local.get $subtask))
316+
317+
;; all tests passed
318+
(i32.const 42)
319+
)
320+
)
321+
(canon subtask.cancel async (core func $subtask.cancel))
322+
(canon subtask.drop (core func $subtask.drop))
323+
(canon future.new $FT (core func $future.new))
324+
(canon future.write $FT async (memory $memory "mem") (core func $future.write))
325+
(canon waitable.join (core func $waitable.join))
326+
(canon waitable-set.new (core func $waitable-set.new))
327+
(canon waitable-set.wait (memory $memory "mem") (core func $waitable-set.wait))
328+
(canon lower (func $wait-cancel) async (memory $memory "mem") (core func $wait-cancel'))
329+
(canon lower (func $poll-cancel) async (memory $memory "mem") (core func $poll-cancel'))
330+
(canon lower (func $yield-cancel) async (memory $memory "mem") (core func $yield-cancel'))
331+
(canon lower (func $poll-cancel-pending) async (memory $memory "mem") (core func $poll-cancel-pending'))
332+
(canon lower (func $yield-cancel-pending) async (memory $memory "mem") (core func $yield-cancel-pending'))
333+
(core instance $dm (instantiate $DM (with "" (instance
334+
(export "mem" (memory $memory "mem"))
335+
(export "subtask.cancel" (func $subtask.cancel))
336+
(export "subtask.drop" (func $subtask.drop))
337+
(export "future.new" (func $future.new))
338+
(export "future.write" (func $future.write))
339+
(export "waitable.join" (func $waitable.join))
340+
(export "waitable-set.new" (func $waitable-set.new))
341+
(export "waitable-set.wait" (func $waitable-set.wait))
342+
(export "wait-cancel" (func $wait-cancel'))
343+
(export "poll-cancel" (func $poll-cancel'))
344+
(export "yield-cancel" (func $yield-cancel'))
345+
(export "poll-cancel-pending" (func $poll-cancel-pending'))
346+
(export "yield-cancel-pending" (func $yield-cancel-pending'))
347+
))))
348+
(func (export "run") async (result u32) (canon lift (core func $dm "run")))
349+
)
350+
351+
(instance $c (instantiate $C))
352+
(instance $d (instantiate $D
353+
(with "wait-cancel" (func $c "wait-cancel"))
354+
(with "poll-cancel" (func $c "poll-cancel"))
355+
(with "yield-cancel" (func $c "yield-cancel"))
356+
(with "poll-cancel-pending" (func $c "poll-cancel-pending"))
357+
(with "yield-cancel-pending" (func $c "yield-cancel-pending"))
358+
))
359+
(func (export "run") (alias export $d "run"))
360+
)
361+
(assert_return (invoke "run") (u32.const 42))

0 commit comments

Comments
 (0)