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_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",
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/baz")]
33
unsafe extern "C" {
34
#[link_name = "[task-return][async]foo"]
35
fn task_return_foo(ptr: *mut u8, len: usize);
36
}
37
#[cfg(not(target_arch = "wasm32"))]
38
unsafe extern "C" fn task_return_foo(_ptr: *mut u8, _len: usize) {
39
unreachable!()
40
}
41
42
#[cfg(target_arch = "wasm32")]
43
#[link(wasm_import_module = "local:local/baz")]
44
unsafe extern "C" {
45
#[link_name = "[async-lower][async]foo"]
46
fn import_foo(ptr: *mut u8, len: usize, results: *mut u8) -> u32;
47
}
48
#[cfg(not(target_arch = "wasm32"))]
49
unsafe extern "C" fn import_foo(_ptr: *mut u8, _len: usize, _results: *mut u8) -> u32 {
50
unreachable!()
51
}
52
53
#[unsafe(export_name = "[async-lift-stackful]local:local/baz#[async]foo")]
54
unsafe extern "C" fn export_foo(ptr: *mut u8, len: usize) {
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
let s = format!(
62
"{} - entered guest",
63
String::from_utf8(Vec::from_raw_parts(ptr, len, len)).unwrap()
64
);
65
66
let layout = Layout::from_size_align(8, 4).unwrap();
67
68
let results = alloc::alloc(layout);
69
70
let result = import_foo(s.as_ptr().cast_mut(), s.len(), results);
71
let mut status = result & 0xf;
72
let call = result >> 4;
73
let set = waitable_set_new();
74
if call != 0 {
75
waitable_join(call, set);
76
}
77
while status != STATUS_RETURNED {
78
// Note the use of `Box` here to avoid taking the address of a stack
79
// allocation.
80
let payload = Box::into_raw(Box::new([0i32; 2]));
81
let event = waitable_set_wait(set, payload.cast());
82
let payload = Box::from_raw(payload);
83
if event == EVENT_SUBTASK {
84
assert_eq!(call, payload[0] as u32);
85
status = payload[1] as u32;
86
if status == STATUS_RETURNED {
87
subtask_drop(call);
88
waitable_set_drop(set);
89
}
90
}
91
}
92
93
let len = *results.add(4).cast::<usize>();
94
let s = format!(
95
"{} - exited guest",
96
String::from_utf8(Vec::from_raw_parts(*results.cast::<*mut u8>(), len, len)).unwrap()
97
);
98
alloc::dealloc(results, layout);
99
100
task_return_foo(s.as_ptr().cast_mut(), s.len());
101
}
102
103
// Unused function; required since this file is built as a `bin`:
104
fn main() {}
105
106