1804060c8SJoel Dice #![expect(unsafe_op_in_unsafe_fn, reason = "old code, not worth updating yet")]
2804060c8SJoel Dice
3804060c8SJoel Dice // This tests callback-less (AKA stackful) async exports.
4804060c8SJoel Dice //
5804060c8SJoel Dice // Testing this case using Rust's LLVM-based toolchain is tricky because, as of
6804060c8SJoel Dice // this writing, LLVM does not produce reentrance-safe code. Specifically, it
7804060c8SJoel Dice // allocates a single shadow stack for use whenever a program needs to take the
8804060c8SJoel Dice // address of a stack variable, which makes concurrent execution of multiple
9804060c8SJoel Dice // Wasm stacks in the same instance hazardous.
10804060c8SJoel Dice //
11804060c8SJoel Dice // Given the above, we write code directly against the component model ABI
12804060c8SJoel Dice // rather than use `wit-bindgen`, and we carefully avoid use of the shadow stack
13804060c8SJoel Dice // across yield points such as calls to `waitable-set.wait` in order to keep the
14804060c8SJoel Dice // code reentrant.
15804060c8SJoel Dice
16804060c8SJoel Dice mod bindings {
17804060c8SJoel Dice wit_bindgen::generate!({
18804060c8SJoel Dice path: "../misc/component-async-tests/wit",
19804060c8SJoel Dice world: "round-trip-many",
20804060c8SJoel Dice });
21804060c8SJoel Dice }
22804060c8SJoel Dice
23804060c8SJoel Dice use {
24804060c8SJoel Dice std::alloc::{self, Layout},
25804060c8SJoel Dice test_programs::async_::{
26804060c8SJoel Dice EVENT_SUBTASK, STATUS_RETURNED, subtask_drop, waitable_join, waitable_set_drop,
27804060c8SJoel Dice waitable_set_new, waitable_set_wait,
28804060c8SJoel Dice },
29804060c8SJoel Dice };
30804060c8SJoel Dice
31804060c8SJoel Dice #[cfg(target_arch = "wasm32")]
32804060c8SJoel Dice #[link(wasm_import_module = "[export]local:local/many")]
33804060c8SJoel Dice unsafe extern "C" {
34*020727d0SAlex Crichton #[link_name = "[task-return]foo"]
task_return_foo(ptr: *mut u8)35804060c8SJoel Dice fn task_return_foo(ptr: *mut u8);
36804060c8SJoel Dice }
37804060c8SJoel Dice #[cfg(not(target_arch = "wasm32"))]
task_return_foo(_ptr: *mut u8)38804060c8SJoel Dice unsafe extern "C" fn task_return_foo(_ptr: *mut u8) {
39804060c8SJoel Dice unreachable!()
40804060c8SJoel Dice }
41804060c8SJoel Dice
42804060c8SJoel Dice #[cfg(target_arch = "wasm32")]
43804060c8SJoel Dice #[link(wasm_import_module = "local:local/many")]
44804060c8SJoel Dice unsafe extern "C" {
45*020727d0SAlex Crichton #[link_name = "[async-lower]foo"]
import_foo(params: *mut u8, results: *mut u8) -> u3246804060c8SJoel Dice fn import_foo(params: *mut u8, results: *mut u8) -> u32;
47804060c8SJoel Dice }
48804060c8SJoel Dice #[cfg(not(target_arch = "wasm32"))]
import_foo(_params: *mut u8, _results: *mut u8) -> u3249804060c8SJoel Dice unsafe extern "C" fn import_foo(_params: *mut u8, _results: *mut u8) -> u32 {
50804060c8SJoel Dice unreachable!()
51804060c8SJoel Dice }
52804060c8SJoel Dice
53*020727d0SAlex Crichton #[unsafe(export_name = "[async-lift-stackful]local:local/many#foo")]
export_foo(args: *mut u8)54804060c8SJoel Dice unsafe extern "C" fn export_foo(args: *mut u8) {
55804060c8SJoel Dice // Note that we're careful not to take the address of any stack-allocated
56804060c8SJoel Dice // value here. We need to avoid relying on the LLVM-generated shadow stack
57804060c8SJoel Dice // in order to correctly support reentrancy. It's okay to call functions
58804060c8SJoel Dice // which use the shadow stack, as long as they pop everything off before we
59804060c8SJoel Dice // reach a yield point such as a call to `waitable-set.wait`.
60804060c8SJoel Dice
61804060c8SJoel Dice // type | size | align | offset
62804060c8SJoel Dice // ----------------------------------------------------------
63804060c8SJoel Dice // string | 8 | 4 | 0
64804060c8SJoel Dice // u32 | 4 | 4 | 8
65804060c8SJoel Dice // list<u8> | 8 | 4 | 12
66804060c8SJoel Dice // tuple<u64, u64> | 16 | 8 | 24
67804060c8SJoel Dice // tuple<list<u8>, bool, u64> | 24 | 8 | 40
68804060c8SJoel Dice // option<tuple<list<u8>, bool, u64>> | 32 | 8 | 64
69804060c8SJoel Dice // result<tuple<list<u8>, bool, u64>> | 32 | 8 | 96
70804060c8SJoel Dice // ----------------------------------------------------------
71804060c8SJoel Dice // total | 128 | 8 |
72804060c8SJoel Dice
73804060c8SJoel Dice let len = *args.add(4).cast::<usize>();
74804060c8SJoel Dice let s = format!(
75804060c8SJoel Dice "{} - entered guest",
76804060c8SJoel Dice String::from_utf8(Vec::from_raw_parts(*args.cast::<*mut u8>(), len, len)).unwrap()
77804060c8SJoel Dice );
78804060c8SJoel Dice
79804060c8SJoel Dice let layout = Layout::from_size_align(128, 8).unwrap();
80804060c8SJoel Dice
81804060c8SJoel Dice let params = alloc::alloc(layout);
82804060c8SJoel Dice *params.cast::<*mut u8>() = s.as_ptr().cast_mut();
83804060c8SJoel Dice *params.add(4).cast::<usize>() = s.len();
84804060c8SJoel Dice params.add(8).copy_from(args.add(8), 120);
85804060c8SJoel Dice
86804060c8SJoel Dice let results = alloc::alloc(layout);
87804060c8SJoel Dice
88804060c8SJoel Dice let result = import_foo(params, results);
89804060c8SJoel Dice let mut status = result & 0xf;
90804060c8SJoel Dice let call = result >> 4;
91804060c8SJoel Dice let set = waitable_set_new();
92804060c8SJoel Dice if call != 0 {
93804060c8SJoel Dice waitable_join(call, set);
94804060c8SJoel Dice }
95804060c8SJoel Dice while status != STATUS_RETURNED {
96804060c8SJoel Dice // Note the use of `Box` here to avoid taking the address of a stack
97804060c8SJoel Dice // allocation.
98804060c8SJoel Dice let payload = Box::into_raw(Box::new([0i32; 2]));
99804060c8SJoel Dice let event = waitable_set_wait(set, payload.cast());
100804060c8SJoel Dice let payload = Box::from_raw(payload);
101804060c8SJoel Dice if event == EVENT_SUBTASK {
102804060c8SJoel Dice assert_eq!(call, payload[0] as u32);
103804060c8SJoel Dice status = payload[1] as u32;
104804060c8SJoel Dice if status == STATUS_RETURNED {
105804060c8SJoel Dice subtask_drop(call);
106804060c8SJoel Dice waitable_set_drop(set);
107804060c8SJoel Dice }
108804060c8SJoel Dice }
109804060c8SJoel Dice }
110804060c8SJoel Dice alloc::dealloc(params, layout);
111804060c8SJoel Dice
112804060c8SJoel Dice let len = *results.add(4).cast::<usize>();
113804060c8SJoel Dice let s = format!(
114804060c8SJoel Dice "{} - exited guest",
115804060c8SJoel Dice String::from_utf8(Vec::from_raw_parts(*results.cast::<*mut u8>(), len, len)).unwrap()
116804060c8SJoel Dice );
117804060c8SJoel Dice *results.cast::<*mut u8>() = s.as_ptr().cast_mut();
118804060c8SJoel Dice *results.add(4).cast::<usize>() = s.len();
119804060c8SJoel Dice
120804060c8SJoel Dice task_return_foo(results);
121804060c8SJoel Dice }
122804060c8SJoel Dice
123804060c8SJoel Dice // Unused function; required since this file is built as a `bin`:
main()124804060c8SJoel Dice fn main() {}
125