From 2910482b03b8f8c691b4859273149bc1a4b69941 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun, 19 Dec 2021 16:17:09 +0100 Subject: [PATCH] improve everything --- config.toml | 12 ++++- src/controller/child.rs | 109 ++++++++++++++++++++++++++-------------- src/controller/mod.rs | 20 +++++--- src/model.rs | 2 +- src/view.rs | 2 +- 5 files changed, 98 insertions(+), 47 deletions(-) diff --git a/config.toml b/config.toml index 25da140..3b9d416 100644 --- a/config.toml +++ b/config.toml @@ -10,7 +10,7 @@ env = { HELLO = "uwu hi ヾ(•ω•`)o" } [pwd] command = "pwd" -workdir = "./src" +workdir = "./src/controller" [crash] command = "cat fkasdjfölashjdflksd" @@ -32,4 +32,14 @@ while true; do echo $counter counter=$((counter+1)) done +""" + +[many-lines] +command = """ +counter=0 + +while [ $counter -lt 100 ]; do + echo $counter + counter=$((counter+1)) +done """ \ No newline at end of file diff --git a/src/controller/child.rs b/src/controller/child.rs index 9bbb500..e645a68 100644 --- a/src/controller/child.rs +++ b/src/controller/child.rs @@ -1,21 +1,25 @@ use crate::controller::{StdioSendBuf, STDIO_SEND_BUF_SIZE}; use crate::model::{ServiceStatus, SmError, SmResult}; use std::io::{Read, Write}; -use std::process::{Child, ChildStderr}; +use std::process::{Child, ChildStderr, ChildStdout}; use std::sync::mpsc::TryRecvError; use std::sync::{mpsc, Arc, Mutex}; use std::{io, thread}; -use tracing::error; +use tracing::{error, info}; + +enum ChildAction { + Ignore, + Kill, +} pub fn child_process_thread( - child: Child, - stdout_send: mpsc::Sender, + mut child: Child, + mut stdout_send: mpsc::Sender, service_status: Arc>, service_name: String, terminate_channel: mpsc::Receiver<()>, ) -> SmResult { - let mut child = child; - let mut stdout = child + let stdout = child .stdout .take() .ok_or(SmError::Bug("Stdout of child could not be taken"))?; @@ -37,12 +41,52 @@ pub fn child_process_thread( error!(error = %err, "Failed to spawn stderr thread"); } - let result = loop { + info!(name = %service_name, "Entering main processing loop"); + let (action, result) = child_process_main_thread( + &mut stdout_send, + terminate_channel, + stderr_terminate_send, + stdout, + &mut child, + service_status.clone(), + ); + + if let ChildAction::Kill = action { + match child.kill() { + Ok(()) => { + *service_status.lock().map_err(|_| SmError::MutexPoisoned)? = ServiceStatus::Killed + } + Err(e) if e.kind() == io::ErrorKind::InvalidInput => {} + Err(e) => return Err(e.into()), + } + + let mut send_message_buf = [0; STDIO_SEND_BUF_SIZE]; + let kill_msg = "\n\n\n"; + send_message_buf + .as_mut_slice() + .write_all(kill_msg.as_bytes())?; + stdout_send + .send((send_message_buf, kill_msg.len())) + .map_err(|_| SmError::Bug("Failed to send stdout to main thread"))?; + } + + result +} + +fn child_process_main_thread( + stdout_send: &mut mpsc::Sender, + terminate_channel: mpsc::Receiver<()>, + stderr_terminate_send: mpsc::Sender<()>, + mut stdout: ChildStdout, + child: &mut Child, + service_status: Arc>, +) -> (ChildAction, SmResult) { + loop { match terminate_channel.try_recv() { Ok(_) | Err(TryRecvError::Disconnected) => { // terminating the thread is a best-effort, it doesn't matter if it died let _ = stderr_terminate_send.send(()); - break Ok(()); + return (ChildAction::Kill, Ok(())); } Err(TryRecvError::Empty) => {} } @@ -50,19 +94,27 @@ pub fn child_process_thread( let mut stdout_buf = [0; STDIO_SEND_BUF_SIZE]; match stdout.read(&mut stdout_buf) { Ok(0) => {} - Ok(n) => { - stdout_send - .send((stdout_buf, n)) - .map_err(|_| SmError::Bug("Failed to send stdout to main thread"))?; - } - Err(e) if e.kind() == io::ErrorKind::Interrupted => {} - Err(e) => break Err(e.into()), + Ok(n) => match stdout_send.send((stdout_buf, n)) { + Ok(()) => {} + Err(_) => { + return ( + ChildAction::Kill, + Err(SmError::Bug("Failed to send stdout to main thread")), + ) + } + }, + Err(err) if err.kind() == io::ErrorKind::Interrupted => {} + // todo: maybe we are overreacting here? + Err(err) => return (ChildAction::Kill, Err(err.into())), }; match child.try_wait() { Ok(None) => {} Ok(Some(status)) => { - let mut status_lock = service_status.lock().map_err(|_| SmError::MutexPoisoned)?; + let mut status_lock = match service_status.lock() { + Ok(lock) => lock, + Err(_) => return (ChildAction::Kill, Err(SmError::MutexPoisoned)), + }; *status_lock = match status.code() { Some(0) => ServiceStatus::Exited, @@ -70,30 +122,11 @@ pub fn child_process_thread( None => ServiceStatus::Killed, }; - return Ok(()); + return (ChildAction::Ignore, Ok(())); } - Err(e) => break Err(e.into()), + Err(err) => return (ChildAction::Kill, Err(err.into())), } - }; - - match child.kill() { - Ok(()) => { - *service_status.lock().map_err(|_| SmError::MutexPoisoned)? = ServiceStatus::Killed - } - Err(e) if e.kind() == io::ErrorKind::InvalidInput => {} - Err(e) => return Err(e.into()), } - - let mut send_message_buf = [0; STDIO_SEND_BUF_SIZE]; - let kill_msg = "\n\n\n"; - send_message_buf - .as_mut_slice() - .write_all(kill_msg.as_bytes())?; - stdout_send - .send((send_message_buf, kill_msg.len())) - .map_err(|_| SmError::Bug("Failed to send stdout to main thread"))?; - - result } fn child_process_stderr_thread( @@ -101,6 +134,8 @@ fn child_process_stderr_thread( terminate_channel: mpsc::Receiver<()>, mut stderr: ChildStderr, ) { + info!("Entering main processing loop for stderr thread"); + loop { match terminate_channel.try_recv() { Ok(_) | Err(TryRecvError::Disconnected) => return, diff --git a/src/controller/mod.rs b/src/controller/mod.rs index 699def7..20ea26d 100644 --- a/src/controller/mod.rs +++ b/src/controller/mod.rs @@ -8,7 +8,7 @@ use crossterm::event; use crossterm::event::{Event, KeyCode}; use std::collections::HashMap; use std::io::{ErrorKind, Write}; -use std::process::{ChildStderr, Command, Stdio}; +use std::process::{Command, Stdio}; use std::sync::{mpsc, Arc, Mutex}; use std::time::Duration; use std::{io, thread}; @@ -34,7 +34,9 @@ pub fn run_app(terminal: &mut Terminal, mut app: App) -> SmResult match key.code { KeyCode::Char('q') => match app.selected { Some(_) => app.leave_service(), - None => break, + None => { + break; + } }, KeyCode::Char('r') => app.run_service()?, KeyCode::Char('k') => app.kill_service()?, @@ -49,7 +51,9 @@ pub fn run_app(terminal: &mut Terminal, mut app: App) -> SmResult } // terminate the child processes - for sender in app.thread_terminates.values() { + for (i, sender) in app.thread_terminates.values().enumerate() { + info!(index = i, "Terminating child thread..."); + let _ = sender.send(()); } @@ -151,6 +155,7 @@ impl App { if let Some(index) = index { let status = { let service = &mut self.table.services[index]; + service.std_io_buf.clear(); *service.status.lock()? }; @@ -166,12 +171,13 @@ impl App { let index = self.selected.or_else(|| self.table.table_state.selected()); if let Some(index) = index { - let status = { - let service = &mut self.table.services[index]; - *service.status.lock()? - }; + let service = &mut self.table.services[index]; + + let status = { *service.status.lock()? }; if status == ServiceStatus::Running { + info!(name = %service.name,"Killing service"); + let terminate_sender = &mut self .thread_terminates .get(&index) diff --git a/src/model.rs b/src/model.rs index 9787479..dab4b16 100644 --- a/src/model.rs +++ b/src/model.rs @@ -72,7 +72,7 @@ mod error { MutexPoisoned, FailedToSendStdio, /// This should never happen and would be a panic in most programs, but panicking here - /// might fuck things up badly, so we don't want to + /// is not a good idea Bug(&'static str), } diff --git a/src/view.rs b/src/view.rs index 1dedaea..f031c1a 100644 --- a/src/view.rs +++ b/src/view.rs @@ -56,7 +56,7 @@ fn render_full_view(f: &mut Frame, state: &mut AppState, index: u fn render_table(f: &mut Frame, state: &mut AppState, area: Rect) { let selected_style = Style::default().add_modifier(Modifier::REVERSED); let normal_style = Style::default().bg(Color::Blue); - let header_cells = ["name", "status"] + let header_cells = ["Name", "Status"] .iter() .map(|h| Cell::from(*h).style(Style::default()));