Skip to content

Commit fda9b64

Browse files
committed
fix: high CPU usage when desktop files are accessed
1 parent c01d98d commit fda9b64

File tree

3 files changed

+70
-89
lines changed

3 files changed

+70
-89
lines changed

Cargo.lock

Lines changed: 24 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ i18n-embed = { version = "0.14.1", features = [
3232
i18n-embed-fl = "0.8.0"
3333
rust-embed = "8.4.0"
3434
glob = "0.3.0"
35-
freedesktop-desktop-entry = "0.5.2"
35+
freedesktop-desktop-entry = "0.7.5"
3636
shlex = "1.1.0"
3737
serde = { version = "1.0.134", features = ["derive"] }
3838
ron = "0.8.0"

src/subscriptions/desktop_files.rs

Lines changed: 45 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,59 @@
1-
use cosmic::iced::Subscription;
2-
use futures::stream;
3-
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
4-
use std::{fmt::Debug, hash::Hash};
5-
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
6-
7-
#[derive(Debug)]
8-
pub enum State {
9-
Ready,
10-
Waiting {
11-
watcher: RecommendedWatcher,
12-
rx: UnboundedReceiver<notify::Result<Event>>,
13-
},
14-
Finished,
15-
}
1+
use cosmic::{
2+
iced::{stream, Subscription},
3+
iced_futures::futures::{self, SinkExt},
4+
};
5+
use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
6+
use std::fmt::Debug;
7+
use std::hash::Hash;
8+
use tokio::sync::mpsc;
169

1710
#[derive(Debug, Clone, Copy)]
18-
pub enum DesktopFileEvent {
11+
pub enum Event {
1912
Changed,
2013
}
2114

2215
pub fn desktop_files<I: 'static + Hash + Copy + Send + Sync + Debug>(
2316
id: I,
24-
) -> cosmic::iced::Subscription<(I, DesktopFileEvent)> {
17+
) -> cosmic::iced::Subscription<Event> {
2518
Subscription::run_with_id(
2619
id,
27-
stream::unfold(State::Ready, move |mut state| async move {
28-
let (event, new_state) = start_watching(id, state).await;
29-
state = new_state;
30-
if let Some(event) = event {
31-
return Some((event, state));
32-
} else {
33-
None
34-
}
35-
}),
36-
)
37-
}
38-
39-
async fn start_watching<I: Copy>(id: I, state: State) -> (Option<(I, DesktopFileEvent)>, State) {
40-
match state {
41-
State::Ready => {
42-
let paths = freedesktop_desktop_entry::default_paths();
43-
// TODO log errors
44-
if let Ok((mut watcher, rx)) = async_watcher() {
45-
for path in paths {
20+
stream::channel(50, move |mut output| async move {
21+
let handle = tokio::runtime::Handle::current();
22+
let (tx, mut rx) = mpsc::channel(4);
23+
let mut last_update = std::time::Instant::now();
24+
25+
// Automatically select the best implementation for your platform.
26+
// You can also access each implementation directly e.g. INotifyWatcher.
27+
let watcher = RecommendedWatcher::new(
28+
move |res: Result<notify::Event, notify::Error>| {
29+
if let Ok(event) = res {
30+
match event.kind {
31+
EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) => {
32+
let now = std::time::Instant::now();
33+
if now.duration_since(last_update).as_secs() > 3 {
34+
_ = handle.block_on(tx.send(()));
35+
last_update = now;
36+
}
37+
}
38+
39+
_ => (),
40+
}
41+
}
42+
},
43+
Config::default(),
44+
);
45+
46+
if let Ok(mut watcher) = watcher {
47+
for path in freedesktop_desktop_entry::default_paths() {
4648
let _ = watcher.watch(path.as_ref(), RecursiveMode::Recursive);
4749
}
48-
(
49-
Some((id, DesktopFileEvent::Changed)),
50-
State::Waiting { watcher, rx },
51-
)
52-
} else {
53-
(None, State::Finished)
54-
}
55-
}
56-
State::Waiting { watcher, rx } => {
57-
if let Some(rx) = async_watch(rx).await {
58-
(
59-
Some((id, DesktopFileEvent::Changed)),
60-
State::Waiting { watcher, rx },
61-
)
62-
} else {
63-
(None, State::Finished)
64-
}
65-
}
66-
State::Finished => cosmic::iced::futures::future::pending().await,
67-
}
68-
}
69-
70-
fn async_watcher() -> notify::Result<(RecommendedWatcher, UnboundedReceiver<notify::Result<Event>>)>
71-
{
72-
let (tx, rx) = unbounded_channel();
73-
74-
// Automatically select the best implementation for your platform.
75-
// You can also access each implementation directly e.g. INotifyWatcher.
76-
let watcher = RecommendedWatcher::new(
77-
move |res| {
78-
futures::executor::block_on(async {
79-
let _ = tx.send(res);
80-
})
81-
},
82-
Config::default(),
83-
)?;
84-
85-
Ok((watcher, rx))
86-
}
8750

88-
async fn async_watch(
89-
mut rx: UnboundedReceiver<notify::Result<Event>>,
90-
) -> Option<UnboundedReceiver<notify::Result<Event>>> {
91-
// TODO log errors
92-
if let Some(res) = rx.recv().await {
93-
match res {
94-
Ok(_) => return Some(rx),
95-
Err(_) => return None,
96-
}
97-
}
51+
while rx.recv().await.is_some() {
52+
_ = output.send(Event::Changed).await;
53+
}
54+
}
9855

99-
None
56+
futures::future::pending().await
57+
}),
58+
)
10059
}

0 commit comments

Comments
 (0)