summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephan Dilly <dilly.stephan@gmail.com>2020-06-30 09:33:22 +0200
committerStephan Dilly <dilly.stephan@gmail.com>2020-06-30 09:33:22 +0200
commit923bed9abfe97880aa0c164406e30f9382e9fa88 (patch)
tree06c678c227800eda513c93bb8ef3d45d356b3800
parenta4de7014155a347ca63d926cdc5b0ae6ad54acaa (diff)
use condvar/mutex to not busywait when suspending input polling (closes #153)
-rw-r--r--src/input.rs66
-rw-r--r--src/main.rs1
-rw-r--r--src/notify_mutex.rs42
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(&current_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")
+ }
+}