From eefd31ad05cac1d7bd72cf5a87725ca06a86aa99 Mon Sep 17 00:00:00 2001 From: Stephan Dilly Date: Thu, 19 Mar 2020 12:55:38 +0100 Subject: [PATCH] almost a magnitude of perf win by moving input polling into thread --- README.md | 2 +- src/main.rs | 18 +++++++-------- src/poll.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 66 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index c5c9a253..0c18ef8f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Over the last 2 years my go to GUI tool for this was [fork](https://git-fork.com * [x] (un)stage files * [x] inspect diffs * [x] commit -* [ ] input polling in thread +* [x] input polling in thread * [ ] file watcher instead of polling git * [ ] log view diff --git a/src/main.rs b/src/main.rs index 9e0390c9..6449c368 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ mod git_utils; mod poll; mod tui_utils; -use app::App; +use crate::{app::App, poll::QueueEvent}; use crossterm::{ event::{DisableMouseCapture, EnableMouseCapture}, terminal::{ @@ -15,8 +15,7 @@ use crossterm::{ }, ExecutableCommand, Result, }; -use poll::PollResult; -use std::{io, time::Duration}; +use std::io; use tui::{backend::CrosstermBackend, Terminal}; fn main() -> Result<()> { @@ -33,18 +32,17 @@ fn main() -> Result<()> { let mut app = App::new(); + let receiver = poll::start_polling_thread(); + loop { app.update(); terminal.draw(|mut f| app.draw(&mut f))?; - loop { - if let PollResult::Event(e) = - poll::poll(Duration::from_millis(10)) - { - app.event(e); - } else { - break; + let events = receiver.recv().unwrap(); + for e in events { + if let QueueEvent::Event(ev) = e { + app.event(ev); } } diff --git a/src/poll.rs b/src/poll.rs index cf82fa96..cc2c78a4 100644 --- a/src/poll.rs +++ b/src/poll.rs @@ -1,20 +1,69 @@ use crossterm::event::{self, Event}; -use std::time::Duration; +use std::{ + sync::mpsc::{self, Receiver}, + thread::{self, sleep}, + time::{Duration, Instant}, +}; -/// as -pub enum PollResult { - Timeout, +/// +#[derive(Clone)] +pub enum QueueEvent { + Tick, Event(Event), } +static MAX_POLL_DURATION: Duration = Duration::from_secs(2); +static MIN_POLL_DURATION: Duration = Duration::from_millis(5); +static MAX_BATCHING_DURATION: Duration = Duration::from_millis(25); +static TICK_DURATION: Duration = Duration::from_secs(2); + +/// we run 2 threads feeding us with update events. /// -pub fn poll(dur: Duration) -> PollResult { +/// Thread 1: +/// We will +pub fn start_polling_thread() -> Receiver> { + let (tx, rx) = mpsc::channel(); + + let tx1 = tx.clone(); + thread::spawn(move || { + let mut last_send = Instant::now(); + let mut batch = Vec::new(); + + loop { + let timeout = if batch.len() > 0 { + MIN_POLL_DURATION + } else { + MAX_POLL_DURATION + }; + if let Some(e) = poll(timeout) { + batch.push(QueueEvent::Event(e)); + } + + if batch.len() > 0 + && last_send.elapsed() > MAX_BATCHING_DURATION + { + tx1.send(batch).unwrap(); + batch = Vec::new(); + last_send = Instant::now(); + } + } + }); + + thread::spawn(move || loop { + tx.send(vec![QueueEvent::Tick]).unwrap(); + sleep(TICK_DURATION); + }); + + rx +} + +/// +fn poll(dur: Duration) -> Option { if event::poll(dur).unwrap() { // It's guaranteed that read() wont block if `poll` returns `Ok(true)` let event = event::read().unwrap(); - - PollResult::Event(event) + Some(event) } else { - PollResult::Timeout + None } }