Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/test-programs/src/bin/async_intertask_communication.rs
1693 views
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][async]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#[async]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#[async]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
201