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