use std::collections::VecDeque;
use std::fmt;
use std::io::{self, Read, Write};
use std::process::{Child, ChildStdin, ChildStdout};
use std::sync::mpsc::{Receiver, SyncSender};
use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
use std::time::Duration;
use super::process::{CHUNK, Lines};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChildLine {
Line(String),
Ended {
code: Option<i32>,
},
}
pub(crate) type Sink = Box<dyn FnMut(ChildLine) + Send>;
#[derive(Clone)]
pub struct LiveChild {
inner: Arc<Inner>,
}
enum Inner {
Real(Real),
Double(Arc<Double>),
}
struct Real {
id: u32,
stdin: Mutex<Option<ChildStdin>>,
process: Arc<Mutex<Child>>,
attach: Mutex<Option<SyncSender<Attach>>>,
}
pub(crate) struct Attach {
sink: Sink,
process: Arc<Mutex<Child>>,
}
fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
mutex.lock().unwrap_or_else(PoisonError::into_inner)
}
impl LiveChild {
pub(crate) fn running(process: Child, stdin: ChildStdin, attach: SyncSender<Attach>) -> Self {
Self {
inner: Arc::new(Inner::Real(Real {
id: process.id(),
stdin: Mutex::new(Some(stdin)),
process: Arc::new(Mutex::new(process)),
attach: Mutex::new(Some(attach)),
})),
}
}
#[must_use]
pub fn for_tests() -> (Self, TestChild) {
let double = Arc::new(Double::default());
(Self { inner: Arc::new(Inner::Double(Arc::clone(&double))) }, TestChild { double })
}
#[must_use]
pub fn id(&self) -> Option<u32> {
match &*self.inner {
Inner::Real(real) => Some(real.id),
Inner::Double(_) => None,
}
}
pub fn write_line(&self, line: &str) -> io::Result<()> {
match &*self.inner {
Inner::Real(real) => {
let mut stdin = lock(&real.stdin);
let pipe = stdin.as_mut().ok_or_else(closed)?;
let mut bytes = Vec::with_capacity(line.len() + 1);
bytes.extend_from_slice(line.as_bytes());
bytes.push(b'\n');
pipe.write_all(&bytes)?;
pipe.flush()
}
Inner::Double(double) => {
let mut state = lock(&double.state);
if !state.stdin_open || state.code.is_some() {
return Err(closed());
}
state.written.push(line.to_owned());
Ok(())
}
}
}
pub fn close_stdin(&self) {
match &*self.inner {
Inner::Real(real) => drop(lock(&real.stdin).take()),
Inner::Double(double) => lock(&double.state).stdin_open = false,
}
}
pub fn kill(&self) -> io::Result<()> {
match &*self.inner {
Inner::Real(real) => lock(&real.process).kill(),
Inner::Double(double) => {
let mut state = lock(&double.state);
state.killed = true;
state.end(None);
Ok(())
}
}
}
pub fn try_wait(&self) -> io::Result<Option<Option<i32>>> {
match &*self.inner {
Inner::Real(real) => Ok(lock(&real.process).try_wait()?.map(|status| status.code())),
Inner::Double(double) => Ok(lock(&double.state).code),
}
}
pub(crate) fn attach(&self, sink: Sink) {
match &*self.inner {
Inner::Real(real) => {
if let Some(attach) = lock(&real.attach).take() {
let _ = attach.send(Attach { sink, process: Arc::clone(&real.process) });
}
}
Inner::Double(double) => {
let mut state = lock(&double.state);
if state.sink.is_none() {
let mut sink = sink;
for line in state.waiting.drain(..) {
sink(line);
}
state.sink = Some(sink);
}
}
}
}
}
fn closed() -> io::Error {
io::Error::new(io::ErrorKind::BrokenPipe, "the program's standard input is closed")
}
impl Drop for Inner {
fn drop(&mut self) {
if let Self::Double(double) = self {
lock(&double.state).stdin_open = false;
}
}
}
impl fmt::Debug for LiveChild {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &*self.inner {
Inner::Real(real) => f.debug_struct("LiveChild").field("id", &real.id).finish_non_exhaustive(),
Inner::Double(_) => f.write_str("LiveChild(test)"),
}
}
}
impl PartialEq for LiveChild {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.inner, &other.inner)
}
}
impl Eq for LiveChild {}
#[derive(Clone)]
pub struct TestChild {
double: Arc<Double>,
}
#[derive(Default)]
struct Double {
state: Mutex<DoubleState>,
}
struct DoubleState {
written: Vec<String>,
stdin_open: bool,
killed: bool,
code: Option<Option<i32>>,
sink: Option<Sink>,
waiting: Vec<ChildLine>,
}
impl Default for DoubleState {
fn default() -> Self {
Self { written: Vec::new(), stdin_open: true, killed: false, code: None, sink: None, waiting: Vec::new() }
}
}
impl DoubleState {
fn say(&mut self, line: ChildLine) {
match &mut self.sink {
Some(sink) => sink(line),
None => self.waiting.push(line),
}
}
fn end(&mut self, code: Option<i32>) {
if self.code.is_none() {
self.code = Some(code);
self.say(ChildLine::Ended { code });
}
}
}
impl TestChild {
pub fn say(&self, line: impl Into<String>) {
let mut state = lock(&self.double.state);
if state.code.is_none() {
state.say(ChildLine::Line(line.into()));
}
}
pub fn exit(&self, code: Option<i32>) {
lock(&self.double.state).end(code);
}
#[must_use]
pub fn written(&self) -> Vec<String> {
lock(&self.double.state).written.clone()
}
#[must_use]
pub fn stdin_open(&self) -> bool {
lock(&self.double.state).stdin_open
}
#[must_use]
pub fn killed(&self) -> bool {
lock(&self.double.state).killed
}
}
impl fmt::Debug for TestChild {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("TestChild")
}
}
const LONGEST_LOOK: Duration = Duration::from_millis(500);
pub(crate) fn read(mut stdout: ChildStdout, first: &SyncSender<Option<String>>, attach: &Receiver<Attach>) {
let mut lines = Lines::default();
let mut ready = VecDeque::new();
let mut chunk = [0_u8; CHUNK];
let mut open = true;
while open && ready.is_empty() {
open = read_some(&mut stdout, &mut chunk, &mut lines, &mut ready);
}
let first_line = ready.pop_front();
let said = first_line.is_some();
if first.send(first_line).is_err() || !said {
drain(open, &mut stdout, &mut chunk);
return;
}
let Ok(Attach { mut sink, process }) = attach.recv() else {
drain(open, &mut stdout, &mut chunk);
return;
};
loop {
for line in ready.drain(..) {
sink(ChildLine::Line(line));
}
if !open {
break;
}
open = read_some(&mut stdout, &mut chunk, &mut lines, &mut ready);
}
drop(stdout);
sink(ChildLine::Ended { code: wait_for_end(&process) });
}
fn read_some(stdout: &mut ChildStdout, chunk: &mut [u8], lines: &mut Lines, ready: &mut VecDeque<String>) -> bool {
loop {
match stdout.read(chunk) {
Ok(0) => break,
Ok(count) => {
lines.feed(&chunk[..count], &mut |line| ready.push_back(line));
return true;
}
Err(error) if error.kind() == io::ErrorKind::Interrupted => {}
Err(_) => break,
}
}
lines.finish(&mut |line| ready.push_back(line));
false
}
fn drain(open: bool, stdout: &mut ChildStdout, chunk: &mut [u8]) {
if open {
while !matches!(stdout.read(chunk), Ok(0) | Err(_)) {}
}
}
fn wait_for_end(process: &Mutex<Child>) -> Option<i32> {
let mut pause = Duration::from_millis(5);
loop {
match lock(process).try_wait() {
Ok(Some(status)) => return status.code(),
Ok(None) => {}
Err(_) => return None,
}
std::thread::sleep(pause);
pause = (pause * 2).min(LONGEST_LOOK);
}
}