1 mod bindings { 2 wit_bindgen::generate!({ 3 path: "../misc/component-async-tests/wit", 4 world: "intertask-communication", 5 }); 6 } 7 8 use { 9 std::sync::atomic::{AtomicU32, Ordering::Relaxed}, 10 test_programs::async_::{ 11 BLOCKED, CALLBACK_CODE_EXIT, CALLBACK_CODE_WAIT, COMPLETED, EVENT_FUTURE_WRITE, EVENT_NONE, 12 context_get, context_set, waitable_join, waitable_set_drop, waitable_set_new, 13 }, 14 }; 15 16 #[cfg(target_arch = "wasm32")] 17 #[link(wasm_import_module = "[export]local:local/run")] 18 unsafe extern "C" { 19 #[link_name = "[task-return]run"] 20 fn task_return_run(); 21 } 22 #[cfg(not(target_arch = "wasm32"))] 23 unsafe extern "C" fn task_return_run() { 24 unreachable!() 25 } 26 27 fn future_new() -> (u32, u32) { 28 #[cfg(target_arch = "wasm32")] 29 #[link(wasm_import_module = "local:local/intertask")] 30 unsafe extern "C" { 31 #[link_name = "[future-new-0]foo"] 32 fn future_new() -> u64; 33 } 34 #[cfg(not(target_arch = "wasm32"))] 35 unsafe extern "C" fn future_new() -> u64 { 36 unreachable!() 37 } 38 39 let pair = unsafe { future_new() }; 40 ( 41 (pair >> 32).try_into().unwrap(), 42 (pair & 0xFFFFFFFF_u64).try_into().unwrap(), 43 ) 44 } 45 46 fn future_write(writer: u32) -> u32 { 47 #[cfg(target_arch = "wasm32")] 48 #[link(wasm_import_module = "local:local/intertask")] 49 unsafe extern "C" { 50 #[link_name = "[async-lower][future-write-0]foo"] 51 fn future_write(_: u32, _: u32) -> u32; 52 } 53 #[cfg(not(target_arch = "wasm32"))] 54 unsafe extern "C" fn future_write(_: u32, _: u32) -> u32 { 55 unreachable!() 56 } 57 58 unsafe { future_write(writer, 0) } 59 } 60 61 fn future_read(reader: u32) -> u32 { 62 #[cfg(target_arch = "wasm32")] 63 #[link(wasm_import_module = "local:local/intertask")] 64 unsafe extern "C" { 65 #[link_name = "[async-lower][future-read-0]foo"] 66 fn future_read(_: u32, _: u32) -> u32; 67 } 68 #[cfg(not(target_arch = "wasm32"))] 69 unsafe extern "C" fn future_read(_: u32, _: u32) -> u32 { 70 unreachable!() 71 } 72 73 unsafe { future_read(reader, 0) } 74 } 75 76 fn future_drop_readable(reader: u32) { 77 #[cfg(target_arch = "wasm32")] 78 #[link(wasm_import_module = "local:local/intertask")] 79 unsafe extern "C" { 80 #[link_name = "[future-drop-readable-0]foo"] 81 fn future_drop_readable(_: u32); 82 } 83 #[cfg(not(target_arch = "wasm32"))] 84 unsafe extern "C" fn future_drop_readable(_: u32) { 85 unreachable!() 86 } 87 88 unsafe { future_drop_readable(reader) } 89 } 90 91 fn future_drop_writable(writer: u32) { 92 #[cfg(target_arch = "wasm32")] 93 #[link(wasm_import_module = "local:local/intertask")] 94 unsafe extern "C" { 95 #[link_name = "[future-drop-writable-0]foo"] 96 fn future_drop_writable(_: u32); 97 } 98 #[cfg(not(target_arch = "wasm32"))] 99 unsafe extern "C" fn future_drop_writable(_: u32) { 100 unreachable!() 101 } 102 103 unsafe { future_drop_writable(writer) } 104 } 105 106 static TASK_NUMBER: AtomicU32 = AtomicU32::new(0); 107 static SET: AtomicU32 = AtomicU32::new(0); 108 109 enum State { 110 S0 { number: u32 }, 111 S1 { set: u32 }, 112 } 113 114 #[unsafe(export_name = "[async-lift]local:local/run#run")] 115 unsafe extern "C" fn export_run() -> u32 { 116 unsafe { 117 context_set( 118 u32::try_from(Box::into_raw(Box::new(State::S0 { 119 number: TASK_NUMBER.fetch_add(1, Relaxed), 120 })) as usize) 121 .unwrap(), 122 ); 123 callback_run(EVENT_NONE, 0, 0) 124 } 125 } 126 127 #[unsafe(export_name = "[callback][async-lift]local:local/run#run")] 128 unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 { 129 unsafe { 130 let state = &mut *(usize::try_from(context_get()).unwrap() as *mut State); 131 match state { 132 State::S0 { number } => { 133 assert_eq!(event0, EVENT_NONE); 134 135 match *number { 136 0 => { 137 // Create a new waitable-set, store it for the other task to 138 // find, then return `CALLBACK_CODE_WAIT` to wait on it. 139 // This would lead to an infinite wait, except that the 140 // other task will add to the waitable-set after this one 141 // has started waiting and then trigger an event to wake it 142 // up. 143 let set = waitable_set_new(); 144 145 let old = SET.swap(set, Relaxed); 146 assert_eq!(old, 0); 147 148 *state = State::S1 { set }; 149 150 CALLBACK_CODE_WAIT | (set << 4) 151 } 152 1 => { 153 // Retrieve the waitable-set our peer task is waiting on, 154 // create a future, write to write end, add the write end to 155 // the waitable-set, then read from the read end. The read 156 // should trigger an event on the write end, waking up the 157 // peer task. 158 let set = SET.swap(0, Relaxed); 159 assert_ne!(set, 0); 160 161 let (tx, rx) = future_new(); 162 let status = future_write(tx); 163 assert_eq!(status, BLOCKED); 164 165 waitable_join(tx, set); 166 167 let status = future_read(rx); 168 assert_eq!(status, COMPLETED); // i.e. one element was read 169 170 future_drop_readable(rx); 171 172 task_return_run(); 173 CALLBACK_CODE_EXIT 174 } 175 _ => { 176 unreachable!() 177 } 178 } 179 } 180 181 State::S1 { set } => { 182 assert_eq!(event0, EVENT_FUTURE_WRITE); 183 assert_eq!(event2, COMPLETED); // i.e. one element was written 184 185 waitable_join(event1, 0); 186 waitable_set_drop(*set); 187 future_drop_writable(event1); 188 189 TASK_NUMBER.store(0, Relaxed); 190 191 task_return_run(); 192 CALLBACK_CODE_EXIT 193 } 194 } 195 } 196 } 197 198 // Unused function; required since this file is built as a `bin`: 199 fn main() {} 200