1;;! component_model_async = true 2;;! component_model_threading = true 3 4;; Spawns 5 threads, makes them write their indices into a buffer out-of-order, then yields to them in-order, 5;; ensuring that the yield order is as-expected. 6 7;; More concretely: 8;; Thread Index | Assigned Number 9;; 1 | 16 10;; 2 | 12 11;; 3 | 04 12;; 4 | 00 13;; 5 | 08 14 15;; After all threads have spawned and written their indices to the byte position given by their assigned number, 16;; the buffer state will be: 17;; 4 3 5 2 1 18 19;; The main thread will then yield to these threads in the order that they are stored in the buffer, 20;; and they will write their assigned number into the buffer, after the indices. 21;; After all threads have been yielded to, the buffer contents will be: 22;; 4 3 5 2 1 0 4 8 12 16 23 24;; The main thread then ensures that the assigned numbers have been written into the correct locations. 25 26(component 27 ;; Defines the table for the thread start function 28 (core module $libc 29 (table (export "__indirect_function_table") 1 funcref)) 30 ;; Defines the thread start function and a function that calls thread.new-indirect 31 (core module $m 32 ;; Import the threading builtins and the table from libc 33 (import "" "thread.new-indirect" (func $thread-new-indirect (param i32 i32) (result i32))) 34 (import "" "thread.suspend" (func $thread-suspend (result i32))) 35 (import "" "thread.yield-to-suspended" (func $thread-yield-to-suspended (param i32) (result i32))) 36 (import "" "thread.suspend-to-suspended" (func $thread-suspend-to-suspended (param i32) (result i32))) 37 (import "" "thread.yield" (func $thread-yield (result i32))) 38 (import "" "thread.index" (func $thread-index (result i32))) 39 (import "" "thread.unsuspend" (func $thread-unsuspend (param i32))) 40 (import "libc" "__indirect_function_table" (table $indirect-function-table 1 funcref)) 41 42 ;; A memory block that threads will write their thread indexes and assigned values into 43 (memory 1) 44 45 ;; A global that points to the next memory index to write into 46 ;; We initialize this to 20 (threads * 4 bytes of storage per thread) 47 (global $g (mut i32) (i32.const 20)) 48 49 ;; The thread entry point, which writes the thread's index into memory at the assigned location, 50 ;; suspends back to the main thread, then writes the assigned value into memory 51 (func $thread-start (param i32) 52 ;; Store the thread index into the assigned location 53 (i32.store (local.get 0) (call $thread-index)) 54 (drop (call $thread-suspend)) 55 (i32.store (global.get $g) (local.get 0)) 56 (global.set $g 57 (i32.add (global.get $g) (i32.const 4)))) 58 (export "thread-start" (func $thread-start)) 59 60 ;; Initialize the function table with our thread-start function; this will be 61 ;; used by thread.new-indirect 62 (elem (table $indirect-function-table) (i32.const 0) func $thread-start) 63 64 (func $new-thread (param i32) 65 (drop 66 (call $thread-yield-to-suspended 67 (call $thread-new-indirect (i32.const 0) (local.get 0))))) 68 69 ;; The main entry point 70 (func (export "run") (result i32) 71 ;; Spawn 5 new threads with assigned numbers 72 (call $new-thread (i32.const 16)) 73 (call $new-thread (i32.const 12)) 74 (call $new-thread (i32.const 4)) 75 (call $new-thread (i32.const 0)) 76 (call $new-thread (i32.const 8)) 77 78 ;; Yield to all threads in ascending order of assigned number 79 (drop (call $thread-yield-to-suspended (i32.load (i32.const 0)))) 80 (drop (call $thread-yield-to-suspended (i32.load (i32.const 4)))) 81 (drop (call $thread-yield-to-suspended (i32.load (i32.const 8)))) 82 (drop (call $thread-yield-to-suspended (i32.load (i32.const 12)))) 83 (drop (call $thread-yield-to-suspended (i32.load (i32.const 16)))) 84 85 ;; Ensure all assigned numbers have been written to the buffer in order 86 (if (i32.ne (i32.load (i32.const 20)) (i32.const 0)) (then unreachable)) 87 (if (i32.ne (i32.load (i32.const 24)) (i32.const 4)) (then unreachable)) 88 (if (i32.ne (i32.load (i32.const 28)) (i32.const 8)) (then unreachable)) 89 (if (i32.ne (i32.load (i32.const 32)) (i32.const 12)) (then unreachable)) 90 (if (i32.ne (i32.load (i32.const 36)) (i32.const 16)) (then unreachable)) 91 92 ;; Sentinel value 93 (i32.const 42))) 94 95 ;; Instantiate the libc module to get the table 96 (core instance $libc (instantiate $libc)) 97 ;; Get access to `thread.new-indirect` that uses the table from libc 98 (core type $start-func-ty (func (param i32))) 99 (alias core export $libc "__indirect_function_table" (core table $indirect-function-table)) 100 101 (core func $thread-new-indirect 102 (canon thread.new-indirect $start-func-ty (table $indirect-function-table))) 103 (core func $thread-yield (canon thread.yield)) 104 (core func $thread-index (canon thread.index)) 105 (core func $thread-yield-to-suspended (canon thread.yield-to-suspended)) 106 (core func $thread-unsuspend (canon thread.unsuspend)) 107 (core func $thread-suspend-to-suspended (canon thread.suspend-to-suspended)) 108 (core func $thread-suspend (canon thread.suspend)) 109 110 ;; Instantiate the main module 111 (core instance $i ( 112 instantiate $m 113 (with "" (instance 114 (export "thread.new-indirect" (func $thread-new-indirect)) 115 (export "thread.index" (func $thread-index)) 116 (export "thread.yield-to-suspended" (func $thread-yield-to-suspended)) 117 (export "thread.yield" (func $thread-yield)) 118 (export "thread.suspend-to-suspended" (func $thread-suspend-to-suspended)) 119 (export "thread.suspend" (func $thread-suspend)) 120 (export "thread.unsuspend" (func $thread-unsuspend)))) 121 (with "libc" (instance $libc)))) 122 123 ;; Export the main entry point 124 (func (export "run") async (result u32) (canon lift (core func $i "run")))) 125 126(assert_return (invoke "run") (u32.const 42)) 127