1e06fbf70SSy Brand;;! component_model_async = true
2e06fbf70SSy Brand;;! component_model_async_stackful = true
3e06fbf70SSy Brand;;! component_model_async_builtins = true
4e06fbf70SSy Brand;;! component_model_threading = true
5e06fbf70SSy Brand;;! reference_types = true
6e06fbf70SSy Brand
7e06fbf70SSy Brand;; Tests that cancellation works with the async threading intrinsics.
8e06fbf70SSy Brand;; Consists of two components, C and D. C implements functions that mix cancellable and uncancellable yields and suspensions.
9e06fbf70SSy Brand;; D calls these functions and cancels the resulting subtasks, ensuring that cancellation is only seen when expected.
10e06fbf70SSy Brand
11e06fbf70SSy Brand;; -- Component C --
12e06fbf70SSy Brand
13e06fbf70SSy Brand;; `run-yield`: Yields twice, first with an uncancellable yield, then with a cancellable yield.
14e06fbf70SSy Brand;;      The caller cancels the subtask during the first yield, and ensures that the cancellation only takes effect
15e06fbf70SSy Brand;;      on the second yield.
16e06fbf70SSy Brand
17*d2fbd2deSAlex Crichton;; `run-yield-to-suspended`: Yields twice to a spawned thread, first with an uncancellable yield, then with a cancellable yield.
18e06fbf70SSy Brand;;      A complication is that we can't guarantee that if the spawned thread yields, the supertask will be scheduled to
19e06fbf70SSy Brand;;      cancel the subtask before the subtask's implicit thread is rescheduled. To handle this, the subtask's implicit
20e06fbf70SSy Brand;;      thread first waits on a future to be written by the supertask, then yields to the spawned thread.
21e06fbf70SSy Brand
22e06fbf70SSy Brand;; `run-suspend`: More complex, because executing an uncancellable suspension requires another
23e06fbf70SSy Brand;;      thread in the same subtask to explicitly wake it up. This is done by the subtask spawning a new thread that
24e06fbf70SSy Brand;;      waits on a future to be written by the supertask, and then resumes the main thread once that happens.
25e06fbf70SSy Brand;;      After setting up this thread, `run-suspend` performs an uncancellable suspend, then a cancellable suspend.
26e06fbf70SSy Brand;;      The caller cancels the subtask during the first suspend, writes to the future to make the spawned thread
27e06fbf70SSy Brand;;      resume the implicit thread, and ensures that the cancellation only takes effect on the second suspend.
28e06fbf70SSy Brand
29*d2fbd2deSAlex Crichton;; `run-suspend-to-suspended`: Similar to `run-suspend`, but uses `thread.suspend-to-suspended` instead of `thread.suspend`.
30e06fbf70SSy Brand
31e06fbf70SSy Brand;; -- Component D --
32e06fbf70SSy Brand
33e06fbf70SSy Brand;; `run-test`: Calls one of the functions in C based on a test id, cancels the resulting subtask, and ensures that
34e06fbf70SSy Brand;;      cancellation is only seen when expected.
35e06fbf70SSy Brand
36e06fbf70SSy Brand;; `run`: Calls `run-test` for each of the functions in C.
37e06fbf70SSy Brand
38e06fbf70SSy Brand(component
39e06fbf70SSy Brand    (component $C
40e06fbf70SSy Brand        (type $FT (future))
41e06fbf70SSy Brand        (core module $Memory (memory (export "mem") 1))
42e06fbf70SSy Brand        (core instance $memory (instantiate $Memory))
43e06fbf70SSy Brand        ;; Defines the table for the thread start functions, of which there are two
44e06fbf70SSy Brand        (core module $libc
45e06fbf70SSy Brand            (table (export "__indirect_function_table") 2 funcref))
46e06fbf70SSy Brand        (core module $CM
47e06fbf70SSy Brand            (import "" "mem" (memory 1))
48e06fbf70SSy Brand            (import "" "task.cancel" (func $task-cancel))
49020727d0SAlex Crichton            (import "" "thread.new-indirect" (func $thread-new-indirect (param i32 i32) (result i32)))
50e06fbf70SSy Brand            (import "" "thread.suspend" (func $thread-suspend (result i32)))
51e06fbf70SSy Brand            (import "" "thread.suspend-cancellable" (func $thread-suspend-cancellable (result i32)))
52*d2fbd2deSAlex Crichton            (import "" "thread.yield-to-suspended" (func $thread-yield-to-suspended (param i32) (result i32)))
53*d2fbd2deSAlex Crichton            (import "" "thread.yield-to-suspended-cancellable" (func $thread-yield-to-suspended-cancellable (param i32) (result i32)))
54*d2fbd2deSAlex Crichton            (import "" "thread.suspend-to-suspended" (func $thread-suspend-to-suspended (param i32) (result i32)))
55*d2fbd2deSAlex Crichton            (import "" "thread.suspend-to-suspended-cancellable" (func $thread-suspend-to-suspended-cancellable (param i32) (result i32)))
56e06fbf70SSy Brand            (import "" "thread.yield" (func $thread-yield (result i32)))
57e06fbf70SSy Brand            (import "" "thread.yield-cancellable" (func $thread-yield-cancellable (result i32)))
58e06fbf70SSy Brand            (import "" "thread.index" (func $thread-index (result i32)))
59*d2fbd2deSAlex Crichton            (import "" "thread.unsuspend" (func $thread-unsuspend (param i32)))
60e06fbf70SSy Brand            (import "" "future.read" (func $future.read (param i32 i32) (result i32)))
61e06fbf70SSy Brand            (import "" "waitable.join" (func $waitable.join (param i32 i32)))
62e06fbf70SSy Brand            (import "" "waitable-set.new" (func $waitable-set.new (result i32)))
63e06fbf70SSy Brand            (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32)))
64e06fbf70SSy Brand            (import "libc" "__indirect_function_table" (table $indirect-function-table 2 funcref))
65e06fbf70SSy Brand
66e06fbf70SSy Brand            ;; Indices into the function table for the thread start functions
67e06fbf70SSy Brand            (global $wake-from-suspend-ftbl-idx i32 (i32.const 0))
68e06fbf70SSy Brand            (global $just-yield-ftbl-idx i32 (i32.const 1))
69e06fbf70SSy Brand
70e06fbf70SSy Brand            (func (export "run-yield")
71e06fbf70SSy Brand                ;; Yield back to the caller, who will attempt to cancel us, but we won't see it
72e06fbf70SSy Brand                ;; because we're using an uncancellable yield
73e06fbf70SSy Brand                (if (i32.ne (call $thread-yield) (i32.const 0)) (then unreachable))
74e06fbf70SSy Brand                ;; Yield back to the caller again. This time, we should receive the cancellation immediately.
75e06fbf70SSy Brand                (if (i32.ne (call $thread-yield-cancellable) (i32.const 1)) (then unreachable))
76e06fbf70SSy Brand                (call $task-cancel)
77e06fbf70SSy Brand            )
78e06fbf70SSy Brand
79e06fbf70SSy Brand            (func $wait-for-future-write (param i32)
80e06fbf70SSy Brand                (local $ret i32)
81e06fbf70SSy Brand                ;; Perform a future.read, which will block, waiting for the supertask to write
82e06fbf70SSy Brand                (local.set $ret (call $future.read (local.get 0) (i32.const 0xba5eba11)))
83e06fbf70SSy Brand                (if (i32.ne (i32.const 0 (; COMPLETED ;)) (local.get $ret))
84e06fbf70SSy Brand                    (then unreachable))
85e06fbf70SSy Brand            )
86e06fbf70SSy Brand
87e06fbf70SSy Brand            (func $wake-from-suspend (param i32)
88e06fbf70SSy Brand                ;; Extract the thread index and future to wait on from the argument structure
89e06fbf70SSy Brand                (local $thread-index i32) (local $future i32)
90e06fbf70SSy Brand                (local.set $thread-index (i32.load offset=0 (local.get 0)))
91e06fbf70SSy Brand                (local.set $future (i32.load offset=4 (local.get 0)))
92e06fbf70SSy Brand
93e06fbf70SSy Brand                ;; Wait for the supertask to signal us to wake up suspended thread.
94e06fbf70SSy Brand                (call $wait-for-future-write (local.get $future))
95e06fbf70SSy Brand                ;; Resume the main thread, which is suspended in an uncancellable suspend
96*d2fbd2deSAlex Crichton                (call $thread-unsuspend (local.get $thread-index))
97e06fbf70SSy Brand            )
98e06fbf70SSy Brand
99e06fbf70SSy Brand            (func $just-yield (param $explicit-thread-idx i32)
100e06fbf70SSy Brand                ;; Yield nondeterministically, either back to the supertask, who will then wait on cancellation to be acknowledged,
101e06fbf70SSy Brand                ;; or to the implicit thread, who will acknowledge the cancellation.
102e06fbf70SSy Brand                (if (i32.ne (call $thread-yield) (i32.const 0)) (then unreachable))
103e06fbf70SSy Brand            )
104e06fbf70SSy Brand
105020727d0SAlex Crichton            ;; Initialize the function table that will be used by thread.new-indirect
106e06fbf70SSy Brand            (elem (table $indirect-function-table) (i32.const 0 (; wake-from-suspend-ftbl-idx ;)) func $wake-from-suspend)
107e06fbf70SSy Brand            (elem (table $indirect-function-table) (i32.const 1 (; just-yield-ftbl-idx ;)) func $just-yield)
108e06fbf70SSy Brand
109*d2fbd2deSAlex Crichton            (func (export "run-yield-to-suspended") (param $futr i32)
110e06fbf70SSy Brand                (local $thread-index i32)
111e06fbf70SSy Brand                ;; Spawn a new thread that will wake us up from our uncancellable suspend; we'll switch to it next
112e06fbf70SSy Brand                (local.set $thread-index
113e06fbf70SSy Brand                    (call $thread-new-indirect (global.get $just-yield-ftbl-idx) (call $thread-index)))
114e06fbf70SSy Brand
115e06fbf70SSy Brand                ;; We can't guarantee that the supertask will be scheduled to cancel us before we're rescheduled, so we first
116e06fbf70SSy Brand                ;; wait on the future to be written, then yield to the spawned thread. This means that cancellation will be
117e06fbf70SSy Brand                ;; sent while we're waiting on the future rather than at the yield point, but the cancel will still be pending
118e06fbf70SSy Brand                ;; when we reach the yield point, so it should still be ignored by the uncancellable yield and only take effect
119e06fbf70SSy Brand                ;; when we reach the second, cancellable yield.
120e06fbf70SSy Brand                (call $wait-for-future-write (local.get $futr))
121e06fbf70SSy Brand
122e06fbf70SSy Brand                ;; Yield to the spawned thread uncancellably. We should eventually be rescheduled without being notified
123e06fbf70SSy Brand                ;; of the pending cancellation.
124*d2fbd2deSAlex Crichton                (if (i32.ne (call $thread-yield-to-suspended (local.get $thread-index)) (i32.const 0)) (then unreachable))
125e06fbf70SSy Brand                ;; Yield to the spawned thread again. This time we should see the cancellation immediately.
126*d2fbd2deSAlex Crichton                (if (i32.ne (call $thread-yield-to-suspended-cancellable (local.get $thread-index)) (i32.const 1)) (then unreachable))
127e06fbf70SSy Brand                (call $task-cancel)
128e06fbf70SSy Brand            )
129e06fbf70SSy Brand
130e06fbf70SSy Brand            (func (export "run-suspend") (param $futr i32)
131e06fbf70SSy Brand                ;; Set up the arguments for the wake-for-suspend thread start function.
132e06fbf70SSy Brand                ;; It expects a pointer to a structure containing the thread index to resume
133e06fbf70SSy Brand                ;; and the future to wait on before resuming it.
134e06fbf70SSy Brand                (local $wake-from-suspend-argp i32)
135e06fbf70SSy Brand                (local.set $wake-from-suspend-argp (i32.const 4))
136e06fbf70SSy Brand                (i32.store offset=0 (local.get $wake-from-suspend-argp) (call $thread-index))
137e06fbf70SSy Brand                (i32.store offset=4 (local.get $wake-from-suspend-argp) (local.get $futr))
138e06fbf70SSy Brand
139e06fbf70SSy Brand                ;; Spawn a new thread that will wake us up from our uncancellable suspend and schedule
140e06fbf70SSy Brand                ;; it to resume after we suspend.
141*d2fbd2deSAlex Crichton                (call $thread-unsuspend
142e06fbf70SSy Brand                    (call $thread-new-indirect (global.get $wake-from-suspend-ftbl-idx) (local.get $wake-from-suspend-argp)))
143e06fbf70SSy Brand
144e06fbf70SSy Brand                ;; Request suspension. We will not be woken up by cancellation, because this is an uncancellable
145e06fbf70SSy Brand                ;; suspend. We will be woken up by the other thread we spawned above, which will be resumed after
146e06fbf70SSy Brand                ;; the supertask cancels our subtask.
147e06fbf70SSy Brand                (if (i32.ne (call $thread-suspend) (i32.const 0)) (then unreachable))
148e06fbf70SSy Brand                ;; Request suspension again. This time we should see the cancellation immediately.
149e06fbf70SSy Brand                (if (i32.ne (call $thread-suspend-cancellable) (i32.const 1)) (then unreachable))
150e06fbf70SSy Brand                (call $task-cancel)
151e06fbf70SSy Brand            )
152e06fbf70SSy Brand
153*d2fbd2deSAlex Crichton            (func (export "run-suspend-to-suspended") (param $futr i32)
154e06fbf70SSy Brand                (local $thread-index i32)
155e06fbf70SSy Brand                ;; Set up the arguments for the wake-for-suspend thread start function.
156e06fbf70SSy Brand                ;; It expects a pointer to a structure containing the thread index to resume
157e06fbf70SSy Brand                ;; and the future to wait on before resuming it.
158e06fbf70SSy Brand                (local $wake-from-suspend-argp i32)
159e06fbf70SSy Brand                (local.set $wake-from-suspend-argp (i32.const 4))
160e06fbf70SSy Brand                (i32.store offset=0 (local.get $wake-from-suspend-argp) (call $thread-index))
161e06fbf70SSy Brand                (i32.store offset=4 (local.get $wake-from-suspend-argp) (local.get $futr))
162e06fbf70SSy Brand
163e06fbf70SSy Brand                ;; Spawn a new thread that will wake us up from our uncancellable suspend; we'll switch to it next
164e06fbf70SSy Brand                (local.set $thread-index
165e06fbf70SSy Brand                    (call $thread-new-indirect (global.get $wake-from-suspend-ftbl-idx) (local.get $wake-from-suspend-argp)))
166e06fbf70SSy Brand
167e06fbf70SSy Brand                ;; Request suspension by switching to the spawned thread.
168e06fbf70SSy Brand                ;; We will not be woken up by cancellation, because this is an uncancellable suspend.
169e06fbf70SSy Brand                ;; We will be woken up by the other thread we spawned above, which will be resumed after
170e06fbf70SSy Brand                ;; the supertask cancels our subtask.
171*d2fbd2deSAlex Crichton                (if (i32.ne (call $thread-suspend-to-suspended (local.get $thread-index)) (i32.const 0)) (then unreachable))
172e06fbf70SSy Brand                ;; Request suspension again. This time we should see the cancellation immediately.
173*d2fbd2deSAlex Crichton                (if (i32.ne (call $thread-suspend-to-suspended-cancellable (local.get $thread-index)) (i32.const 1)) (then unreachable))
174e06fbf70SSy Brand                (call $task-cancel)
175e06fbf70SSy Brand            )
176e06fbf70SSy Brand        )
177e06fbf70SSy Brand
178e06fbf70SSy Brand        ;; Instantiate the libc module to get the table
179e06fbf70SSy Brand        (core instance $libc (instantiate $libc))
180020727d0SAlex Crichton        ;; Get access to `thread.new-indirect` that uses the table from libc
181e06fbf70SSy Brand        (core type $start-func-ty (func (param i32)))
182e06fbf70SSy Brand        (alias core export $libc "__indirect_function_table" (core table $indirect-function-table))
183e06fbf70SSy Brand
184e06fbf70SSy Brand        (core func $task-cancel (canon task.cancel))
185e06fbf70SSy Brand        (core func $thread-new-indirect
186020727d0SAlex Crichton            (canon thread.new-indirect $start-func-ty (table $indirect-function-table)))
187e06fbf70SSy Brand        (core func $thread-yield (canon thread.yield))
188e06fbf70SSy Brand        (core func $thread-yield-cancellable (canon thread.yield cancellable))
189e06fbf70SSy Brand        (core func $thread-index (canon thread.index))
190*d2fbd2deSAlex Crichton        (core func $thread-yield-to-suspended (canon thread.yield-to-suspended))
191*d2fbd2deSAlex Crichton        (core func $thread-yield-to-suspended-cancellable (canon thread.yield-to-suspended cancellable))
192*d2fbd2deSAlex Crichton        (core func $thread-unsuspend (canon thread.unsuspend))
193*d2fbd2deSAlex Crichton        (core func $thread-suspend-to-suspended (canon thread.suspend-to-suspended))
194*d2fbd2deSAlex Crichton        (core func $thread-suspend-to-suspended-cancellable (canon thread.suspend-to-suspended cancellable))
195e06fbf70SSy Brand        (core func $thread-suspend (canon thread.suspend))
196e06fbf70SSy Brand        (core func $thread-suspend-cancellable (canon thread.suspend cancellable))
197e06fbf70SSy Brand        (core func $future.read (canon future.read $FT (memory $memory "mem")))
198e06fbf70SSy Brand        (core func $waitable-set.new (canon waitable-set.new))
199e06fbf70SSy Brand        (core func $waitable.join (canon waitable.join))
200e06fbf70SSy Brand        (core func $waitable-set.wait (canon waitable-set.wait (memory $memory "mem")))
201e06fbf70SSy Brand
202e06fbf70SSy Brand        ;; Instantiate the main module
203e06fbf70SSy Brand        (core instance $cm (
204e06fbf70SSy Brand            instantiate $CM
205e06fbf70SSy Brand                (with "" (instance
206e06fbf70SSy Brand                    (export "mem" (memory $memory "mem"))
207e06fbf70SSy Brand                    (export "task.cancel" (func $task-cancel))
208020727d0SAlex Crichton                    (export "thread.new-indirect" (func $thread-new-indirect))
209e06fbf70SSy Brand                    (export "thread.index" (func $thread-index))
210*d2fbd2deSAlex Crichton                    (export "thread.yield-to-suspended" (func $thread-yield-to-suspended))
211*d2fbd2deSAlex Crichton                    (export "thread.yield-to-suspended-cancellable" (func $thread-yield-to-suspended-cancellable))
212e06fbf70SSy Brand                    (export "thread.yield" (func $thread-yield))
213e06fbf70SSy Brand                    (export "thread.yield-cancellable" (func $thread-yield-cancellable))
214*d2fbd2deSAlex Crichton                    (export "thread.suspend-to-suspended" (func $thread-suspend-to-suspended))
215*d2fbd2deSAlex Crichton                    (export "thread.suspend-to-suspended-cancellable" (func $thread-suspend-to-suspended-cancellable))
216e06fbf70SSy Brand                    (export "thread.suspend" (func $thread-suspend))
217e06fbf70SSy Brand                    (export "thread.suspend-cancellable" (func $thread-suspend-cancellable))
218*d2fbd2deSAlex Crichton                    (export "thread.unsuspend" (func $thread-unsuspend))
219e06fbf70SSy Brand                    (export "future.read" (func $future.read))
220e06fbf70SSy Brand                    (export "waitable.join" (func $waitable.join))
221e06fbf70SSy Brand                    (export "waitable-set.wait" (func $waitable-set.wait))
222e06fbf70SSy Brand                    (export "waitable-set.new" (func $waitable-set.new))))
223e06fbf70SSy Brand                (with "libc" (instance $libc))))
224e06fbf70SSy Brand
2258992b99bSJoel Dice        (func (export "run-yield") async (result u32) (canon lift (core func $cm "run-yield") async))
226*d2fbd2deSAlex Crichton        (func (export "run-yield-to-suspended") async (param "fut" $FT) (result u32) (canon lift (core func $cm "run-yield-to-suspended") async))
2278992b99bSJoel Dice        (func (export "run-suspend") async (param "fut" $FT) (result u32) (canon lift (core func $cm "run-suspend") async))
228*d2fbd2deSAlex Crichton        (func (export "run-suspend-to-suspended") async (param "fut" $FT) (result u32) (canon lift (core func $cm "run-suspend-to-suspended") async))
229e06fbf70SSy Brand    )
230e06fbf70SSy Brand
231e06fbf70SSy Brand    (component $D
232e06fbf70SSy Brand        (type $FT (future))
2338992b99bSJoel Dice        (import "run-yield" (func $run-yield async (result u32)))
234*d2fbd2deSAlex Crichton        (import "run-yield-to-suspended" (func $run-yield-to-suspended async (param "fut" $FT) (result u32)))
2358992b99bSJoel Dice        (import "run-suspend" (func $run-suspend async (param "fut" $FT) (result u32)))
236*d2fbd2deSAlex Crichton        (import "run-suspend-to-suspended" (func $run-suspend-to-suspended async (param "fut" $FT) (result u32)))
237e06fbf70SSy Brand
238e06fbf70SSy Brand        (core module $Memory (memory (export "mem") 1))
239e06fbf70SSy Brand        (core instance $memory (instantiate $Memory))
240e06fbf70SSy Brand        (core module $DM
241e06fbf70SSy Brand            (import "" "mem" (memory 1))
242e06fbf70SSy Brand            (import "" "subtask.cancel" (func $subtask.cancel (param i32) (result i32)))
243e06fbf70SSy Brand            (import "" "run-yield" (func $run-yield (param i32) (result i32)))
244*d2fbd2deSAlex Crichton            (import "" "run-yield-to-suspended" (func $run-yield-to-suspended (param i32 i32) (result i32)))
245e06fbf70SSy Brand            (import "" "run-suspend" (func $run-suspend (param i32 i32) (result i32)))
246*d2fbd2deSAlex Crichton            (import "" "run-suspend-to-suspended" (func $run-suspend-to-suspended (param i32 i32) (result i32)))
247e06fbf70SSy Brand            (import "" "waitable.join" (func $waitable.join (param i32 i32)))
248e06fbf70SSy Brand            (import "" "waitable-set.new" (func $waitable-set.new (result i32)))
249e06fbf70SSy Brand            (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32)))
250e06fbf70SSy Brand            (import "" "future.new" (func $future.new (result i64)))
251e06fbf70SSy Brand            (import "" "future.write" (func $future.write (param i32 i32) (result i32)))
252e06fbf70SSy Brand            (import "" "thread.yield" (func $thread-yield (result i32)))
253e06fbf70SSy Brand
254e06fbf70SSy Brand            (func $run-test (param $test-id i32) (result i32)
255e06fbf70SSy Brand                (local $ret i32) (local $subtask i32)
256e06fbf70SSy Brand                (local $ws i32) (local $event_code i32)
257e06fbf70SSy Brand                (local $run-retp i32) (local $wait-retp i32)
258e06fbf70SSy Brand                (local $ret64 i64) (local $futr i32) (local $futw i32)
259e06fbf70SSy Brand
260*d2fbd2deSAlex Crichton                ;; Set up return value storage for run-suspend/suspend-to-suspended and waitable-set.wait
261e06fbf70SSy Brand                (local.set $run-retp (i32.const 4))
262e06fbf70SSy Brand                (local.set $wait-retp (i32.const 8))
263e06fbf70SSy Brand                (i32.store (local.get $run-retp) (i32.const 0xbad0bad0))
264e06fbf70SSy Brand                (i32.store (local.get $wait-retp) (i32.const 0xbad0bad0))
265e06fbf70SSy Brand
266e06fbf70SSy Brand                ;; Create a future that the subtask may wait on
267e06fbf70SSy Brand                (local.set $ret64 (call $future.new))
268e06fbf70SSy Brand                (local.set $futr (i32.wrap_i64 (local.get $ret64)))
269e06fbf70SSy Brand                (local.set $futw (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32))))
270e06fbf70SSy Brand
271*d2fbd2deSAlex Crichton                ;; Calling run-suspend/suspend-to-suspended will start the thread, which will suspend.
272e06fbf70SSy Brand                ;; This is basically a switch statement:
273e06fbf70SSy Brand                ;; 0: run-yield
274*d2fbd2deSAlex Crichton                ;; 1: run-yield-to-suspended
275e06fbf70SSy Brand                ;; 2: run-suspend
276*d2fbd2deSAlex Crichton                ;; 3: run-suspend-to-suspended
277e06fbf70SSy Brand                (if (i32.eq (local.get $test-id) (i32.const 0))
278e06fbf70SSy Brand                    (then (local.set $ret (call $run-yield (local.get $run-retp))))
279e06fbf70SSy Brand                    (else (if (i32.eq (local.get $test-id) (i32.const 1))
280*d2fbd2deSAlex Crichton                        (then (local.set $ret (call $run-yield-to-suspended (local.get $futr) (local.get $run-retp))))
281e06fbf70SSy Brand                        (else (if (i32.eq (local.get $test-id) (i32.const 2))
282e06fbf70SSy Brand                            (then (local.set $ret (call $run-suspend (local.get $futr) (local.get $run-retp))))
283e06fbf70SSy Brand                            (else (if (i32.eq (local.get $test-id) (i32.const 3))
284*d2fbd2deSAlex Crichton                                (then (local.set $ret (call $run-suspend-to-suspended (local.get $futr) (local.get $run-retp))))
285e06fbf70SSy Brand                                (else unreachable))))))))
286e06fbf70SSy Brand
287e06fbf70SSy Brand                ;; Ensure that the thread started
288e06fbf70SSy Brand                (if (i32.ne (i32.and (local.get $ret) (i32.const 0xF)) (i32.const 1 (; STARTED ;)))
289e06fbf70SSy Brand                 (then unreachable))
290e06fbf70SSy Brand                ;; Extract the subtask index
291e06fbf70SSy Brand                (local.set $subtask (i32.shr_u (local.get $ret) (i32.const 4)))
292e06fbf70SSy Brand                ;; Cancel the subtask, which should block, because the initial suspend/yield is uncancellable
293e06fbf70SSy Brand                (local.set $ret (call $subtask.cancel (local.get $subtask)))
294e06fbf70SSy Brand                ;; Ensure the cancellation blocked
295e06fbf70SSy Brand                (if (i32.ne (local.get $ret) (i32.const -1 (; BLOCKED ;)))
296e06fbf70SSy Brand                 (then unreachable))
297e06fbf70SSy Brand
298e06fbf70SSy Brand                ;; If we're not testing run-yield, the subtask is expecting a write to our future, so write to it
299e06fbf70SSy Brand                (if (i32.ne (local.get $test-id) (i32.const 0))
300e06fbf70SSy Brand                    (then
301e06fbf70SSy Brand                        (local.set $ret (call $future.write (local.get $futw) (i32.const 0xdeadbeef)))
302e06fbf70SSy Brand                        ;; The write should succeed
303e06fbf70SSy Brand                        (if (i32.ne (i32.const 0 (; COMPLETED ;)) (local.get $ret))
304e06fbf70SSy Brand                            (then unreachable))))
305e06fbf70SSy Brand
306e06fbf70SSy Brand                ;; Wait on the subtask, which will eventually progress to a cancellable yield/suspend and acknowledge the cancellation
307e06fbf70SSy Brand                (local.set $ws (call $waitable-set.new))
308e06fbf70SSy Brand                (call $waitable.join (local.get $subtask) (local.get $ws))
309e06fbf70SSy Brand                (local.set $event_code (call $waitable-set.wait (local.get $ws) (local.get $wait-retp)))
310e06fbf70SSy Brand                ;; Ensure we got the subtask event
311e06fbf70SSy Brand                (if (i32.ne (local.get $event_code) (i32.const 1 (; SUBTASK ;)))
312e06fbf70SSy Brand                 (then unreachable))
313e06fbf70SSy Brand                ;; Ensure the subtask index matches
314e06fbf70SSy Brand                (if (i32.ne (local.get $subtask) (i32.load (local.get $wait-retp)))
315e06fbf70SSy Brand                  (then unreachable))
316e06fbf70SSy Brand                ;; Ensure the subtask was cancelled before it returned
317e06fbf70SSy Brand                (if (i32.ne (i32.const 4 (; CANCELLED_BEFORE_RETURNED=4 | (0<<4) ;))
318e06fbf70SSy Brand                            (i32.load offset=4 (local.get $wait-retp)))
319e06fbf70SSy Brand                  (then unreachable))
320e06fbf70SSy Brand
321e06fbf70SSy Brand                ;; Return success
322e06fbf70SSy Brand                (i32.const 42)
323e06fbf70SSy Brand            )
324e06fbf70SSy Brand
325e06fbf70SSy Brand            (func $run (export "run") (result i32)
326e06fbf70SSy Brand                ;; test-id 0: run-yield
327e06fbf70SSy Brand                (if (i32.ne (call $run-test (i32.const 0)) (i32.const 42))
328e06fbf70SSy Brand                    (then unreachable))
329e06fbf70SSy Brand
330*d2fbd2deSAlex Crichton                ;; test-id 1: run-yield-to-suspended
331e06fbf70SSy Brand                (if (i32.ne (call $run-test (i32.const 1)) (i32.const 42))
332e06fbf70SSy Brand                    (then unreachable))
333e06fbf70SSy Brand
334e06fbf70SSy Brand                ;; test-id 2: run-suspend
335e06fbf70SSy Brand                (if (i32.ne (call $run-test (i32.const 2)) (i32.const 42))
336e06fbf70SSy Brand                    (then unreachable))
337e06fbf70SSy Brand
338*d2fbd2deSAlex Crichton                ;; test-id 3: run-suspend-to-suspended
339e06fbf70SSy Brand                (if (i32.ne (call $run-test (i32.const 3)) (i32.const 42))
340e06fbf70SSy Brand                    (then unreachable))
341e06fbf70SSy Brand
342e06fbf70SSy Brand                ;; Return success
343e06fbf70SSy Brand                (i32.const 42)
344e06fbf70SSy Brand            )
345e06fbf70SSy Brand        )
346e06fbf70SSy Brand
347e06fbf70SSy Brand        (core func $waitable-set.new (canon waitable-set.new))
348e06fbf70SSy Brand        (core func $waitable-set.wait (canon waitable-set.wait (memory $memory "mem")))
349e06fbf70SSy Brand        (core func $waitable.join (canon waitable.join))
350e06fbf70SSy Brand        (core func $subtask.cancel (canon subtask.cancel async))
351e06fbf70SSy Brand        (core func $future.new (canon future.new $FT))
352e06fbf70SSy Brand        (core func $future.write (canon future.write $FT (memory $memory "mem")))
353e06fbf70SSy Brand        (core func $thread.yield (canon thread.yield))
354e06fbf70SSy Brand        (canon lower (func $run-yield) async (memory $memory "mem") (core func $run-yield'))
355e06fbf70SSy Brand        (canon lower (func $run-suspend) async (memory $memory "mem") (core func $run-suspend'))
356*d2fbd2deSAlex Crichton        (canon lower (func $run-suspend-to-suspended) async (memory $memory "mem") (core func $run-suspend-to-suspended'))
357*d2fbd2deSAlex Crichton        (canon lower (func $run-yield-to-suspended) async (memory $memory "mem") (core func $run-yield-to-suspended'))
358e06fbf70SSy Brand        (core instance $dm (instantiate $DM (with "" (instance
359e06fbf70SSy Brand            (export "mem" (memory $memory "mem"))
360e06fbf70SSy Brand            (export "run-yield" (func $run-yield'))
361e06fbf70SSy Brand            (export "run-suspend" (func $run-suspend'))
362*d2fbd2deSAlex Crichton            (export "run-suspend-to-suspended" (func $run-suspend-to-suspended'))
363*d2fbd2deSAlex Crichton            (export "run-yield-to-suspended" (func $run-yield-to-suspended'))
364e06fbf70SSy Brand            (export "waitable.join" (func $waitable.join))
365e06fbf70SSy Brand            (export "waitable-set.new" (func $waitable-set.new))
366e06fbf70SSy Brand            (export "waitable-set.wait" (func $waitable-set.wait))
367e06fbf70SSy Brand            (export "subtask.cancel" (func $subtask.cancel))
368e06fbf70SSy Brand            (export "future.new" (func $future.new))
369e06fbf70SSy Brand            (export "future.write" (func $future.write))
370e06fbf70SSy Brand            (export "thread.yield" (func $thread.yield))
371e06fbf70SSy Brand        ))))
3728992b99bSJoel Dice        (func (export "run") async (result u32) (canon lift (core func $dm "run")))
373e06fbf70SSy Brand    )
374e06fbf70SSy Brand
375e06fbf70SSy Brand    (instance $c (instantiate $C))
376e06fbf70SSy Brand    (instance $d (instantiate $D
377e06fbf70SSy Brand        (with "run-yield" (func $c "run-yield"))
378*d2fbd2deSAlex Crichton        (with "run-yield-to-suspended" (func $c "run-yield-to-suspended"))
379e06fbf70SSy Brand        (with "run-suspend" (func $c "run-suspend"))
380*d2fbd2deSAlex Crichton        (with "run-suspend-to-suspended" (func $c "run-suspend-to-suspended"))
381e06fbf70SSy Brand    ))
382e06fbf70SSy Brand  (func (export "run") (alias export $d "run"))
383e06fbf70SSy Brand)
384e06fbf70SSy Brand
385e06fbf70SSy Brand(assert_return (invoke "run") (u32.const 42))
386