diff options
author | Stephan Dilly <dilly.stephan@gmail.com> | 2020-06-30 09:33:22 +0200 |
---|---|---|
committer | Stephan Dilly <dilly.stephan@gmail.com> | 2020-06-30 09:33:22 +0200 |
commit | 923bed9abfe97880aa0c164406e30f9382e9fa88 (patch) | |
tree | 06c678c227800eda513c93bb8ef3d45d356b3800 | |
parent | a4de7014155a347ca63d926cdc5b0ae6ad54acaa (diff) |
use condvar/mutex to not busywait when suspending input polling (closes #153)
-rw-r--r-- | src/input.rs | 66 | ||||
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | src/notify_mutex.rs | 42 |
3 files changed, 78 insertions, 31 deletions
diff --git a/src/input.rs b/src/input.rs index 1ad49c27..794bb2c3 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,3 +1,4 @@ +use crate::notify_mutex::NotifyableMutex; use crossbeam_channel::{unbounded, Receiver}; use crossterm::event::{self, Event}; use std::{ @@ -27,7 +28,7 @@ pub enum InputEvent { /// pub struct Input { - desired_state: Arc<AtomicBool>, + desired_state: Arc<NotifyableMutex<bool>>, current_state: Arc<AtomicBool>, receiver: Receiver<InputEvent>, } @@ -37,40 +38,39 @@ impl Input { pub fn new() -> Self { let (tx, rx) = unbounded(); - let desired_state = Arc::new(AtomicBool::new(true)); + let desired_state = Arc::new(NotifyableMutex::new(true)); let current_state = Arc::new(AtomicBool::new(true)); let arc_desired = Arc::clone(&desired_state); let arc_current = Arc::clone(¤t_state); - thread::spawn(move || { - loop { - //TODO: use condvar to not busy wait - if arc_desired.load(Ordering::Relaxed) { - if !arc_current.load(Ordering::Relaxed) { - tx.send(InputEvent::State( - InputState::Polling, - )) - .expect("send failed"); - } - arc_current.store(true, Ordering::Relaxed); - - if let Some(e) = Self::poll(POLL_DURATION) - .expect("failed to pull events.") - { - tx.send(InputEvent::Input(e)) - .expect("send input event failed"); - } - } else { - if arc_current.load(Ordering::Relaxed) { - tx.send(InputEvent::State( - InputState::Paused, - )) - .expect("send failed"); - } - - arc_current.store(false, Ordering::Relaxed); + thread::spawn(move || loop { + if arc_desired.get() { + if !arc_current.load(Ordering::Relaxed) { + log::info!("input polling resumed"); + + tx.send(InputEvent::State(InputState::Polling)) + .expect("send state failed"); + } + arc_current.store(true, Ordering::Relaxed); + + if let Some(e) = Self::poll(POLL_DURATION) + .expect("failed to pull events.") + { + tx.send(InputEvent::Input(e)) + .expect("send input failed"); + } + } else { + if arc_current.load(Ordering::Relaxed) { + log::info!("input polling suspended"); + + tx.send(InputEvent::State(InputState::Paused)) + .expect("send state failed"); } + + arc_current.store(false, Ordering::Relaxed); + + arc_desired.wait(true); } }); @@ -88,12 +88,16 @@ impl Input { /// pub fn set_polling(&mut self, enabled: bool) { - self.desired_state.store(enabled, Ordering::Relaxed); + self.desired_state.set_and_notify(enabled) + } + + fn shall_poll(&self) -> bool { + self.desired_state.get() } /// pub fn is_state_changing(&self) -> bool { - self.desired_state.load(Ordering::Relaxed) + self.shall_poll() != self.current_state.load(Ordering::Relaxed) } diff --git a/src/main.rs b/src/main.rs index 56821df0..a971da89 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ mod cmdbar; mod components; mod input; mod keys; +mod notify_mutex; mod queue; mod spinner; mod strings; diff --git a/src/notify_mutex.rs b/src/notify_mutex.rs new file mode 100644 index 00000000..beff0daa --- /dev/null +++ b/src/notify_mutex.rs @@ -0,0 +1,42 @@ +use std::sync::{Arc, Condvar, Mutex}; + +/// +#[derive(Clone, Debug)] +pub struct NotifyableMutex<T> { + data: Arc<(Mutex<T>, Condvar)>, +} + +impl<T> NotifyableMutex<T> { + /// + pub fn new(start_value: T) -> Self { + Self { + data: Arc::new((Mutex::new(start_value), Condvar::new())), + } + } + + /// + #[allow(clippy::needless_pass_by_value)] + pub fn wait(&self, condition: T) + where + T: PartialEq, + { + let mut data = self.data.0.lock().expect("lock err"); + while *data != condition { + data = self.data.1.wait(data).expect("wait err"); + } + } + + /// + pub fn set_and_notify(&self, value: T) { + *self.data.0.lock().expect("set err") = value; + self.data.1.notify_one(); + } + + /// + pub fn get(&self) -> T + where + T: Copy, + { + *self.data.0.lock().expect("get err") + } +} |