1e06fbf70SSy Brand;;! component_model_async = true
2e06fbf70SSy Brand;;! component_model_threading = true
3e06fbf70SSy Brand
4e06fbf70SSy Brand;; Tests for basic functioning of all threading builtins with the implicit thread + one explicit thread
5e06fbf70SSy Brand;; Switches between threads using all of the different threading intrinsics.
6e06fbf70SSy Brand
7e06fbf70SSy Brand(component
8e06fbf70SSy Brand    ;; Defines the table for the thread start function
9e06fbf70SSy Brand    (core module $libc
10e06fbf70SSy Brand        (table (export "__indirect_function_table") 1 funcref))
11020727d0SAlex Crichton    ;; Defines the thread start function and a function that calls thread.new-indirect
12e06fbf70SSy Brand    (core module $m
13e06fbf70SSy Brand        ;; Import the threading builtins and the table from libc
14020727d0SAlex Crichton        (import "" "thread.new-indirect" (func $thread-new-indirect (param i32 i32) (result i32)))
15e06fbf70SSy Brand        (import "" "thread.suspend" (func $thread-suspend (result i32)))
16*d2fbd2deSAlex Crichton        (import "" "thread.yield-to-suspended" (func $thread-yield-to-suspended (param i32) (result i32)))
17*d2fbd2deSAlex Crichton        (import "" "thread.suspend-to-suspended" (func $thread-suspend-to-suspended (param i32) (result i32)))
18e06fbf70SSy Brand        (import "" "thread.yield" (func $thread-yield (result i32)))
19e06fbf70SSy Brand        (import "" "thread.index" (func $thread-index (result i32)))
20*d2fbd2deSAlex Crichton        (import "" "thread.unsuspend" (func $thread-unsuspend (param i32)))
21e06fbf70SSy Brand        (import "libc" "__indirect_function_table" (table $indirect-function-table 1 funcref))
22e06fbf70SSy Brand
23e06fbf70SSy Brand        ;; A global that we will set from the spawned thread
24e06fbf70SSy Brand        (global $g (mut i32) (i32.const 0))
25e06fbf70SSy Brand        (global $main-thread-index (mut i32) (i32.const 0))
26e06fbf70SSy Brand
27e06fbf70SSy Brand        ;; The thread entry point, which sets the global to incrementing values starting from the context value
28e06fbf70SSy Brand        (func $thread-start (param i32)
29e06fbf70SSy Brand            ;; Set the global to the context value
30e06fbf70SSy Brand            (global.set $g (local.get 0))
31e06fbf70SSy Brand            ;; The main thread switched to us, so is no longer scheduled, so we explicitly schedule it
32*d2fbd2deSAlex Crichton            (call $thread-unsuspend (global.get $main-thread-index))
33e06fbf70SSy Brand            ;; Yield back to the main thread (since that is the only other one)
34e06fbf70SSy Brand            (drop (call $thread-yield)
35e06fbf70SSy Brand            ;; Increment the global
36e06fbf70SSy Brand            (global.set $g (i32.add (global.get $g) (i32.const 1)))
37e06fbf70SSy Brand            ;; The main thread will have explicitly requested suspension, so yield to it directly
38*d2fbd2deSAlex Crichton            (drop (call $thread-yield-to-suspended (global.get $main-thread-index)))
39e06fbf70SSy Brand            ;; Increment the global again
40e06fbf70SSy Brand            (global.set $g (i32.add (global.get $g) (i32.const 1)))
41e06fbf70SSy Brand            ;; Reschedule the main thread so that it runs after we exit
42*d2fbd2deSAlex Crichton            (call $thread-unsuspend (global.get $main-thread-index))))
43e06fbf70SSy Brand        (export "thread-start" (func $thread-start))
44e06fbf70SSy Brand
45e06fbf70SSy Brand        ;; Initialize the function table with our thread-start function; this will be
46020727d0SAlex Crichton        ;; used by thread.new-indirect
47e06fbf70SSy Brand        (elem (table $indirect-function-table) (i32.const 0) func $thread-start)
48e06fbf70SSy Brand
49e06fbf70SSy Brand        ;; The main entry point, which spawns a new thread to run `thread-start`, passing 42
50e06fbf70SSy Brand        ;; as the context value, and then yields to it
51e06fbf70SSy Brand        (func (export "run") (result i32)
52e06fbf70SSy Brand            ;; Store the main thread's index for the spawned thread to yield to
53e06fbf70SSy Brand            (global.set $main-thread-index (call $thread-index))
54e06fbf70SSy Brand            ;; Create a new thread, which starts suspended, and switch to it
55e06fbf70SSy Brand            (drop
56*d2fbd2deSAlex Crichton                (call $thread-suspend-to-suspended
57e06fbf70SSy Brand                    (call $thread-new-indirect (i32.const 0) (i32.const 42))))
58e06fbf70SSy Brand            ;; After the thread yields back to us, check that the global was set to 42
59e06fbf70SSy Brand            (if (i32.ne (global.get $g) (i32.const 42)) (then unreachable))
60e06fbf70SSy Brand            ;; Suspend ourselves, which will cause the spawned thread to run
61e06fbf70SSy Brand            (drop (call $thread-suspend))
62e06fbf70SSy Brand            ;; The spawned thread will resume us after incrementing the global, so check that it is now 43
63e06fbf70SSy Brand            (if (i32.ne (global.get $g) (i32.const 43)) (then unreachable))
64e06fbf70SSy Brand            ;; Suspend again, which will cause the spawned thread to run again
65e06fbf70SSy Brand            (drop (call $thread-suspend))
66e06fbf70SSy Brand            ;; The spawned thread will reschedule us before it exits, so when we resume here the global should be 44
67e06fbf70SSy Brand            (if (i32.ne (global.get $g) (i32.const 44)) (then unreachable))
68e06fbf70SSy Brand            ;; Return success
69e06fbf70SSy Brand            (i32.const 42)))
70e06fbf70SSy Brand
71e06fbf70SSy Brand    ;; Instantiate the libc module to get the table
72e06fbf70SSy Brand    (core instance $libc (instantiate $libc))
73020727d0SAlex Crichton    ;; Get access to `thread.new-indirect` that uses the table from libc
74e06fbf70SSy Brand    (core type $start-func-ty (func (param i32)))
75e06fbf70SSy Brand    (alias core export $libc "__indirect_function_table" (core table $indirect-function-table))
76e06fbf70SSy Brand
77e06fbf70SSy Brand    (core func $thread-new-indirect
78020727d0SAlex Crichton        (canon thread.new-indirect $start-func-ty (table $indirect-function-table)))
79e06fbf70SSy Brand    (core func $thread-yield (canon thread.yield))
80e06fbf70SSy Brand    (core func $thread-index (canon thread.index))
81*d2fbd2deSAlex Crichton    (core func $thread-yield-to-suspended (canon thread.yield-to-suspended))
82*d2fbd2deSAlex Crichton    (core func $thread-unsuspend (canon thread.unsuspend))
83*d2fbd2deSAlex Crichton    (core func $thread-suspend-to-suspended (canon thread.suspend-to-suspended))
84e06fbf70SSy Brand    (core func $thread-suspend (canon thread.suspend))
85e06fbf70SSy Brand
86e06fbf70SSy Brand    ;; Instantiate the main module
87e06fbf70SSy Brand    (core instance $i (
88e06fbf70SSy Brand        instantiate $m
89e06fbf70SSy Brand            (with "" (instance
90020727d0SAlex Crichton                (export "thread.new-indirect" (func $thread-new-indirect))
91e06fbf70SSy Brand                (export "thread.index" (func $thread-index))
92*d2fbd2deSAlex Crichton                (export "thread.yield-to-suspended" (func $thread-yield-to-suspended))
93e06fbf70SSy Brand                (export "thread.yield" (func $thread-yield))
94*d2fbd2deSAlex Crichton                (export "thread.suspend-to-suspended" (func $thread-suspend-to-suspended))
95e06fbf70SSy Brand                (export "thread.suspend" (func $thread-suspend))
96*d2fbd2deSAlex Crichton                (export "thread.unsuspend" (func $thread-unsuspend))))
97e06fbf70SSy Brand            (with "libc" (instance $libc))))
98e06fbf70SSy Brand
99e06fbf70SSy Brand    ;; Export the main entry point
1008992b99bSJoel Dice    (func (export "run") async (result u32) (canon lift (core func $i "run"))))
101e06fbf70SSy Brand
102e06fbf70SSy Brand(assert_return (invoke "run") (u32.const 42))
1034d129904SAlex Crichton
1044d129904SAlex Crichton;; Test that `thread.index` is exempt from may-leave checks
1054d129904SAlex Crichton(component
1064d129904SAlex Crichton  (core func $thread.index (canon thread.index))
1074d129904SAlex Crichton
1084d129904SAlex Crichton  (core module $DM
1094d129904SAlex Crichton    (import "" "thread.index" (func $thread.index (result i32)))
1104d129904SAlex Crichton
1114d129904SAlex Crichton    (func (export "run"))
1124d129904SAlex Crichton    (func (export "post-return") call $thread.index drop)
1134d129904SAlex Crichton  )
1144d129904SAlex Crichton  (core instance $dm (instantiate $DM (with "" (instance
1154d129904SAlex Crichton    (export "thread.index" (func $thread.index))
1164d129904SAlex Crichton  ))))
1174d129904SAlex Crichton  (func (export "run")
1184d129904SAlex Crichton    (canon lift (core func $dm "run") (post-return (func $dm "post-return"))))
1194d129904SAlex Crichton)
1204d129904SAlex Crichton
1214d129904SAlex Crichton(assert_return (invoke "run"))
122