seems like when multiple components are composed with wac-graph, block_on no longer polls futures spawned with wit-bindgen::spawn_local, is that accurate?
is wit-bindgen::spawn_local even still supposed to be used? or was it just not removed in wasip3? and shouldn't std::thread::spawn work now in wasip3 as an alternative?
well, looks like wasip3 isn't quite ready to be the user space for a microkernel then. shame, because was this close to getting a wasip3 hello world running, but just can't implement the wasi api's:
scripts/boot.sh
exec 1
wos: starting PID1
run 1
recv 1 Irq(8)
suspend 1
run 1
send 1 Irq(8)
Hello, world!
exec 2
suspend 1
run 2
suspend 2
run 1
suspend 1
run 2
block 2
suspend 2
run 1
send 1 Ipc(2)
block 1
suspend 1
run 2
recv 2 Ipc(1)
suspend 2
run 2
send 2 Ipc(1)
exit 2 with error UnreachableCodeReached
run 1
recv 1 Ipc(2)
send 1 Irq(8)
panic PanicHookInfo { payload: Any { .. }, location: Location { file: "/home/dvc/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wit-bindgen-0.62.0/src/rt/async_support/waitable.rs", line: 244, column: 13 }, can_unwind: true, force_no_backtrace: false }block 1
suspend 1
impl exports::wasi::cli::stdout::Guest for WasiHandler {
fn write_via_stream(stream: StreamReader<u8>) -> FutureReader<Result<(), ErrorCode>> {
fn f() -> Result<(), ErrorCode> {
Ok(())
}
let (writer, reader) = wit_future::new(f);
/*std::thread::spawn(|| {
panic!("spawning thread");
wit_bindgen::block_on(async move {
let bytes = stream.collect().await;
while CONFIG
.get()
.unwrap()
.stdout
.send(bytes.clone())
.await
.is_err()
{}
writer.write(Ok(()));
})
});*/
reader
}
}
I'm not sure why composing components would prevent spawn_local from working? It does have the limitations described in the docs, but should otherwise work. Do you have a repro?
here's a repro: https://github.com/dvc94ch/wac-repro
to build run ./build.sh, it hangs indefinitely because wit_bindgen::spawn_local is never polled
my hypothesis for why it isn't possible is because wit-bindgen::block_on polls the tasks that were spawned in its memory using the static mut TASKS, and when composing the globals are isolated, so when the first component calls wit-bindgen::block_on it can't see the ones spawned in the other component
but I'm not sure what the correct way of doing it is supposed to be
never mind, decided that a microkernel is the wrong approach. with rust/cargo there isn't much point in splitting an OS into microservices or pushing them into user space anyway and the performance tax is real
I think the issue in your repro was that write-via-stream is sync lifted. So there's no way for it to keep running after it returns (only async lifts can do that).
have most of cli/random/clocks functional, should be able to get the rest working tomorrow.
exec 1
boot at 19:19:14 06.10.2026
run 1
send 1 Irq(8)
Hello, world at 19:19:14
suspend 1
run 1
send 1 Irq(8)
random bytes [196, 144, 86, 232, 59, 5, 210, 228, 71, 99, 123, 178, 119, 241, 179, 131, 50, 111, 166, 10, 15, 193, 229, 190, 227, 241, 151, 80, 91, 255, 236, 25]
send 1 Irq(8)
thread 'main' (1) panicked at app/src/main.rs:23:5:
let's panic!
send 1 Irq(8)
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace
exit 1 with error UnreachableCodeReached
Last updated: Oct 11 2026 at 04:10 UTC