1;;! reference_types = true
2;;! component_model_async = true
3
4(component definition $A
5  ;; A component with a single export that's just an infinitely looping
6  ;; subtask. Used to represent a pending subtask that has yet to resolve.
7  (component $a
8    (core module $m
9      (import "" "cancel" (func $cancel))
10      (func (export "a") (result i32) i32.const 1) ;; CALLBACK_CODE_YIELD
11      (func (export "cb") (param i32 i32 i32) (result i32)
12        local.get 0
13        i32.const 6 ;; EVENT_TASK_CANCELLED
14        i32.eq
15        if (result i32)
16          call $cancel
17          i32.const 0 ;; CALLBACK_CODE_EXIT
18        else
19          i32.const 1 ;; CALLBACK_CODE_YIELD
20        end
21
22        )
23    )
24    (core func $cancel (canon task.cancel))
25    (core instance $i (instantiate $m
26      (with "" (instance
27        (export "cancel" (func $cancel))
28      ))
29    ))
30    (func (export "f") async
31      (canon lift (core func $i "a") async (callback (func $i "cb"))))
32  )
33
34  (component $b
35    (import "f" (func $f async))
36    (core module $m
37      (import "" "f" (func $f (result i32)))
38      (import "" "cancel" (func $cancel (param i32) (result i32)))
39      (import "" "drop" (func $drop (param i32)))
40      (global $subtask (mut i32) (i32.const 0))
41
42      ;; This export starts a call to `$f` in the above component but doesn't
43      ;; await it or complete it. Instead this task exits.
44      (func (export "a") (param $cancel i32)
45        (local $ret i32)
46
47        ;; start the subtask
48        (local.set $ret (call $f))
49
50        ;; verify it's in the `SUBTASK_STARTED` state
51        (i32.ne
52          (i32.and (local.get $ret) (i32.const 0xf))
53          (i32.const 1)) ;; SUBTASK_STARTED
54        if unreachable end
55
56        ;; store the subtask id in a global.
57        (global.set $subtask
58          (i32.shr_u
59            (local.get $ret)
60            (i32.const 4)))
61
62        local.get $cancel
63        if call $call-cancel end
64      )
65
66      (func $call-cancel
67        (i32.ne
68          (call $cancel (global.get $subtask))
69          (i32.const 4)) ;; RETURN_CANCELLED
70        if unreachable end
71      )
72
73      ;; This export tries to cancel/drop the subtask started by "a" above and
74      ;; this shouldn't cause any issues...
75      (func (export "b") (param $cancel i32)
76        local.get $cancel
77        if call $call-cancel end
78        (call $drop (global.get $subtask))
79      )
80    )
81    (core func $f (canon lower (func $f) async))
82    (core func $cancel (canon subtask.cancel))
83    (core func $drop (canon subtask.drop))
84    (core instance $i (instantiate $m
85      (with "" (instance
86        (export "f" (func $f))
87        (export "cancel" (func $cancel))
88        (export "drop" (func $drop))
89      ))
90    ))
91    (func (export "a") async (param "cancel" bool) (canon lift (core func $i "a")))
92    (func (export "b") async (param "cancel" bool) (canon lift (core func $i "b")))
93  )
94
95  (instance $a (instantiate $a))
96  (instance $b (instantiate $b (with "f" (func $a "f"))))
97  (export "a" (func $b "a"))
98  (export "b" (func $b "b"))
99)
100
101;; start subtask in "a", cancel/drop it in "b"
102(component instance $A $A)
103(assert_return (invoke "a" (bool.const false)))
104(assert_return (invoke "b" (bool.const true)))
105
106;; start/cancel subtask in "a", drop it in "b"
107(component instance $A $A)
108(assert_return (invoke "a" (bool.const true)))
109(assert_return (invoke "b" (bool.const false)))
110