Skip to content

Commit 923bed9

Browse files
author
Stephan Dilly
committed
use condvar/mutex to not busywait when suspending input polling (closes #153)
1 parent a4de701 commit 923bed9

File tree

3 files changed

+78
-31
lines changed

3 files changed

+78
-31
lines changed

src/input.rs

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::notify_mutex::NotifyableMutex;
12
use crossbeam_channel::{unbounded, Receiver};
23
use crossterm::event::{self, Event};
34
use std::{
@@ -27,7 +28,7 @@ pub enum InputEvent {
2728

2829
///
2930
pub struct Input {
30-
desired_state: Arc<AtomicBool>,
31+
desired_state: Arc<NotifyableMutex<bool>>,
3132
current_state: Arc<AtomicBool>,
3233
receiver: Receiver<InputEvent>,
3334
}
@@ -37,40 +38,39 @@ impl Input {
3738
pub fn new() -> Self {
3839
let (tx, rx) = unbounded();
3940

40-
let desired_state = Arc::new(AtomicBool::new(true));
41+
let desired_state = Arc::new(NotifyableMutex::new(true));
4142
let current_state = Arc::new(AtomicBool::new(true));
4243

4344
let arc_desired = Arc::clone(&desired_state);
4445
let arc_current = Arc::clone(&current_state);
4546

46-
thread::spawn(move || {
47-
loop {
48-
//TODO: use condvar to not busy wait
49-
if arc_desired.load(Ordering::Relaxed) {
50-
if !arc_current.load(Ordering::Relaxed) {
51-
tx.send(InputEvent::State(
52-
InputState::Polling,
53-
))
54-
.expect("send failed");
55-
}
56-
arc_current.store(true, Ordering::Relaxed);
57-
58-
if let Some(e) = Self::poll(POLL_DURATION)
59-
.expect("failed to pull events.")
60-
{
61-
tx.send(InputEvent::Input(e))
62-
.expect("send input event failed");
63-
}
64-
} else {
65-
if arc_current.load(Ordering::Relaxed) {
66-
tx.send(InputEvent::State(
67-
InputState::Paused,
68-
))
69-
.expect("send failed");
70-
}
71-
72-
arc_current.store(false, Ordering::Relaxed);
47+
thread::spawn(move || loop {
48+
if arc_desired.get() {
49+
if !arc_current.load(Ordering::Relaxed) {
50+
log::info!("input polling resumed");
51+
52+
tx.send(InputEvent::State(InputState::Polling))
53+
.expect("send state failed");
54+
}
55+
arc_current.store(true, Ordering::Relaxed);
56+
57+
if let Some(e) = Self::poll(POLL_DURATION)
58+
.expect("failed to pull events.")
59+
{
60+
tx.send(InputEvent::Input(e))
61+
.expect("send input failed");
62+
}
63+
} else {
64+
if arc_current.load(Ordering::Relaxed) {
65+
log::info!("input polling suspended");
66+
67+
tx.send(InputEvent::State(InputState::Paused))
68+
.expect("send state failed");
7369
}
70+
71+
arc_current.store(false, Ordering::Relaxed);
72+
73+
arc_desired.wait(true);
7474
}
7575
});
7676

@@ -88,12 +88,16 @@ impl Input {
8888

8989
///
9090
pub fn set_polling(&mut self, enabled: bool) {
91-
self.desired_state.store(enabled, Ordering::Relaxed);
91+
self.desired_state.set_and_notify(enabled)
92+
}
93+
94+
fn shall_poll(&self) -> bool {
95+
self.desired_state.get()
9296
}
9397

9498
///
9599
pub fn is_state_changing(&self) -> bool {
96-
self.desired_state.load(Ordering::Relaxed)
100+
self.shall_poll()
97101
!= self.current_state.load(Ordering::Relaxed)
98102
}
99103

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod cmdbar;
1414
mod components;
1515
mod input;
1616
mod keys;
17+
mod notify_mutex;
1718
mod queue;
1819
mod spinner;
1920
mod strings;

src/notify_mutex.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use std::sync::{Arc, Condvar, Mutex};
2+
3+
///
4+
#[derive(Clone, Debug)]
5+
pub struct NotifyableMutex<T> {
6+
data: Arc<(Mutex<T>, Condvar)>,
7+
}
8+
9+
impl<T> NotifyableMutex<T> {
10+
///
11+
pub fn new(start_value: T) -> Self {
12+
Self {
13+
data: Arc::new((Mutex::new(start_value), Condvar::new())),
14+
}
15+
}
16+
17+
///
18+
#[allow(clippy::needless_pass_by_value)]
19+
pub fn wait(&self, condition: T)
20+
where
21+
T: PartialEq,
22+
{
23+
let mut data = self.data.0.lock().expect("lock err");
24+
while *data != condition {
25+
data = self.data.1.wait(data).expect("wait err");
26+
}
27+
}
28+
29+
///
30+
pub fn set_and_notify(&self, value: T) {
31+
*self.data.0.lock().expect("set err") = value;
32+
self.data.1.notify_one();
33+
}
34+
35+
///
36+
pub fn get(&self) -> T
37+
where
38+
T: Copy,
39+
{
40+
*self.data.0.lock().expect("get err")
41+
}
42+
}

0 commit comments

Comments
 (0)