Skip to content

Commit 3a1ecd8

Browse files
authored
avoid sending redundant Status::Started for subtask (#11980)
Previously, we sent this event both immediately after lowering parameters and when the async-with-callback-lifted export returned. The latter was redundant, but it didn't matter in most cases because the second event usually overwrote the first one such that it was only delivered once. However, in cases of three or more components composed together, both events got delivered, and that made `wit-bindgen` justifiably upset. This removes the redundant event and adds a three component composition test to cover the scenario. Signed-off-by: Joel Dice <[email protected]>
1 parent c8dce6f commit 3a1ecd8

File tree

3 files changed

+47
-23
lines changed

3 files changed

+47
-23
lines changed

crates/misc/component-async-tests/tests/scenario/round_trip.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ pub async fn async_round_trip_stackless_plus_stackless() -> Result<()> {
4242
.await
4343
}
4444

45+
#[tokio::test]
46+
pub async fn async_round_trip_stackless_plus_stackless_plus_stackless() -> Result<()> {
47+
test_round_trip_composed_more(
48+
test_programs_artifacts::ASYNC_ROUND_TRIP_STACKLESS_COMPONENT,
49+
test_programs_artifacts::ASYNC_ROUND_TRIP_STACKLESS_COMPONENT,
50+
test_programs_artifacts::ASYNC_ROUND_TRIP_STACKLESS_COMPONENT,
51+
)
52+
.await
53+
}
54+
4555
#[tokio::test]
4656
async fn async_round_trip_synchronous_plus_stackless() -> Result<()> {
4757
test_round_trip_composed(
@@ -478,3 +488,27 @@ pub async fn test_round_trip_composed(a: &str, b: &str) -> Result<()> {
478488
)
479489
.await
480490
}
491+
492+
pub async fn test_round_trip_composed_more(a: &str, b: &str, c: &str) -> Result<()> {
493+
test_round_trip(
494+
&[a, b, c],
495+
&[
496+
(
497+
"hello, world!",
498+
"hello, world! - entered guest - entered guest - entered guest - entered host \
499+
- exited host - exited guest - exited guest - exited guest",
500+
),
501+
(
502+
"¡hola, mundo!",
503+
"¡hola, mundo! - entered guest - entered guest - entered guest - entered host \
504+
- exited host - exited guest - exited guest - exited guest",
505+
),
506+
(
507+
"hi y'all!",
508+
"hi y'all! - entered guest - entered guest - entered guest - entered host \
509+
- exited host - exited guest - exited guest - exited guest",
510+
),
511+
],
512+
)
513+
.await
514+
}

crates/misc/component-async-tests/tests/scenario/util.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,18 @@ pub async fn make_component(engine: &Engine, components: &[&str]) -> Result<Comp
9898
}
9999

100100
async fn compile(engine: &Engine, components: &[&str]) -> Result<Vec<u8>> {
101-
match components {
102-
[component] => engine.precompile_component(&fs::read(component).await?),
103-
[a, b] => engine
104-
.precompile_component(&compose(&fs::read(a).await?, &fs::read(b).await?).await?),
105-
_ => Err(anyhow!("expected one or two paths")),
101+
let mut composed = None::<Vec<u8>>;
102+
for component in components {
103+
let component = fs::read(component).await?;
104+
if let Some(other) = composed.take() {
105+
composed = Some(compose(&other, &component).await?);
106+
} else {
107+
composed = Some(component);
108+
}
106109
}
110+
engine.precompile_component(
111+
&composed.ok_or_else(|| anyhow!("expected at least one component"))?,
112+
)
107113
}
108114

109115
async fn load(engine: &Engine, components: &[&str]) -> Result<Vec<u8>> {

crates/wasmtime/src/runtime/component/concurrent.rs

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,7 @@ fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> {
845845

846846
store.maybe_pop_call_context(call.thread.task)?;
847847

848-
instance.handle_callback_code(store, call.thread, runtime_instance, code, false)?;
848+
instance.handle_callback_code(store, call.thread, runtime_instance, code)?;
849849

850850
store.concurrent_state_mut().guest_thread = old_thread;
851851
log::trace!("GuestCallKind::DeliverEvent: restored {old_thread:?} as current thread");
@@ -1579,34 +1579,18 @@ impl Instance {
15791579

15801580
/// Handle the `CallbackCode` returned from an async-lifted export or its
15811581
/// callback.
1582-
///
1583-
/// If `initial_call` is `true`, then the code was received from the
1584-
/// async-lifted export; otherwise, it was received from its callback.
15851582
fn handle_callback_code(
15861583
self,
15871584
store: &mut StoreOpaque,
15881585
guest_thread: QualifiedThreadId,
15891586
runtime_instance: RuntimeComponentInstanceIndex,
15901587
code: u32,
1591-
initial_call: bool,
15921588
) -> Result<()> {
15931589
let (code, set) = unpack_callback_code(code);
15941590

15951591
log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
15961592

15971593
let state = store.concurrent_state_mut();
1598-
if !state.get_mut(guest_thread.task)?.returned_or_cancelled() {
1599-
if initial_call {
1600-
// Notify any current or future waiters that this subtask has
1601-
// started.
1602-
Waitable::Guest(guest_thread.task).set_event(
1603-
state,
1604-
Some(Event::Subtask {
1605-
status: Status::Started,
1606-
}),
1607-
)?;
1608-
}
1609-
}
16101594

16111595
let get_set = |store, handle| {
16121596
if handle == 0 {
@@ -1876,7 +1860,7 @@ impl Instance {
18761860
// function returns a `i32` result.
18771861
let code = unsafe { storage[0].assume_init() }.get_i32() as u32;
18781862

1879-
self.handle_callback_code(store, guest_thread, callee_instance, code, true)?;
1863+
self.handle_callback_code(store, guest_thread, callee_instance, code)?;
18801864

18811865
Ok(())
18821866
}) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>

0 commit comments

Comments
 (0)