Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/test-programs/src/bin/async_round_trip_many_stackful.rs
1693 views
1
#![expect(unsafe_op_in_unsafe_fn, reason = "old code, not worth updating yet")]
2
3
// This tests callback-less (AKA stackful) async exports.
4
//
5
// Testing this case using Rust's LLVM-based toolchain is tricky because, as of
6
// this writing, LLVM does not produce reentrance-safe code. Specifically, it
7
// allocates a single shadow stack for use whenever a program needs to take the
8
// address of a stack variable, which makes concurrent execution of multiple
9
// Wasm stacks in the same instance hazardous.
10
//
11
// Given the above, we write code directly against the component model ABI
12
// rather than use `wit-bindgen`, and we carefully avoid use of the shadow stack
13
// across yield points such as calls to `waitable-set.wait` in order to keep the
14
// code reentrant.
15
16
mod bindings {
17
wit_bindgen::generate!({
18
path: "../misc/component-async-tests/wit",
19
world: "round-trip-many",
20
});
21
}
22
23
use {
24
std::alloc::{self, Layout},
25
test_programs::async_::{
26
EVENT_SUBTASK, STATUS_RETURNED, subtask_drop, waitable_join, waitable_set_drop,
27
waitable_set_new, waitable_set_wait,
28
},
29
};
30
31
#[cfg(target_arch = "wasm32")]
32
#[link(wasm_import_module = "[export]local:local/many")]
33
unsafe extern "C" {
34
#[link_name = "[task-return][async]foo"]
35
fn task_return_foo(ptr: *mut u8);
36
}
37
#[cfg(not(target_arch = "wasm32"))]
38
unsafe extern "C" fn task_return_foo(_ptr: *mut u8) {
39
unreachable!()
40
}
41
42
#[cfg(target_arch = "wasm32")]
43
#[link(wasm_import_module = "local:local/many")]
44
unsafe extern "C" {
45
#[link_name = "[async-lower][async]foo"]
46
fn import_foo(params: *mut u8, results: *mut u8) -> u32;
47
}
48
#[cfg(not(target_arch = "wasm32"))]
49
unsafe extern "C" fn import_foo(_params: *mut u8, _results: *mut u8) -> u32 {
50
unreachable!()
51
}
52
53
#[unsafe(export_name = "[async-lift-stackful]local:local/many#[async]foo")]
54
unsafe extern "C" fn export_foo(args: *mut u8) {
55
// Note that we're careful not to take the address of any stack-allocated
56
// value here. We need to avoid relying on the LLVM-generated shadow stack
57
// in order to correctly support reentrancy. It's okay to call functions
58
// which use the shadow stack, as long as they pop everything off before we
59
// reach a yield point such as a call to `waitable-set.wait`.
60
61
// type | size | align | offset
62
// ----------------------------------------------------------
63
// string | 8 | 4 | 0
64
// u32 | 4 | 4 | 8
65
// list<u8> | 8 | 4 | 12
66
// tuple<u64, u64> | 16 | 8 | 24
67
// tuple<list<u8>, bool, u64> | 24 | 8 | 40
68
// option<tuple<list<u8>, bool, u64>> | 32 | 8 | 64
69
// result<tuple<list<u8>, bool, u64>> | 32 | 8 | 96
70
// ----------------------------------------------------------
71
// total | 128 | 8 |
72
73
let len = *args.add(4).cast::<usize>();
74
let s = format!(
75
"{} - entered guest",
76
String::from_utf8(Vec::from_raw_parts(*args.cast::<*mut u8>(), len, len)).unwrap()
77
);
78
79
let layout = Layout::from_size_align(128, 8).unwrap();
80
81
let params = alloc::alloc(layout);
82
*params.cast::<*mut u8>() = s.as_ptr().cast_mut();
83
*params.add(4).cast::<usize>() = s.len();
84
params.add(8).copy_from(args.add(8), 120);
85
86
let results = alloc::alloc(layout);
87
88
let result = import_foo(params, results);
89
let mut status = result & 0xf;
90
let call = result >> 4;
91
let set = waitable_set_new();
92
if call != 0 {
93
waitable_join(call, set);
94
}
95
while status != STATUS_RETURNED {
96
// Note the use of `Box` here to avoid taking the address of a stack
97
// allocation.
98
let payload = Box::into_raw(Box::new([0i32; 2]));
99
let event = waitable_set_wait(set, payload.cast());
100
let payload = Box::from_raw(payload);
101
if event == EVENT_SUBTASK {
102
assert_eq!(call, payload[0] as u32);
103
status = payload[1] as u32;
104
if status == STATUS_RETURNED {
105
subtask_drop(call);
106
waitable_set_drop(set);
107
}
108
}
109
}
110
alloc::dealloc(params, layout);
111
112
let len = *results.add(4).cast::<usize>();
113
let s = format!(
114
"{} - exited guest",
115
String::from_utf8(Vec::from_raw_parts(*results.cast::<*mut u8>(), len, len)).unwrap()
116
);
117
*results.cast::<*mut u8>() = s.as_ptr().cast_mut();
118
*results.add(4).cast::<usize>() = s.len();
119
120
task_return_foo(results);
121
}
122
123
// Unused function; required since this file is built as a `bin`:
124
fn main() {}
125
126