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