improve everything

This commit is contained in:
nora 2021-12-19 16:17:09 +01:00
parent d69c1be8c8
commit 2910482b03
5 changed files with 98 additions and 47 deletions

View file

@ -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
"""

View file

@ -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<StdioSendBuf>,
mut child: Child,
mut stdout_send: mpsc::Sender<StdioSendBuf>,
service_status: Arc<Mutex<ServiceStatus>>,
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<Process was killed>\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<StdioSendBuf>,
terminate_channel: mpsc::Receiver<()>,
stderr_terminate_send: mpsc::Sender<()>,
mut stdout: ChildStdout,
child: &mut Child,
service_status: Arc<Mutex<ServiceStatus>>,
) -> (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<Process was killed>\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,

View file

@ -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<B: Backend>(terminal: &mut Terminal<B>, 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<B: Backend>(terminal: &mut Terminal<B>, 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)

View file

@ -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),
}

View file

@ -56,7 +56,7 @@ fn render_full_view<B: Backend>(f: &mut Frame<B>, state: &mut AppState, index: u
fn render_table<B: Backend>(f: &mut Frame<B>, 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()));