yophilav opened issue #10788:
Hi Wasmtime folks!
Hope you are having a great day so far!
TL;DR - How to keep a unix named pipe alive on continuous read with wasmtime's.stdin()?Full Context:
I have Python Debugger (PDB) "compiles" into a WASM component usingcomponentize-py. The WASM component is then loaded into the latestwasmtimein Rust from themainbranch. The Rust wasmtime is then compiled and run to load the wasm component. The wasm component then launches an interative terminal -- a debugging session.When I do
WasiCtxBuilder::new().inherit_stdin(), this works great. I can have an interactive on-going PDB session.
However, when I change from getting user input via STDIN to be using a FIFO named pipe withWasiCtxBuilder::new().stdin(wasm_stdin_stream), the communication seem to just stopped (and the PDB returned) after the first command is provided through the pipe. I'm hoping there is a way I can implement an interactive debugging session through the FIFO pipe just like when.inherit_stdin()is used.WASM component setup:
Asimpleadd.wasmwas built usingcomponentize-py -d simpleadd.wit -w simpleadd componentize impl -o simpleadd.wasmsimpleadd.wit:
package example:simpleadd; world simpleadd { export add: func(l: s32, r: s32) -> s32; }impl.py:
import simpleadd import pdb class Simpleadd(simpleadd.Simpleadd): def add(self, l, r) -> int: try: x = l + r pdb.set_trace() return x except Exception as e: if str(type(e).__name__) == "BdbQuit": return x else: return -1use tokio::fs::File; use std::fs::{OpenOptions}; use wasmtime_wasi::p2::{add_to_linker_sync, AsyncStdinStream,OutputFile, pipe::AsyncReadStream, WasiCtx, IoView, WasiCtxBuilder, WasiView}; use wasmtime::{ component::{Component, Linker, ResourceTable}, Config, Engine, Store }; struct Ctx { table: ResourceTable, wasi_ctx: WasiCtx, } impl IoView for Ctx { fn table(&mut self) -> &mut ResourceTable { &mut self.table } } impl WasiView for Ctx { fn ctx(&mut self) -> &mut WasiCtx { &mut self.wasi_ctx } } fn main() -> wasmtime::Result<()> { let mut config = Config::new(); config.debug_info(true); config.wasm_component_model(true); let engine: Engine = Engine::new(&config)?; let path = "/path/to/simpleadd.wasm"; let component = Component::from_file(&engine, path)?; let mut linker = Linker::new(&engine); add_to_linker_sync(&mut linker)?; println!("Waiting for FIFO Rx..."); // mkfifo /path/to/test_fifo let stdin_fifo_path = "/path/to/test_fifo"; let stdin_file = OpenOptions::new() .read(true) .open(stdin_fifo_path) .expect("Failed to open FIFO file"); let tokio_stdin_fifo = File::from_std(stdin_file); let wasm_stdin_stream = AsyncStdinStream::new(AsyncReadStream::new(tokio_stdin_fifo)); let ctx = Ctx { table: ResourceTable::new(), wasi_ctx: WasiCtxBuilder::new() .stdin(wasm_stdin_stream) //.inherit_stdin() .inherit_stdout() .inherit_stderr() .build(), }; let mut store = Store::new(&engine, ctx); let instance = linker.instantiate(&mut store, &component)?; println!("Instantiated component..."); let add_fnx = instance.get_typed_func::<(i32, i32), (i32,)>(&mut store, "add")?; let result = add_fnx.call(&mut store, (22, 20))?; println!("Result from add module: {:?}", result.0); Ok(()) }To run:
mkfifo /path/to/test_fifo cargo runThanks,
Bear
yophilav edited issue #10788:
Hi Wasmtime folks!
Hope you are having a great day so far!
TL;DR - How to keep a unix named pipe alive on continuous read with wasmtime's.stdin()?Full Context:
I have Python Debugger (PDB) "compiles" into a WASM component usingcomponentize-py. The WASM component is then loaded into the latestwasmtimein Rust from themainbranch. The Rust wasmtime is then compiled and run to load the wasm component. The wasm component then launches an interative terminal -- a debugging session.When I do
WasiCtxBuilder::new().inherit_stdin(), this works great. I can have an interactive on-going PDB session.
However, when I change from getting user input via STDIN to be using a FIFO named pipe withWasiCtxBuilder::new().stdin(wasm_stdin_stream), the communication seem to just stopped (and the PDB returned) after the first command is provided through the pipe. I'm hoping there is a way I can implement an interactive debugging session through the FIFO pipe just like when.inherit_stdin()is used.WASM component setup:
simpleadd.wasmwas built usingcomponentize-py -d simpleadd.wit -w simpleadd componentize impl -o simpleadd.wasmsimpleadd.wit:
package example:simpleadd; world simpleadd { export add: func(l: s32, r: s32) -> s32; }impl.py:
import simpleadd import pdb class Simpleadd(simpleadd.Simpleadd): def add(self, l, r) -> int: try: x = l + r pdb.set_trace() return x except Exception as e: if str(type(e).__name__) == "BdbQuit": return x else: return -1use tokio::fs::File; use std::fs::{OpenOptions}; use wasmtime_wasi::p2::{add_to_linker_sync, AsyncStdinStream,OutputFile, pipe::AsyncReadStream, WasiCtx, IoView, WasiCtxBuilder, WasiView}; use wasmtime::{ component::{Component, Linker, ResourceTable}, Config, Engine, Store }; struct Ctx { table: ResourceTable, wasi_ctx: WasiCtx, } impl IoView for Ctx { fn table(&mut self) -> &mut ResourceTable { &mut self.table } } impl WasiView for Ctx { fn ctx(&mut self) -> &mut WasiCtx { &mut self.wasi_ctx } } fn main() -> wasmtime::Result<()> { let mut config = Config::new(); config.debug_info(true); config.wasm_component_model(true); let engine: Engine = Engine::new(&config)?; let path = "/path/to/simpleadd.wasm"; let component = Component::from_file(&engine, path)?; let mut linker = Linker::new(&engine); add_to_linker_sync(&mut linker)?; println!("Waiting for FIFO Rx..."); // mkfifo /path/to/test_fifo let stdin_fifo_path = "/path/to/test_fifo"; let stdin_file = OpenOptions::new() .read(true) .open(stdin_fifo_path) .expect("Failed to open FIFO file"); let tokio_stdin_fifo = File::from_std(stdin_file); let wasm_stdin_stream = AsyncStdinStream::new(AsyncReadStream::new(tokio_stdin_fifo)); let ctx = Ctx { table: ResourceTable::new(), wasi_ctx: WasiCtxBuilder::new() .stdin(wasm_stdin_stream) //.inherit_stdin() .inherit_stdout() .inherit_stderr() .build(), }; let mut store = Store::new(&engine, ctx); let instance = linker.instantiate(&mut store, &component)?; println!("Instantiated component..."); let add_fnx = instance.get_typed_func::<(i32, i32), (i32,)>(&mut store, "add")?; let result = add_fnx.call(&mut store, (22, 20))?; println!("Result from add module: {:?}", result.0); Ok(()) }To run:
mkfifo /path/to/test_fifo cargo runThanks,
Bear
alexcrichton commented on issue #10788:
Can you describe a bit more about what you're seeing and how it differs from what you expect? I tried this example locally and it seems to work reasonably well, but I've also never worked with FIFOs before and I also am not 100% sure what you're expecting.
For example what I used was
cat > ./test_fifoto write to the FIFO and from there once the component was running I was able to typehelpplus enter multiple times and the help text showed up in the wasmtime session. I could enter other commands and then once Wasmtime exited a write into the FIFO would abort thecatprocess due to what I suspect was SIGPIPE.Above you say:
the communication seem to just stopped (and the PDB returned) after the first command is provided through the pipe
Can you provide the commands you're trying to execute through the FIFO? And what you're seeing and what you're expecting to see? I've also never worked with PDB/Python in this regard before so my test of running
helpa few times may not be sufficient to test what you're thinking.
yophilav closed issue #10788:
Hi Wasmtime folks!
Hope you are having a great day so far!
TL;DR - How to keep a unix named pipe alive on continuous read with wasmtime's.stdin()?Full Context:
I have Python Debugger (PDB) "compiles" into a WASM component usingcomponentize-py. The WASM component is then loaded into the latestwasmtimein Rust from themainbranch. The Rust wasmtime is then compiled and run to load the wasm component. The wasm component then launches an interative terminal -- a debugging session.When I do
WasiCtxBuilder::new().inherit_stdin(), this works great. I can have an interactive on-going PDB session.
However, when I change from getting user input via STDIN to be using a FIFO named pipe withWasiCtxBuilder::new().stdin(wasm_stdin_stream), the communication seem to just stopped (and the PDB returned) after the first command is provided through the pipe. I'm hoping there is a way I can implement an interactive debugging session through the FIFO pipe just like when.inherit_stdin()is used.WASM component setup:
simpleadd.wasmwas built usingcomponentize-py -d simpleadd.wit -w simpleadd componentize impl -o simpleadd.wasmsimpleadd.wit:
package example:simpleadd; world simpleadd { export add: func(l: s32, r: s32) -> s32; }impl.py:
import simpleadd import pdb class Simpleadd(simpleadd.Simpleadd): def add(self, l, r) -> int: try: x = l + r pdb.set_trace() return x except Exception as e: if str(type(e).__name__) == "BdbQuit": return x else: return -1use tokio::fs::File; use std::fs::{OpenOptions}; use wasmtime_wasi::p2::{add_to_linker_sync, AsyncStdinStream,OutputFile, pipe::AsyncReadStream, WasiCtx, IoView, WasiCtxBuilder, WasiView}; use wasmtime::{ component::{Component, Linker, ResourceTable}, Config, Engine, Store }; struct Ctx { table: ResourceTable, wasi_ctx: WasiCtx, } impl IoView for Ctx { fn table(&mut self) -> &mut ResourceTable { &mut self.table } } impl WasiView for Ctx { fn ctx(&mut self) -> &mut WasiCtx { &mut self.wasi_ctx } } fn main() -> wasmtime::Result<()> { let mut config = Config::new(); config.debug_info(true); config.wasm_component_model(true); let engine: Engine = Engine::new(&config)?; let path = "/path/to/simpleadd.wasm"; let component = Component::from_file(&engine, path)?; let mut linker = Linker::new(&engine); add_to_linker_sync(&mut linker)?; println!("Waiting for FIFO Rx..."); // mkfifo /path/to/test_fifo let stdin_fifo_path = "/path/to/test_fifo"; let stdin_file = OpenOptions::new() .read(true) .open(stdin_fifo_path) .expect("Failed to open FIFO file"); let tokio_stdin_fifo = File::from_std(stdin_file); let wasm_stdin_stream = AsyncStdinStream::new(AsyncReadStream::new(tokio_stdin_fifo)); let ctx = Ctx { table: ResourceTable::new(), wasi_ctx: WasiCtxBuilder::new() .stdin(wasm_stdin_stream) //.inherit_stdin() .inherit_stdout() .inherit_stderr() .build(), }; let mut store = Store::new(&engine, ctx); let instance = linker.instantiate(&mut store, &component)?; println!("Instantiated component..."); let add_fnx = instance.get_typed_func::<(i32, i32), (i32,)>(&mut store, "add")?; let result = add_fnx.call(&mut store, (22, 20))?; println!("Result from add module: {:?}", result.0); Ok(()) }To run:
mkfifo /path/to/test_fifo cargo runThanks,
Bear
yophilav commented on issue #10788:
Welp. Thank you for pointing out that it's a fumble on my part. The
cat > ./test_fifoworks as expected. I didecho -n "where"which doesn't accept stdio, only string param which obv terminates at the end of the string.Thank you!
Last updated: Dec 06 2025 at 06:05 UTC