use std::time::Duration;
use crate::term::LogicError;
#[cfg(not(feature = "tokio"))]
pub type ChildType = std::process::Child;
#[cfg(feature = "tokio")]
pub type ChildType = tokio::process::Child;
#[cfg(not(feature = "tokio"))]
pub type StdinType = std::process::ChildStdin;
#[cfg(feature = "tokio")]
pub type StdinType = tokio::process::ChildStdin;
#[cfg(not(feature = "tokio"))]
pub type StdoutType = std::io::BufReader<std::process::ChildStdout>;
#[cfg(feature = "tokio")]
pub type StdoutType = tokio::io::BufReader<tokio::process::ChildStdout>;
#[cfg(not(feature = "tokio"))]
pub type JoinHandleType = std::thread::JoinHandle<()>;
#[cfg(feature = "tokio")]
pub type JoinHandleType = tokio::task::JoinHandle<()>;
pub struct Driver {
pub child: ChildType,
pub stdin: StdinType,
pub stdout: StdoutType,
pub watchdog_handle: Option<JoinHandleType>,
}
#[derive(Debug, Clone)]
pub struct Config {
pub program: String,
pub args: Vec<String>,
pub timeout: Duration,
pub trace: bool,
}
#[cfg(not(feature = "tokio"))]
pub fn launch(config: &Config) -> Result<Driver, LogicError> {
use std::process::{Command, Stdio};
use std::io::BufReader;
let mut child = Command::new(&config.program)
.args(&config.args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()?;
let stdin = child.stdin.take().ok_or_else(|| {
LogicError::Solver("failed to capture stdin".into())
})?;
let stdout = child.stdout.take().ok_or_else(|| {
LogicError::Solver("failed to capture stdout".into())
})?;
let stdout = BufReader::new(stdout);
let pid = child.id();
let timeout = config.timeout;
let watchdog_handle = std::thread::spawn(move || {
std::thread::sleep(timeout);
drop(kill_tree::kill_tree(pid));
});
Ok(Driver {
child,
stdin,
stdout,
watchdog_handle: Some(watchdog_handle),
})
}
#[cfg(feature = "tokio")]
pub fn launch(config: &Config) -> Result<Driver, LogicError> {
use tokio::process::Command;
use tokio::io::BufReader;
let rt = tokio::runtime::Handle::try_current()
.map_err(|_| LogicError::Solver("no tokio runtime".into()))?;
let mut child = rt.block_on(async {
Command::new(&config.program)
.args(&config.args)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::null())
.spawn()
})?;
let stdin = child.stdin.take().ok_or_else(|| {
LogicError::Solver("failed to capture stdin".into())
})?;
let stdout = child.stdout.take().ok_or_else(|| {
LogicError::Solver("failed to capture stdout".into())
})?;
let stdout = BufReader::new(stdout);
let pid = child.id().unwrap_or(0);
let timeout = config.timeout;
let watchdog_handle = rt.spawn(async move {
tokio::time::sleep(timeout).await;
drop(kill_tree::kill_tree(pid));
});
Ok(Driver {
child,
stdin,
stdout,
watchdog_handle: Some(watchdog_handle),
})
}