1 use crate::async_functions::{PollOnce, execute_across_threads};
2 use anyhow::Result;
3 use wasmtime::{AsContextMut, Config, component::*};
4 use wasmtime::{Engine, Store, StoreContextMut, Trap};
5 use wasmtime_component_util::REALLOC_AND_FREE;
6 
7 /// This is super::func::thunks, except with an async store.
8 #[tokio::test]
9 #[cfg_attr(miri, ignore)]
10 async fn smoke() -> Result<()> {
11     let component = r#"
12         (component
13             (core module $m
14                 (func (export "thunk"))
15                 (func (export "thunk-trap") unreachable)
16             )
17             (core instance $i (instantiate $m))
18             (func (export "thunk")
19                 (canon lift (core func $i "thunk"))
20             )
21             (func (export "thunk-trap")
22                 (canon lift (core func $i "thunk-trap"))
23             )
24         )
25     "#;
26 
27     let engine = super::async_engine();
28     let component = Component::new(&engine, component)?;
29     let mut store = Store::new(&engine, ());
30     let instance = Linker::new(&engine)
31         .instantiate_async(&mut store, &component)
32         .await?;
33 
34     let thunk = instance.get_typed_func::<(), ()>(&mut store, "thunk")?;
35 
36     thunk.call_async(&mut store, ()).await?;
37     thunk.post_return_async(&mut store).await?;
38 
39     let err = instance
40         .get_typed_func::<(), ()>(&mut store, "thunk-trap")?
41         .call_async(&mut store, ())
42         .await
43         .unwrap_err();
44     assert_eq!(err.downcast::<Trap>()?, Trap::UnreachableCodeReached);
45 
46     Ok(())
47 }
48 
49 /// Handle an import function, created using component::Linker::func_wrap_async.
50 #[tokio::test]
51 #[cfg_attr(miri, ignore)]
52 async fn smoke_func_wrap() -> Result<()> {
53     let component = r#"
54         (component
55             (type $f (func))
56             (import "i" (func $f))
57 
58             (core module $m
59                 (import "imports" "i" (func $i))
60                 (func (export "thunk") call $i)
61             )
62 
63             (core func $f (canon lower (func $f)))
64             (core instance $i (instantiate $m
65                 (with "imports" (instance
66                     (export "i" (func $f))
67                 ))
68              ))
69             (func (export "thunk")
70                 (canon lift (core func $i "thunk"))
71             )
72         )
73     "#;
74 
75     let engine = super::async_engine();
76     let component = Component::new(&engine, component)?;
77     let mut store = Store::new(&engine, ());
78     let mut linker = Linker::new(&engine);
79     let mut root = linker.root();
80     root.func_wrap_async("i", |_: StoreContextMut<()>, _: ()| {
81         Box::new(async { Ok(()) })
82     })?;
83 
84     let instance = linker.instantiate_async(&mut store, &component).await?;
85 
86     let thunk = instance.get_typed_func::<(), ()>(&mut store, "thunk")?;
87 
88     thunk.call_async(&mut store, ()).await?;
89     thunk.post_return_async(&mut store).await?;
90 
91     Ok(())
92 }
93 
94 // This test stresses TLS management in combination with the `realloc` option
95 // for imported functions. This will create an async computation which invokes a
96 // component that invokes an imported function. The imported function returns a
97 // list which will require invoking malloc.
98 //
99 // As an added stressor all polls are sprinkled across threads through
100 // `execute_across_threads`. Yields are injected liberally by configuring 1
101 // fuel consumption to trigger a yield.
102 //
103 // Overall a yield should happen during malloc which should be an "interesting
104 // situation" with respect to the runtime.
105 #[tokio::test]
106 #[cfg_attr(miri, ignore)]
107 async fn resume_separate_thread() -> Result<()> {
108     let mut config = wasmtime_test_util::component::config();
109     config.async_support(true);
110     config.consume_fuel(true);
111     let engine = Engine::new(&config)?;
112     let component = format!(
113         r#"
114             (component
115                 (import "yield" (func $yield (result (list u8))))
116                 (core module $libc
117                     (memory (export "memory") 1)
118                     {REALLOC_AND_FREE}
119                 )
120                 (core instance $libc (instantiate $libc))
121 
122                 (core func $yield
123                     (canon lower
124                         (func $yield)
125                         (memory $libc "memory")
126                         (realloc (func $libc "realloc"))
127                     )
128                 )
129 
130                 (core module $m
131                     (import "" "yield" (func $yield (param i32)))
132                     (import "libc" "memory" (memory 0))
133                     (func $start
134                         i32.const 8
135                         call $yield
136                     )
137                     (start $start)
138                 )
139                 (core instance (instantiate $m
140                     (with "" (instance (export "yield" (func $yield))))
141                     (with "libc" (instance $libc))
142                 ))
143             )
144         "#
145     );
146     let component = Component::new(&engine, component)?;
147     let mut linker = Linker::new(&engine);
148     linker
149         .root()
150         .func_wrap_async("yield", |_: StoreContextMut<()>, _: ()| {
151             Box::new(async {
152                 tokio::task::yield_now().await;
153                 Ok((vec![1u8, 2u8],))
154             })
155         })?;
156 
157     execute_across_threads(async move {
158         let mut store = Store::new(&engine, ());
159         store.set_fuel(u64::MAX).unwrap();
160         store.fuel_async_yield_interval(Some(1)).unwrap();
161         linker.instantiate_async(&mut store, &component).await?;
162         Ok::<_, anyhow::Error>(())
163     })
164     .await?;
165     Ok(())
166 }
167 
168 // This test is intended to stress TLS management in the component model around
169 // the management of the `realloc` function. This creates an async computation
170 // representing the execution of a component model function where entry into the
171 // component uses `realloc` and then the component runs. This async computation
172 // is then polled iteratively with another "wasm activation" (in this case a
173 // core wasm function) on the stack. The poll-per-call should work and nothing
174 // should in theory have problems here.
175 //
176 // As an added stressor all polls are sprinkled across threads through
177 // `execute_across_threads`. Yields are injected liberally by configuring 1
178 // fuel consumption to trigger a yield.
179 //
180 // Overall a yield should happen during malloc which should be an "interesting
181 // situation" with respect to the runtime.
182 #[tokio::test]
183 #[cfg_attr(miri, ignore)]
184 async fn poll_through_wasm_activation() -> Result<()> {
185     let mut config = wasmtime_test_util::component::config();
186     config.async_support(true);
187     config.consume_fuel(true);
188     let engine = Engine::new(&config)?;
189     let component = format!(
190         r#"
191             (component
192                 (core module $m
193                     {REALLOC_AND_FREE}
194                     (memory (export "memory") 1)
195                     (func (export "run") (param i32 i32)
196                     )
197                 )
198                 (core instance $i (instantiate $m))
199                 (func (export "run") (param "x" (list u8))
200                     (canon lift (core func $i "run")
201                                 (memory $i "memory")
202                                 (realloc (func $i "realloc"))))
203             )
204         "#
205     );
206     let component = Component::new(&engine, component)?;
207     let linker = Linker::new(&engine);
208 
209     let invoke_component = {
210         let engine = engine.clone();
211         async move {
212             let mut store = Store::new(&engine, ());
213             store.set_fuel(u64::MAX).unwrap();
214             store.fuel_async_yield_interval(Some(1)).unwrap();
215             let instance = linker.instantiate_async(&mut store, &component).await?;
216             let func = instance.get_typed_func::<(Vec<u8>,), ()>(&mut store, "run")?;
217             func.call_async(&mut store, (vec![1, 2, 3],)).await?;
218             Ok::<_, anyhow::Error>(())
219         }
220     };
221 
222     execute_across_threads(async move {
223         let mut store = Store::new(&engine, Some(Box::pin(invoke_component)));
224         let poll_once = wasmtime::Func::wrap_async(&mut store, |mut cx, _: ()| {
225             let invoke_component = cx.data_mut().take().unwrap();
226             Box::new(async move {
227                 match PollOnce::new(invoke_component).await {
228                     Ok(result) => {
229                         result?;
230                         Ok(1)
231                     }
232                     Err(future) => {
233                         *cx.data_mut() = Some(future);
234                         Ok(0)
235                     }
236                 }
237             })
238         });
239         let poll_once = poll_once.typed::<(), i32>(&mut store)?;
240         while poll_once.call_async(&mut store, ()).await? != 1 {
241             // loop around to call again
242         }
243         Ok::<_, anyhow::Error>(())
244     })
245     .await?;
246     Ok(())
247 }
248 
249 /// Test async drop method for host resources.
250 #[tokio::test]
251 #[cfg_attr(miri, ignore)]
252 async fn drop_resource_async() -> Result<()> {
253     use std::sync::Arc;
254     use std::sync::Mutex;
255 
256     let engine = super::async_engine();
257     let c = Component::new(
258         &engine,
259         r#"
260             (component
261                 (import "t" (type $t (sub resource)))
262 
263                 (core func $drop (canon resource.drop $t))
264 
265                 (core module $m
266                     (import "" "drop" (func $drop (param i32)))
267                     (func (export "f") (param i32)
268                         (call $drop (local.get 0))
269                     )
270                 )
271                 (core instance $i (instantiate $m
272                     (with "" (instance
273                         (export "drop" (func $drop))
274                     ))
275                 ))
276 
277                 (func (export "f") (param "x" (own $t))
278                     (canon lift (core func $i "f")))
279             )
280         "#,
281     )?;
282 
283     struct MyType;
284 
285     let mut store = Store::new(&engine, ());
286     let mut linker = Linker::new(&engine);
287 
288     let drop_status = Arc::new(Mutex::new("not dropped"));
289     let ds = drop_status.clone();
290 
291     linker
292         .root()
293         .resource_async("t", ResourceType::host::<MyType>(), move |_, _| {
294             let ds = ds.clone();
295             Box::new(async move {
296                 *ds.lock().unwrap() = "before yield";
297                 tokio::task::yield_now().await;
298                 *ds.lock().unwrap() = "after yield";
299                 Ok(())
300             })
301         })?;
302     let i = linker.instantiate_async(&mut store, &c).await?;
303     let f = i.get_typed_func::<(Resource<MyType>,), ()>(&mut store, "f")?;
304 
305     execute_across_threads(async move {
306         let resource = Resource::new_own(100);
307         f.call_async(&mut store, (resource,)).await?;
308         f.post_return_async(&mut store).await?;
309         Ok::<_, anyhow::Error>(())
310     })
311     .await?;
312 
313     assert_eq!("after yield", *drop_status.lock().unwrap());
314 
315     Ok(())
316 }
317 
318 /// Test task deletion in three situations, for every combination of lift/lower/(guest/host):
319 /// 1. An explicit thread calls task.return
320 /// 2. An explicit thread suspends indefinitely
321 /// 3. An explicit thread yield loops indefinitely
322 #[tokio::test]
323 #[cfg_attr(miri, ignore)]
324 async fn task_deletion() -> Result<()> {
325     let mut config = Config::new();
326     config.async_support(true);
327     config.wasm_component_model_async(true);
328     config.wasm_component_model_threading(true);
329     config.wasm_component_model_async_stackful(true);
330     config.wasm_component_model_async_builtins(true);
331     let engine = Engine::new(&config)?;
332     let component = Component::new(
333         &engine,
334         r#"(component
335     (component $C
336         (core module $Memory (memory (export "mem") 1))
337         (core instance $memory (instantiate $Memory))
338         ;; Defines the table for the thread start functions
339         (core module $libc
340             (table (export "__indirect_function_table") 3 funcref))
341         (core module $CM
342             (import "" "mem" (memory 1))
343             (import "" "task.return" (func $task-return (param i32)))
344             (import "" "task.cancel" (func $task-cancel))
345             (import "" "thread.new_indirect" (func $thread-new-indirect (param i32 i32) (result i32)))
346             (import "" "thread.suspend" (func $thread-suspend (result i32)))
347             (import "" "thread.suspend-cancellable" (func $thread-suspend-cancellable (result i32)))
348             (import "" "thread.yield-to" (func $thread-yield-to (param i32) (result i32)))
349             (import "" "thread.yield-to-cancellable" (func $thread-yield-to-cancellable (param i32) (result i32)))
350             (import "" "thread.switch-to" (func $thread-switch-to (param i32) (result i32)))
351             (import "" "thread.switch-to-cancellable" (func $thread-switch-to-cancellable (param i32) (result i32)))
352             (import "" "thread.yield" (func $thread-yield (result i32)))
353             (import "" "thread.yield-cancellable" (func $thread-yield-cancellable (result i32)))
354             (import "" "thread.index" (func $thread-index (result i32)))
355             (import "" "thread.resume-later" (func $thread-resume-later (param i32)))
356             (import "" "waitable.join" (func $waitable.join (param i32 i32)))
357             (import "" "waitable-set.new" (func $waitable-set.new (result i32)))
358             (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32)))
359             (import "libc" "__indirect_function_table" (table $indirect-function-table 3 funcref))
360 
361             ;; Indices into the function table for the thread start functions
362             (global $call-return-ftbl-idx i32 (i32.const 0))
363             (global $suspend-ftbl-idx i32 (i32.const 1))
364             (global $yield-loop-ftbl-idx i32 (i32.const 2))
365 
366             (func $call-return (param i32)
367                 (call $task-return (local.get 0)))
368 
369             (func $suspend (param i32)
370                 (drop (call $thread-suspend)))
371 
372             (func $yield-loop (param i32)
373                 (loop $top
374                     (drop (call $thread-yield))
375                     (br $top)))
376 
377             (func (export "explicit-thread-calls-return-stackful")
378                 (call $thread-resume-later
379                     (call $thread-new-indirect (global.get $call-return-ftbl-idx) (i32.const 42))))
380 
381             (func (export "explicit-thread-calls-return-stackless") (result i32)
382                 (call $thread-resume-later
383                     (call $thread-new-indirect (global.get $call-return-ftbl-idx) (i32.const 42)))
384                 (i32.const 0 (; EXIT ;)))
385 
386             (func (export "cb") (param i32 i32 i32) (result i32)
387                 (unreachable))
388 
389             (func (export "explicit-thread-suspends-sync") (result i32)
390                 (call $thread-resume-later
391                     (call $thread-new-indirect (global.get $suspend-ftbl-idx) (i32.const 42)))
392                 (i32.const 42))
393 
394             (func (export "explicit-thread-suspends-stackful")
395                 (call $thread-resume-later
396                     (call $thread-new-indirect (global.get $suspend-ftbl-idx) (i32.const 42)))
397                 (call $task-return (i32.const 42)))
398 
399             (func (export "explicit-thread-suspends-stackless") (result i32)
400                 (call $thread-resume-later
401                     (call $thread-new-indirect (global.get $suspend-ftbl-idx) (i32.const 42)))
402                 (call $task-return (i32.const 42))
403                 (i32.const 0))
404 
405             (func (export "explicit-thread-yield-loops-sync") (result i32)
406                 (call $thread-resume-later
407                     (call $thread-new-indirect (global.get $yield-loop-ftbl-idx) (i32.const 42)))
408                 (i32.const 42))
409 
410             (func (export "explicit-thread-yield-loops-stackful")
411                 (call $thread-resume-later
412                     (call $thread-new-indirect (global.get $yield-loop-ftbl-idx) (i32.const 42)))
413                 (call $task-return (i32.const 42)))
414 
415             (func (export "explicit-thread-yield-loops-stackless") (result i32)
416                 (call $thread-resume-later
417                     (call $thread-new-indirect (global.get $suspend-ftbl-idx) (i32.const 42)))
418                 (call $task-return (i32.const 42))
419                 (i32.const 0 (; EXIT ;)))
420 
421 
422             ;; Initialize the function table that will be used by thread.new_indirect
423             (elem (table $indirect-function-table) (i32.const 0 (; call-return-ftbl-idx ;)) func $call-return)
424             (elem (table $indirect-function-table) (i32.const 1 (; suspend-ftbl-idx ;)) func $suspend)
425             (elem (table $indirect-function-table) (i32.const 2 (; yield-loop-ftbl-idx ;)) func $yield-loop)
426         )
427 
428         ;; Instantiate the libc module to get the table
429         (core instance $libc (instantiate $libc))
430         ;; Get access to `thread.new_indirect` that uses the table from libc
431         (core type $start-func-ty (func (param i32)))
432         (alias core export $libc "__indirect_function_table" (core table $indirect-function-table))
433 
434         (core func $task-return (canon task.return (result u32)))
435         (core func $task-cancel (canon task.cancel))
436         (core func $thread-new-indirect
437             (canon thread.new_indirect $start-func-ty (table $indirect-function-table)))
438         (core func $thread-yield (canon thread.yield))
439         (core func $thread-yield-cancellable (canon thread.yield cancellable))
440         (core func $thread-index (canon thread.index))
441         (core func $thread-yield-to (canon thread.yield-to))
442         (core func $thread-yield-to-cancellable (canon thread.yield-to cancellable))
443         (core func $thread-resume-later (canon thread.resume-later))
444         (core func $thread-switch-to (canon thread.switch-to))
445         (core func $thread-switch-to-cancellable (canon thread.switch-to cancellable))
446         (core func $thread-suspend (canon thread.suspend))
447         (core func $thread-suspend-cancellable (canon thread.suspend cancellable))
448         (core func $waitable-set.new (canon waitable-set.new))
449         (core func $waitable.join (canon waitable.join))
450         (core func $waitable-set.wait (canon waitable-set.wait (memory $memory "mem")))
451 
452         ;; Instantiate the main module
453         (core instance $cm (
454             instantiate $CM
455                 (with "" (instance
456                     (export "mem" (memory $memory "mem"))
457                     (export "task.return" (func $task-return))
458                     (export "task.cancel" (func $task-cancel))
459                     (export "thread.new_indirect" (func $thread-new-indirect))
460                     (export "thread.index" (func $thread-index))
461                     (export "thread.yield-to" (func $thread-yield-to))
462                     (export "thread.yield-to-cancellable" (func $thread-yield-to-cancellable))
463                     (export "thread.yield" (func $thread-yield))
464                     (export "thread.yield-cancellable" (func $thread-yield-cancellable))
465                     (export "thread.switch-to" (func $thread-switch-to))
466                     (export "thread.switch-to-cancellable" (func $thread-switch-to-cancellable))
467                     (export "thread.suspend" (func $thread-suspend))
468                     (export "thread.suspend-cancellable" (func $thread-suspend-cancellable))
469                     (export "thread.resume-later" (func $thread-resume-later))
470                     (export "waitable.join" (func $waitable.join))
471                     (export "waitable-set.wait" (func $waitable-set.wait))
472                     (export "waitable-set.new" (func $waitable-set.new))))
473                 (with "libc" (instance $libc))))
474 
475         (func (export "explicit-thread-calls-return-stackful") (result u32)
476             (canon lift (core func $cm "explicit-thread-calls-return-stackful") async))
477         (func (export "explicit-thread-calls-return-stackless") (result u32)
478             (canon lift (core func $cm "explicit-thread-calls-return-stackless") async (callback (func $cm "cb"))))
479         (func (export "explicit-thread-suspends-sync") (result u32)
480             (canon lift (core func $cm "explicit-thread-suspends-sync")))
481         (func (export "explicit-thread-suspends-stackful") (result u32)
482             (canon lift (core func $cm "explicit-thread-suspends-stackful") async))
483         (func (export "explicit-thread-suspends-stackless") (result u32)
484             (canon lift (core func $cm "explicit-thread-suspends-stackless") async (callback (func $cm "cb"))))
485         (func (export "explicit-thread-yield-loops-sync") (result u32)
486             (canon lift (core func $cm "explicit-thread-yield-loops-sync")))
487         (func (export "explicit-thread-yield-loops-stackful") (result u32)
488             (canon lift (core func $cm "explicit-thread-yield-loops-stackful") async))
489         (func (export "explicit-thread-yield-loops-stackless") (result u32)
490             (canon lift (core func $cm "explicit-thread-yield-loops-stackless") async (callback (func $cm "cb"))))
491     )
492 
493     (component $D
494         (import "explicit-thread-calls-return-stackful" (func $explicit-thread-calls-return-stackful (result u32)))
495         (import "explicit-thread-calls-return-stackless" (func $explicit-thread-calls-return-stackless (result u32)))
496         (import "explicit-thread-suspends-sync" (func $explicit-thread-suspends-sync (result u32)))
497         (import "explicit-thread-suspends-stackful" (func $explicit-thread-suspends-stackful (result u32)))
498         (import "explicit-thread-suspends-stackless" (func $explicit-thread-suspends-stackless (result u32)))
499         (import "explicit-thread-yield-loops-sync" (func $explicit-thread-yield-loops-sync (result u32)))
500         (import "explicit-thread-yield-loops-stackful" (func $explicit-thread-yield-loops-stackful (result u32)))
501         (import "explicit-thread-yield-loops-stackless" (func $explicit-thread-yield-loops-stackless (result u32)))
502 
503         (core module $Memory (memory (export "mem") 1))
504         (core instance $memory (instantiate $Memory))
505         (core module $DM
506             (import "" "mem" (memory 1))
507             (import "" "subtask.cancel" (func $subtask.cancel (param i32) (result i32)))
508             ;; sync lowered
509             (import "" "explicit-thread-calls-return-stackful" (func $explicit-thread-calls-return-stackful (result i32)))
510             (import "" "explicit-thread-calls-return-stackless" (func $explicit-thread-calls-return-stackless (result i32)))
511             (import "" "explicit-thread-suspends-sync" (func $explicit-thread-suspends-sync (result i32)))
512             (import "" "explicit-thread-suspends-stackful" (func $explicit-thread-suspends-stackful (result i32)))
513             (import "" "explicit-thread-suspends-stackless" (func $explicit-thread-suspends-stackless (result i32)))
514             (import "" "explicit-thread-yield-loops-sync" (func $explicit-thread-yield-loops-sync (result i32)))
515             (import "" "explicit-thread-yield-loops-stackful" (func $explicit-thread-yield-loops-stackful (result i32)))
516             (import "" "explicit-thread-yield-loops-stackless" (func $explicit-thread-yield-loops-stackless (result i32)))
517             ;; async lowered
518             (import "" "explicit-thread-calls-return-stackful-async" (func $explicit-thread-calls-return-stackful-async (param i32) (result i32)))
519             (import "" "explicit-thread-calls-return-stackless-async" (func $explicit-thread-calls-return-stackless-async (param i32) (result i32)))
520             (import "" "explicit-thread-suspends-sync-async" (func $explicit-thread-suspends-sync-async (param i32) (result i32)))
521             (import "" "explicit-thread-suspends-stackful-async" (func $explicit-thread-suspends-stackful-async (param i32) (result i32)))
522             (import "" "explicit-thread-suspends-stackless-async" (func $explicit-thread-suspends-stackless-async (param i32) (result i32)))
523             (import "" "explicit-thread-yield-loops-sync-async" (func $explicit-thread-yield-loops-sync-async (param i32) (result i32)))
524             (import "" "explicit-thread-yield-loops-stackful-async" (func $explicit-thread-yield-loops-stackful-async (param i32) (result i32)))
525             (import "" "explicit-thread-yield-loops-stackless-async" (func $explicit-thread-yield-loops-stackless-async (param i32) (result i32)))
526             (import "" "waitable.join" (func $waitable.join (param i32 i32)))
527             (import "" "waitable-set.new" (func $waitable-set.new (result i32)))
528             (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32)))
529             (import "" "thread.yield" (func $thread-yield (result i32)))
530 
531             (func $check (param i32)
532                 (if (i32.ne (local.get 0) (i32.const 42))
533                     (then unreachable))
534             )
535 
536             (func $check-async (param i32)
537                 (local $retp i32) (local $ws i32) (local $ws-retp i32)
538                 (local.set $retp (i32.const 8))
539                 (local.set $ws-retp (i32.const 16))
540                 (local.set $ws (call $waitable-set.new))
541 
542                 (if (i32.eq (i32.and (local.get 0) (i32.const 0xF)) (i32.const 2 (; RETURNED ;)))
543                     (then (call $check (i32.load (local.get $retp))))
544                     (else
545                         (call $waitable.join (i32.shr_u (local.get 0) (i32.const 4)) (local.get $ws))
546                         (drop (call $waitable-set.wait (local.get $ws) (local.get $ws-retp)))
547                         (call $check (i32.load (local.get $retp)))))
548             )
549 
550             (func $run (export "run") (result i32)
551                 (local $retp i32)
552                 (local.set $retp (i32.const 8))
553                 (call $check (call $explicit-thread-calls-return-stackless))
554                 (call $check (call $explicit-thread-calls-return-stackful))
555                 (call $check (call $explicit-thread-suspends-sync))
556                 (call $check (call $explicit-thread-suspends-stackful))
557                 (call $check (call $explicit-thread-suspends-stackless))
558                 (call $check (call $explicit-thread-yield-loops-sync))
559                 (call $check (call $explicit-thread-yield-loops-stackful))
560                 (call $check (call $explicit-thread-yield-loops-stackless))
561 
562                 (call $check-async (call $explicit-thread-calls-return-stackless-async (local.get $retp)))
563                 (call $check-async (call $explicit-thread-calls-return-stackful-async (local.get $retp)))
564                 (call $check-async (call $explicit-thread-suspends-sync-async (local.get $retp)))
565                 (call $check-async (call $explicit-thread-suspends-stackful-async (local.get $retp)))
566                 (call $check-async (call $explicit-thread-suspends-stackless-async (local.get $retp)))
567                 (call $check-async (call $explicit-thread-yield-loops-sync-async (local.get $retp)))
568                 (call $check-async (call $explicit-thread-yield-loops-stackful-async (local.get $retp)))
569                 (call $check-async (call $explicit-thread-yield-loops-stackless-async (local.get $retp)))
570 
571                 (i32.const 42)
572             )
573         )
574 
575         (core func $waitable-set.new (canon waitable-set.new))
576         (core func $waitable-set.wait (canon waitable-set.wait (memory $memory "mem")))
577         (core func $waitable.join (canon waitable.join))
578         (core func $subtask.cancel (canon subtask.cancel async))
579         (core func $thread.yield (canon thread.yield))
580         ;; sync lowered
581         (canon lower (func $explicit-thread-calls-return-stackful) (memory $memory "mem") (core func $explicit-thread-calls-return-stackful'))
582         (canon lower (func $explicit-thread-calls-return-stackless) (memory $memory "mem") (core func $explicit-thread-calls-return-stackless'))
583         (canon lower (func $explicit-thread-suspends-sync) (memory $memory "mem") (core func $explicit-thread-suspends-sync'))
584         (canon lower (func $explicit-thread-suspends-stackful) (memory $memory "mem") (core func $explicit-thread-suspends-stackful'))
585         (canon lower (func $explicit-thread-suspends-stackless) (memory $memory "mem") (core func $explicit-thread-suspends-stackless'))
586         (canon lower (func $explicit-thread-yield-loops-sync) (memory $memory "mem") (core func $explicit-thread-yield-loops-sync'))
587         (canon lower (func $explicit-thread-yield-loops-stackful) (memory $memory "mem") (core func $explicit-thread-yield-loops-stackful'))
588         (canon lower (func $explicit-thread-yield-loops-stackless) (memory $memory "mem") (core func $explicit-thread-yield-loops-stackless'))
589         ;; async lowered
590         (canon lower (func $explicit-thread-calls-return-stackful) async (memory $memory "mem") (core func $explicit-thread-calls-return-stackful-async'))
591         (canon lower (func $explicit-thread-calls-return-stackless) async (memory $memory "mem") (core func $explicit-thread-calls-return-stackless-async'))
592         (canon lower (func $explicit-thread-suspends-sync) async (memory $memory "mem") (core func $explicit-thread-suspends-sync-async'))
593         (canon lower (func $explicit-thread-suspends-stackful) async (memory $memory "mem") (core func $explicit-thread-suspends-stackful-async'))
594         (canon lower (func $explicit-thread-suspends-stackless) async (memory $memory "mem") (core func $explicit-thread-suspends-stackless-async'))
595         (canon lower (func $explicit-thread-yield-loops-sync) async (memory $memory "mem") (core func $explicit-thread-yield-loops-sync-async'))
596         (canon lower (func $explicit-thread-yield-loops-stackful) async (memory $memory "mem") (core func $explicit-thread-yield-loops-stackful-async'))
597         (canon lower (func $explicit-thread-yield-loops-stackless) async (memory $memory "mem") (core func $explicit-thread-yield-loops-stackless-async'))
598         (core instance $dm (instantiate $DM (with "" (instance
599             (export "mem" (memory $memory "mem"))
600             (export "explicit-thread-calls-return-stackful" (func $explicit-thread-calls-return-stackful'))
601             (export "explicit-thread-calls-return-stackless" (func $explicit-thread-calls-return-stackless'))
602             (export "explicit-thread-suspends-sync" (func $explicit-thread-suspends-sync'))
603             (export "explicit-thread-suspends-stackful" (func $explicit-thread-suspends-stackful'))
604             (export "explicit-thread-suspends-stackless" (func $explicit-thread-suspends-stackless'))
605             (export "explicit-thread-yield-loops-sync" (func $explicit-thread-yield-loops-sync'))
606             (export "explicit-thread-yield-loops-stackful" (func $explicit-thread-yield-loops-stackful'))
607             (export "explicit-thread-yield-loops-stackless" (func $explicit-thread-yield-loops-stackless'))
608             (export "explicit-thread-calls-return-stackful-async" (func $explicit-thread-calls-return-stackful-async'))
609             (export "explicit-thread-calls-return-stackless-async" (func $explicit-thread-calls-return-stackless-async'))
610             (export "explicit-thread-suspends-sync-async" (func $explicit-thread-suspends-sync-async'))
611             (export "explicit-thread-suspends-stackful-async" (func $explicit-thread-suspends-stackful-async'))
612             (export "explicit-thread-suspends-stackless-async" (func $explicit-thread-suspends-stackless-async'))
613             (export "explicit-thread-yield-loops-sync-async" (func $explicit-thread-yield-loops-sync-async'))
614             (export "explicit-thread-yield-loops-stackful-async" (func $explicit-thread-yield-loops-stackful-async'))
615             (export "explicit-thread-yield-loops-stackless-async" (func $explicit-thread-yield-loops-stackless-async'))
616             (export "waitable.join" (func $waitable.join))
617             (export "waitable-set.new" (func $waitable-set.new))
618             (export "waitable-set.wait" (func $waitable-set.wait))
619             (export "subtask.cancel" (func $subtask.cancel))
620             (export "thread.yield" (func $thread.yield))
621         ))))
622         (func (export "run") (result u32) (canon lift (core func $dm "run")))
623     )
624 
625     (instance $c (instantiate $C))
626     (instance $d (instantiate $D
627         (with "explicit-thread-calls-return-stackful" (func $c "explicit-thread-calls-return-stackful"))
628         (with "explicit-thread-calls-return-stackless" (func $c "explicit-thread-calls-return-stackless"))
629         (with "explicit-thread-suspends-sync" (func $c "explicit-thread-suspends-sync"))
630         (with "explicit-thread-suspends-stackful" (func $c "explicit-thread-suspends-stackful"))
631         (with "explicit-thread-suspends-stackless" (func $c "explicit-thread-suspends-stackless"))
632         (with "explicit-thread-yield-loops-sync" (func $c "explicit-thread-yield-loops-sync"))
633         (with "explicit-thread-yield-loops-stackful" (func $c "explicit-thread-yield-loops-stackful"))
634         (with "explicit-thread-yield-loops-stackless" (func $c "explicit-thread-yield-loops-stackless"))
635     ))
636   (func (export "run") (alias export $d "run"))
637   (func (export "explicit-thread-calls-return-stackful") (alias export $c "explicit-thread-calls-return-stackful"))
638   (func (export "explicit-thread-calls-return-stackless") (alias export $c "explicit-thread-calls-return-stackless"))
639   (func (export "explicit-thread-suspends-sync") (alias export $c "explicit-thread-suspends-sync"))
640   (func (export "explicit-thread-suspends-stackful") (alias export $c "explicit-thread-suspends-stackful"))
641   (func (export "explicit-thread-suspends-stackless") (alias export $c "explicit-thread-suspends-stackless"))
642   (func (export "explicit-thread-yield-loops-sync") (alias export $c "explicit-thread-yield-loops-sync"))
643   (func (export "explicit-thread-yield-loops-stackful") (alias export $c "explicit-thread-yield-loops-stackful"))
644   (func (export "explicit-thread-yield-loops-stackless") (alias export $c "explicit-thread-yield-loops-stackless"))
645 )
646         "#,
647     )?
648     .serialize()?;
649 
650     let component = unsafe { Component::deserialize(&engine, &component)? };
651     let mut store = Store::new(&engine, ());
652     let instance = Linker::new(&engine)
653         .instantiate_async(&mut store, &component)
654         .await?;
655     let funcs = vec![
656         "run",
657         "explicit-thread-calls-return-stackful",
658         "explicit-thread-calls-return-stackless",
659         "explicit-thread-suspends-sync",
660         "explicit-thread-suspends-stackful",
661         "explicit-thread-suspends-stackless",
662         "explicit-thread-yield-loops-sync",
663         "explicit-thread-yield-loops-stackful",
664         "explicit-thread-yield-loops-stackless",
665     ];
666     for func in funcs {
667         let func = instance.get_typed_func::<(), (u32,)>(&mut store, func)?;
668         assert_eq!(func.call_async(&mut store, ()).await?, (42,));
669         func.post_return_async(store.as_context_mut()).await?;
670     }
671 
672     Ok(())
673 }
674