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