improve perf

This commit is contained in:
nora 2021-12-18 00:25:51 +01:00
parent 8de36860af
commit 817a14c3bc
4 changed files with 84 additions and 49 deletions

View file

@ -19,4 +19,17 @@ command = "cat fkasdjfölashjdflksd"
command = "touch uwu.txt" command = "touch uwu.txt"
[side-effect-remove] [side-effect-remove]
command = "rm uwu.txt" command = "rm uwu.txt"
[compile]
command = "cargo check"
[loop]
command = """
counter=0
while true; do
echo $counter
counter=$((counter+1))
done
"""

View file

@ -1,5 +1,5 @@
use crate::model::config::Config; use crate::model::config::Config;
use crate::model::{AppState, Service, ServiceStatus, SmError, SmResult}; use crate::model::{AppState, Service, ServiceStatus, SmError, SmResult, StdIoStream};
use crate::{view, App}; use crate::{view, App};
use crossterm::event; use crossterm::event;
use crossterm::event::{Event, KeyCode}; use crossterm::event::{Event, KeyCode};
@ -9,13 +9,14 @@ use std::io::{ErrorKind, Read, Write};
use std::process::{Child, Command, Stdio}; use std::process::{Child, Command, Stdio};
use std::sync::mpsc::TryRecvError; use std::sync::mpsc::TryRecvError;
use std::sync::{mpsc, Mutex}; use std::sync::{mpsc, Mutex};
use std::time::Duration;
use tui::backend::Backend; use tui::backend::Backend;
use tui::widgets::TableState; use tui::widgets::TableState;
use tui::Terminal; use tui::Terminal;
const STDOUT_SEND_BUF_SIZE: usize = 512; const STDIO_SEND_BUF_SIZE: usize = 512;
pub type StdoutSendBuf = ([u8; STDOUT_SEND_BUF_SIZE], usize); pub type StdioSendBuf = ([u8; STDIO_SEND_BUF_SIZE], usize);
pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> SmResult { pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> SmResult {
loop { loop {
@ -23,18 +24,20 @@ pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> SmResult
app.recv_stdouts(); app.recv_stdouts();
if let Event::Key(key) = event::read()? { if event::poll(Duration::from_millis(10))? {
match key.code { if let Event::Key(key) = event::read()? {
KeyCode::Char('q') => match app.selected { match key.code {
Some(_) => app.leave_service(), KeyCode::Char('q') => match app.selected {
None => break, Some(_) => app.leave_service(),
}, None => break,
KeyCode::Char('r') => app.run_service()?, },
KeyCode::Down => app.next(), KeyCode::Char('r') => app.run_service()?,
KeyCode::Up => app.previous(), KeyCode::Down => app.next(),
KeyCode::Enter => app.select_service(), KeyCode::Up => app.previous(),
KeyCode::Esc => app.leave_service(), KeyCode::Enter => app.select_service(),
_ => {} KeyCode::Esc => app.leave_service(),
_ => {}
}
} }
} }
} }
@ -66,9 +69,11 @@ impl App {
.or_else(|_| std::env::current_dir())?, .or_else(|_| std::env::current_dir())?,
env: service.env.unwrap_or_else(HashMap::new), env: service.env.unwrap_or_else(HashMap::new),
status: Mutex::new(ServiceStatus::NotStarted), status: Mutex::new(ServiceStatus::NotStarted),
stdout_buf: Vec::new(), std_io_buf: Vec::new(),
stdout_recv, stdout: StdIoStream {
stdout_send: Mutex::new(Some(stdout_send)), recv: stdout_recv,
send: stdout_send,
},
}) })
}) })
.collect::<io::Result<_>>()?, .collect::<io::Result<_>>()?,
@ -84,8 +89,12 @@ impl App {
fn recv_stdouts(&mut self) { fn recv_stdouts(&mut self) {
for service in self.table.services.iter_mut() { for service in self.table.services.iter_mut() {
while let Ok((buf, n)) = service.stdout_recv.try_recv() { while let Ok((buf, n)) = service.stdout.recv.try_recv() {
service.stdout_buf.extend_from_slice(&buf[0..n]); service.std_io_buf.extend(&buf[0..n]);
if service.std_io_buf.len() > 2_000_000 {
service.std_io_buf.clear(); // todo don't
}
} }
} }
} }
@ -131,13 +140,20 @@ impl App {
} }
fn run_service(&mut self) -> SmResult { fn run_service(&mut self) -> SmResult {
if let Some(selected) = self.selected { let index = self.selected.or_else(|| self.table.table_state.selected());
self.start_service(selected)
} else if let Some(selected) = self.table.table_state.selected() { if let Some(index) = index {
self.start_service(selected) let status = {
} else { let service = &mut self.table.services[index];
Ok(()) *service.status.lock()?
};
if status != ServiceStatus::Running {
self.start_service(index)?;
}
} }
Ok(())
} }
fn start_service(&mut self, service: usize) -> SmResult { fn start_service(&mut self, service: usize) -> SmResult {
@ -145,12 +161,6 @@ impl App {
*service.status.lock()? = ServiceStatus::Running; *service.status.lock()? = ServiceStatus::Running;
let stdout_send = service
.stdout_send
.lock()?
.take()
.ok_or(SmError::StdioStolen)?;
let mut cmd = Command::new("sh"); let mut cmd = Command::new("sh");
cmd.args(["-c", &service.command]); cmd.args(["-c", &service.command]);
@ -160,9 +170,11 @@ impl App {
cmd.stderr(Stdio::piped()); cmd.stderr(Stdio::piped());
cmd.stdin(Stdio::piped()); cmd.stdin(Stdio::piped());
let stdout_send = service.stdout.send.clone();
let child = match cmd.spawn() { let child = match cmd.spawn() {
Err(err) => { Err(err) => {
let mut buf = [0; STDOUT_SEND_BUF_SIZE]; let mut buf = [0; STDIO_SEND_BUF_SIZE];
let bytes = err.to_string(); let bytes = err.to_string();
@ -177,13 +189,15 @@ impl App {
Ok(child) => child, Ok(child) => child,
}; };
let (tx, rx) = mpsc::channel(); let (terminate_send, terminate_recv) = mpsc::channel();
self.thread_terminates.push(tx); self.thread_terminates.push(terminate_send);
std::thread::spawn(move || match child_process_thread(child, stdout_send, rx) { std::thread::spawn(move || {
Ok(_) => {} match child_process_thread(child, stdout_send, terminate_recv) {
Err(e) => std::fs::write("error.txt", e.to_string()).unwrap(), Ok(_) => {}
Err(e) => std::fs::write("error.txt", e.to_string()).unwrap(),
}
}); });
Ok(()) Ok(())
@ -191,7 +205,7 @@ impl App {
} }
fn child_process_thread( fn child_process_thread(
child: Child, child: Child,
stdout_send: mpsc::Sender<StdoutSendBuf>, stdout_send: mpsc::Sender<StdioSendBuf>,
terminate_channel: mpsc::Receiver<()>, terminate_channel: mpsc::Receiver<()>,
) -> io::Result<()> { ) -> io::Result<()> {
let mut child = child; let mut child = child;
@ -203,7 +217,7 @@ fn child_process_thread(
Err(TryRecvError::Empty) => {} Err(TryRecvError::Empty) => {}
} }
let mut stdout_buf = [0; STDOUT_SEND_BUF_SIZE]; let mut stdout_buf = [0; STDIO_SEND_BUF_SIZE];
match stdout.read(&mut stdout_buf) { match stdout.read(&mut stdout_buf) {
Ok(0) => continue, Ok(0) => continue,

View file

@ -1,4 +1,4 @@
use crate::controller::StdoutSendBuf; use crate::controller::StdioSendBuf;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{mpsc, Mutex}; use std::sync::{mpsc, Mutex};
@ -26,12 +26,17 @@ pub struct Service {
pub workdir: PathBuf, pub workdir: PathBuf,
pub env: HashMap<String, String>, pub env: HashMap<String, String>,
pub status: Mutex<ServiceStatus>, pub status: Mutex<ServiceStatus>,
pub stdout_buf: Vec<u8>, pub std_io_buf: Vec<u8>,
pub stdout_recv: mpsc::Receiver<StdoutSendBuf>, pub stdout: StdIoStream,
pub stdout_send: Mutex<Option<mpsc::Sender<StdoutSendBuf>>>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct StdIoStream {
pub recv: mpsc::Receiver<StdioSendBuf>,
pub send: mpsc::Sender<StdioSendBuf>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(dead_code)] #[allow(dead_code)]
pub enum ServiceStatus { pub enum ServiceStatus {
NotStarted, NotStarted,
@ -65,7 +70,6 @@ mod error {
Io(io::Error), Io(io::Error),
FailedToStartChild(io::Error), FailedToStartChild(io::Error),
MutexPoisoned, MutexPoisoned,
StdioStolen,
FailedToSendStdio, FailedToSendStdio,
} }
@ -86,7 +90,6 @@ mod error {
match self { match self {
Self::Io(e) => Display::fmt(e, f), Self::Io(e) => Display::fmt(e, f),
SmError::MutexPoisoned => f.write_str("Mutex was poisoned. This is a bug."), SmError::MutexPoisoned => f.write_str("Mutex was poisoned. This is a bug."),
SmError::StdioStolen => f.write_str("Stdio was stolen. This is a bug."),
SmError::FailedToStartChild(e) => write!(f, "Failed to start child process: {}", e), SmError::FailedToStartChild(e) => write!(f, "Failed to start child process: {}", e),
SmError::FailedToSendStdio => { SmError::FailedToSendStdio => {
f.write_str("Failed to send stdio to display thread. This is a bug.") f.write_str("Failed to send stdio to display thread. This is a bug.")

View file

@ -41,9 +41,14 @@ fn render_full_view<B: Backend>(f: &mut Frame<B>, state: &mut AppState, index: u
.borders(Borders::ALL) .borders(Borders::ALL)
.title("service".as_ref()); .title("service".as_ref());
let stdout = String::from_utf8_lossy(&service.stdout_buf); let len = service.std_io_buf.len();
let stdout = if len > 10000 {
String::from_utf8_lossy(&service.std_io_buf[len - 10000..len])
} else {
String::from_utf8_lossy(&service.std_io_buf)
};
let paragraph = Paragraph::new(stdout.as_ref()).block(block); let paragraph = Paragraph::new(stdout.as_ref()).block(block.clone());
f.render_widget(paragraph, area) f.render_widget(paragraph, area)
} }