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"]
task_return_run()20 fn task_return_run();
21 }
22 #[cfg(not(target_arch = "wasm32"))]
task_return_run()23 unsafe extern "C" fn task_return_run() {
24 unreachable!()
25 }
26
future_new() -> (u32, u32)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
future_write(writer: u32) -> u3246 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
future_read(reader: u32) -> u3261 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
future_drop_readable(reader: u32)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
future_drop_writable(writer: u32)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")]
export_run() -> u32115 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")]
callback_run(event0: u32, event1: u32, event2: u32) -> u32128 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`:
main()199 fn main() {}
200