Stream: git-wasmtime

Topic: wasmtime / issue #10788 How to keep the fifo named pipe a...


view this post on Zulip Wasmtime GitHub notifications bot (May 16 2025 at 22:56):

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 using componentize-py. The WASM component is then loaded into the latest wasmtime in Rust from the main branch. 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 with WasiCtxBuilder::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:
A simpleadd.wasm was built using componentize-py -d simpleadd.wit -w simpleadd componentize impl -o simpleadd.wasm

simpleadd.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 -1

main.rs:

use 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 run

Thanks,
Bear

view this post on Zulip Wasmtime GitHub notifications bot (May 16 2025 at 22:57):

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 using componentize-py. The WASM component is then loaded into the latest wasmtime in Rust from the main branch. 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 with WasiCtxBuilder::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.wasm was built using componentize-py -d simpleadd.wit -w simpleadd componentize impl -o simpleadd.wasm

simpleadd.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 -1

main.rs:

use 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 run

Thanks,
Bear

view this post on Zulip Wasmtime GitHub notifications bot (May 18 2025 at 15:54):

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_fifo to write to the FIFO and from there once the component was running I was able to type help plus 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 the cat process 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 help a few times may not be sufficient to test what you're thinking.

view this post on Zulip Wasmtime GitHub notifications bot (May 19 2025 at 19:12):

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 using componentize-py. The WASM component is then loaded into the latest wasmtime in Rust from the main branch. 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 with WasiCtxBuilder::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.wasm was built using componentize-py -d simpleadd.wit -w simpleadd componentize impl -o simpleadd.wasm

simpleadd.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 -1

main.rs:

use 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 run

Thanks,
Bear

view this post on Zulip Wasmtime GitHub notifications bot (May 19 2025 at 19:12):

yophilav commented on issue #10788:

Welp. Thank you for pointing out that it's a fumble on my part. The cat > ./test_fifo works as expected. I did echo -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