use std::io::{BufRead, Write};
use std::process::Command;
use wasmtime_wasi::cli::StdinStream;
use wasmtime_wasi::p2::Pollable;
const VAR_NAME: &str = "__CHILD_PROCESS";
fn main() {
if cfg!(miri) {
return;
}
if wasmtime_test_util::cargo_test_runner().is_some() {
return;
}
match std::env::var(VAR_NAME) {
Ok(_) => child_process(),
Err(_) => parent_process(),
}
fn child_process() {
let mut result_write = std::io::stderr();
let mut child_running = true;
while child_running {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
'task: loop {
println!("child: creating stdin");
let mut stdin = wasmtime_wasi::cli::stdin().p2_stream();
println!("child: checking that stdin is not ready");
assert!(
tokio::time::timeout(
std::time::Duration::from_millis(100),
stdin.ready()
)
.await
.is_err(),
"stdin available too soon"
);
writeln!(&mut result_write, "start").unwrap();
println!("child: started");
let mut buffer = String::new();
loop {
println!("child: waiting for stdin to be ready");
stdin.ready().await;
println!("child: reading input");
let bytes = stdin.read(1024).unwrap();
println!("child got: {bytes:?}");
buffer.push_str(std::str::from_utf8(bytes.as_ref()).unwrap());
if let Some((line, rest)) = buffer.split_once('\n') {
if line == "all done" {
writeln!(&mut result_write, "done").unwrap();
println!("child: exiting...");
child_running = false;
break 'task;
} else if line == "restart_runtime" {
writeln!(&mut result_write, "restarting").unwrap();
println!("child: restarting runtime...");
break 'task;
} else if line == "restart_task" {
writeln!(&mut result_write, "restarting").unwrap();
println!("child: restarting task...");
continue 'task;
} else {
writeln!(&mut result_write, "{line}").unwrap();
}
buffer = rest.to_owned();
}
}
}
});
println!("child: runtime exited");
}
println!("child: exiting");
}
}
fn parent_process() {
let me = std::env::current_exe().unwrap();
let mut cmd = Command::new(me);
cmd.env(VAR_NAME, "1");
cmd.stdin(std::process::Stdio::piped());
if std::env::args().any(|arg| arg == "--nocapture") {
cmd.stdout(std::process::Stdio::inherit());
} else {
cmd.stdout(std::process::Stdio::null());
}
cmd.stderr(std::process::Stdio::piped());
let mut child = cmd.spawn().unwrap();
let mut stdin_write = child.stdin.take().unwrap();
let mut result_read = std::io::BufReader::new(child.stderr.take().unwrap());
let mut line = String::new();
result_read.read_line(&mut line).unwrap();
assert_eq!(line, "start\n");
for i in 0..5 {
let message = format!("some bytes {i}\n");
stdin_write.write_all(message.as_bytes()).unwrap();
line.clear();
result_read.read_line(&mut line).unwrap();
assert_eq!(line, message);
}
writeln!(&mut stdin_write, "restart_task").unwrap();
line.clear();
result_read.read_line(&mut line).unwrap();
assert_eq!(line, "restarting\n");
line.clear();
result_read.read_line(&mut line).unwrap();
assert_eq!(line, "start\n");
for i in 0..10 {
let message = format!("more bytes {i}\n");
stdin_write.write_all(message.as_bytes()).unwrap();
line.clear();
result_read.read_line(&mut line).unwrap();
assert_eq!(line, message);
}
writeln!(&mut stdin_write, "restart_runtime").unwrap();
line.clear();
result_read.read_line(&mut line).unwrap();
assert_eq!(line, "restarting\n");
line.clear();
result_read.read_line(&mut line).unwrap();
assert_eq!(line, "start\n");
for i in 0..17 {
let message = format!("even more bytes {i}\n");
stdin_write.write_all(message.as_bytes()).unwrap();
line.clear();
result_read.read_line(&mut line).unwrap();
assert_eq!(line, message);
}
writeln!(&mut stdin_write, "all done").unwrap();
line.clear();
result_read.read_line(&mut line).unwrap();
assert_eq!(line, "done\n");
}