Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/misc/component-async-tests/tests/scenario/round_trip_many.rs
1693 views
1
use std::iter;
2
use std::sync::{
3
Arc, Mutex,
4
atomic::{AtomicU32, Ordering::Relaxed},
5
};
6
use std::time::Duration;
7
8
use super::util::{config, make_component};
9
use anyhow::{Result, anyhow};
10
use component_async_tests::Ctx;
11
use component_async_tests::util::sleep;
12
use futures::{
13
FutureExt,
14
stream::{FuturesUnordered, TryStreamExt},
15
};
16
use wasmtime::component::{Linker, ResourceTable, Val};
17
use wasmtime::{Engine, Store};
18
use wasmtime_wasi::WasiCtxBuilder;
19
20
#[tokio::test]
21
pub async fn async_round_trip_many_stackless() -> Result<()> {
22
test_round_trip_many_uncomposed(
23
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
24
)
25
.await
26
}
27
28
#[tokio::test]
29
pub async fn async_round_trip_many_stackful() -> Result<()> {
30
test_round_trip_many_uncomposed(
31
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
32
)
33
.await
34
}
35
36
#[tokio::test]
37
pub async fn async_round_trip_many_synchronous() -> Result<()> {
38
test_round_trip_many_uncomposed(
39
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
40
)
41
.await
42
}
43
44
#[tokio::test]
45
pub async fn async_round_trip_many_wait() -> Result<()> {
46
test_round_trip_many_uncomposed(test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT)
47
.await
48
}
49
50
#[tokio::test]
51
async fn async_round_trip_many_stackless_plus_stackless() -> Result<()> {
52
test_round_trip_many_composed(
53
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
54
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
55
)
56
.await
57
}
58
59
#[tokio::test]
60
async fn async_round_trip_many_synchronous_plus_stackless() -> Result<()> {
61
test_round_trip_many_composed(
62
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
63
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
64
)
65
.await
66
}
67
68
#[tokio::test]
69
async fn async_round_trip_many_stackless_plus_synchronous() -> Result<()> {
70
test_round_trip_many_composed(
71
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
72
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
73
)
74
.await
75
}
76
77
#[tokio::test]
78
async fn async_round_trip_many_synchronous_plus_synchronous() -> Result<()> {
79
test_round_trip_many_composed(
80
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
81
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
82
)
83
.await
84
}
85
86
#[tokio::test]
87
async fn async_round_trip_many_wait_plus_wait() -> Result<()> {
88
test_round_trip_many_composed(
89
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,
90
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,
91
)
92
.await
93
}
94
95
#[tokio::test]
96
async fn async_round_trip_many_synchronous_plus_wait() -> Result<()> {
97
test_round_trip_many_composed(
98
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
99
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,
100
)
101
.await
102
}
103
104
#[tokio::test]
105
async fn async_round_trip_many_wait_plus_synchronous() -> Result<()> {
106
test_round_trip_many_composed(
107
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,
108
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
109
)
110
.await
111
}
112
113
#[tokio::test]
114
async fn async_round_trip_many_stackless_plus_wait() -> Result<()> {
115
test_round_trip_many_composed(
116
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
117
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,
118
)
119
.await
120
}
121
122
#[tokio::test]
123
async fn async_round_trip_many_wait_plus_stackless() -> Result<()> {
124
test_round_trip_many_composed(
125
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,
126
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
127
)
128
.await
129
}
130
131
#[tokio::test]
132
async fn async_round_trip_many_stackful_plus_stackful() -> Result<()> {
133
test_round_trip_many_composed(
134
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
135
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
136
)
137
.await
138
}
139
140
#[tokio::test]
141
async fn async_round_trip_many_stackful_plus_stackless() -> Result<()> {
142
test_round_trip_many_composed(
143
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
144
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
145
)
146
.await
147
}
148
149
#[tokio::test]
150
async fn async_round_trip_many_stackless_plus_stackful() -> Result<()> {
151
test_round_trip_many_composed(
152
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
153
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
154
)
155
.await
156
}
157
158
#[tokio::test]
159
async fn async_round_trip_many_synchronous_plus_stackful() -> Result<()> {
160
test_round_trip_many_composed(
161
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
162
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
163
)
164
.await
165
}
166
167
#[tokio::test]
168
async fn async_round_trip_many_stackful_plus_synchronous() -> Result<()> {
169
test_round_trip_many_composed(
170
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
171
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
172
)
173
.await
174
}
175
176
async fn test_round_trip_many_uncomposed(component: &str) -> Result<()> {
177
test_round_trip_many(
178
&[component],
179
&[
180
(
181
"hello, world!",
182
"hello, world! - entered guest - entered host - exited host - exited guest",
183
),
184
(
185
"¡hola, mundo!",
186
"¡hola, mundo! - entered guest - entered host - exited host - exited guest",
187
),
188
(
189
"hi y'all!",
190
"hi y'all! - entered guest - entered host - exited host - exited guest",
191
),
192
],
193
)
194
.await
195
}
196
197
async fn test_round_trip_many(
198
components: &[&str],
199
inputs_and_outputs: &[(&str, &str)],
200
) -> Result<()> {
201
use component_async_tests::round_trip_many::bindings::exports::local::local::many;
202
203
let engine = Engine::new(&config())?;
204
205
let make_store = || {
206
Store::new(
207
&engine,
208
Ctx {
209
wasi: WasiCtxBuilder::new().inherit_stdio().build(),
210
table: ResourceTable::default(),
211
continue_: false,
212
wakers: Arc::new(Mutex::new(None)),
213
},
214
)
215
};
216
217
let component = make_component(&engine, components).await?;
218
219
let b = 42;
220
let c = vec![42u8; 42];
221
let d = (4242, 424242424242);
222
let e = many::Stuff {
223
a: vec![42i32; 42],
224
b: true,
225
c: 424242,
226
};
227
let f = Some(e.clone());
228
let g = Err(());
229
230
// On miri, we only use one call style per test since they take so long to
231
// run. On non-miri, we use every call style for each test.
232
static CALL_STYLE_COUNTER: AtomicU32 = AtomicU32::new(0);
233
let call_style = CALL_STYLE_COUNTER.fetch_add(1, Relaxed) % 4;
234
235
// First, test the `wasmtime-wit-bindgen` static API:
236
{
237
let mut linker = Linker::new(&engine);
238
239
wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
240
component_async_tests::round_trip_many::bindings::local::local::many::add_to_linker::<
241
_,
242
Ctx,
243
>(&mut linker, |ctx| ctx)?;
244
245
let mut store = make_store();
246
247
let instance = linker.instantiate_async(&mut store, &component).await?;
248
let round_trip_many = component_async_tests::round_trip_many::bindings::RoundTripMany::new(
249
&mut store, &instance,
250
)?;
251
252
if call_style == 0 {
253
instance
254
.run_concurrent(&mut store, {
255
let c = c.clone();
256
let e = e.clone();
257
let f = f.clone();
258
let g = g.clone();
259
let inputs_and_outputs = inputs_and_outputs
260
.iter()
261
.map(|(a, b)| ((*a).to_owned(), (*b).to_owned()))
262
.collect::<Vec<_>>();
263
async move |accessor| {
264
// Start concurrent calls and then join them all:
265
let mut futures = FuturesUnordered::new();
266
for (input, output) in inputs_and_outputs {
267
futures.push(
268
round_trip_many
269
.local_local_many()
270
.call_foo(
271
accessor,
272
input,
273
b,
274
c.clone(),
275
d,
276
e.clone(),
277
f.clone(),
278
g.clone(),
279
)
280
.map(move |v| v.map(move |v| (v, output))),
281
);
282
}
283
284
while let Some((actual, expected)) = futures.try_next().await? {
285
assert_eq!(
286
(expected, b, c.clone(), d, e.clone(), f.clone(), g.clone()),
287
actual
288
);
289
}
290
291
anyhow::Ok(())
292
}
293
})
294
.await??;
295
296
instance.assert_concurrent_state_empty(&mut store);
297
}
298
299
if call_style == 1 {
300
// Now do it again using `TypedFunc::call_async`-based bindings:
301
let e = component_async_tests::round_trip_many::non_concurrent_export_bindings::exports::local::local::many::Stuff {
302
a: vec![42i32; 42],
303
b: true,
304
c: 424242,
305
};
306
let f = Some(e.clone());
307
let g = Err(());
308
309
let round_trip_many = component_async_tests::round_trip_many::non_concurrent_export_bindings::RoundTripMany::instantiate_async(
310
&mut store, &component, &linker,
311
)
312
.await?;
313
314
for (input, expected) in inputs_and_outputs {
315
assert_eq!(
316
(
317
(*expected).to_owned(),
318
b,
319
c.clone(),
320
d,
321
e.clone(),
322
f.clone(),
323
g.clone()
324
),
325
round_trip_many
326
.local_local_many()
327
.call_foo(&mut store, input, b, &c, d, &e, f.as_ref(), Err(()))
328
.await?
329
);
330
}
331
332
instance.assert_concurrent_state_empty(&mut store);
333
}
334
}
335
336
// Now do it again using the dynamic API (except for WASI, where we stick with the static API):
337
{
338
let mut linker = Linker::new(&engine);
339
340
wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
341
linker
342
.root()
343
.instance("local:local/many")?
344
.func_new_concurrent("[async]foo", |_, params, results| {
345
Box::pin(async move {
346
sleep(Duration::from_millis(10)).await;
347
let mut params = params.into_iter();
348
let Some(Val::String(s)) = params.next() else {
349
unreachable!()
350
};
351
results[0] = Val::Tuple(
352
iter::once(Val::String(format!("{s} - entered host - exited host")))
353
.chain(params.cloned())
354
.collect(),
355
);
356
Ok(())
357
})
358
})?;
359
360
let mut store = make_store();
361
362
let instance = linker.instantiate_async(&mut store, &component).await?;
363
let baz_instance = instance
364
.get_export_index(&mut store, None, "local:local/many")
365
.ok_or_else(|| anyhow!("can't find `local:local/many` in instance"))?;
366
let foo_function = instance
367
.get_export_index(&mut store, Some(&baz_instance), "[async]foo")
368
.ok_or_else(|| anyhow!("can't find `foo` in instance"))?;
369
let foo_function = instance
370
.get_func(&mut store, foo_function)
371
.ok_or_else(|| anyhow!("can't find `foo` in instance"))?;
372
373
let make = |input: &str| {
374
let stuff = Val::Record(vec![
375
(
376
"a".into(),
377
Val::List(e.a.iter().map(|v| Val::S32(*v)).collect()),
378
),
379
("b".into(), Val::Bool(e.b)),
380
("c".into(), Val::U64(e.c)),
381
]);
382
vec![
383
Val::String(input.to_owned()),
384
Val::U32(b),
385
Val::List(c.iter().map(|v| Val::U8(*v)).collect()),
386
Val::Tuple(vec![Val::U64(d.0), Val::U64(d.1)]),
387
stuff.clone(),
388
Val::Option(Some(Box::new(stuff))),
389
Val::Result(Err(None)),
390
]
391
};
392
393
if call_style == 2 {
394
instance
395
.run_concurrent(&mut store, async |store| -> wasmtime::Result<_> {
396
// Start three concurrent calls and then join them all:
397
let mut futures = FuturesUnordered::new();
398
for (input, output) in inputs_and_outputs {
399
let output = (*output).to_owned();
400
futures.push(async move {
401
let mut result = vec![Val::Bool(false)];
402
foo_function
403
.call_concurrent(store, &make(input), &mut result)
404
.await?;
405
anyhow::Ok((result, output))
406
});
407
}
408
409
while let Some((actual, expected)) = futures.try_next().await? {
410
let Some(Val::Tuple(actual)) = actual.into_iter().next() else {
411
unreachable!()
412
};
413
assert_eq!(make(&expected), actual);
414
}
415
Ok(())
416
})
417
.await??;
418
419
instance.assert_concurrent_state_empty(&mut store);
420
}
421
422
if call_style == 3 {
423
// Now do it again using `Func::call_async`:
424
for (input, expected) in inputs_and_outputs {
425
let mut results = [Val::Bool(false)];
426
foo_function
427
.call_async(&mut store, &make(input), &mut results)
428
.await?;
429
let Val::Tuple(actual) = &results[0] else {
430
unreachable!()
431
};
432
assert_eq!(&make(expected), actual);
433
foo_function.post_return_async(&mut store).await?;
434
}
435
436
instance.assert_concurrent_state_empty(&mut store);
437
}
438
}
439
440
Ok(())
441
}
442
443
pub async fn test_round_trip_many_composed(a: &str, b: &str) -> Result<()> {
444
test_round_trip_many(
445
&[a, b],
446
&[
447
(
448
"hello, world!",
449
"hello, world! - entered guest - entered guest - entered host \
450
- exited host - exited guest - exited guest",
451
),
452
(
453
"¡hola, mundo!",
454
"¡hola, mundo! - entered guest - entered guest - entered host \
455
- exited host - exited guest - exited guest",
456
),
457
(
458
"hi y'all!",
459
"hi y'all! - entered guest - entered guest - entered host \
460
- exited host - exited guest - exited guest",
461
),
462
],
463
)
464
.await
465
}
466
467