perf: avoid holding async mutex guards across await points

tokio recommends using a sync mutex with a notifier instead of the
async mutex where possible. Rust forbids holding a sync mutex guard
across await points so we can prevent a potential deadlock this way.

This adds a custom channel based on the tokio mpmc example for
handling gvfs events from callbacks to avoid the async mutex
requirement. Messages are held in a `VecDeque` behind a sync mutex
and the receiver will get notified via the notifier when a message
is added to the queue.

Weak references used in gio callbacks in case the sender is dropped
by the application.
This commit is contained in:
Michael Aaron Murphy 2026-04-14 16:38:56 +02:00
parent 971374f60b
commit e35d5123f0
No known key found for this signature in database
GPG key ID: B2732D4240C9212C
5 changed files with 139 additions and 39 deletions

View file

@ -6955,9 +6955,8 @@ impl Tab {
.await
.unwrap();
let output = Arc::new(tokio::sync::Mutex::new(output));
let (watch_tx, mut watch_rx) = tokio::sync::watch::channel(true);
{
let output = output.clone();
tokio::task::spawn_blocking(move || {
scan_search(
&search_location,
@ -6985,14 +6984,7 @@ impl Tab {
true
} else {
// Wake up update method
futures::executor::block_on(async {
output
.lock()
.await
.send(Message::SearchReady(false))
.await
})
.is_ok()
watch_tx.send(false).is_ok()
}
}
Err(_) => false,
@ -7005,13 +6997,16 @@ impl Tab {
search_location,
start.elapsed(),
);
})
.await
.unwrap();
});
}
while watch_rx.changed().await.is_ok() {
let is_ready = *watch_rx.borrow_and_update();
let _ = output.send(Message::SearchReady(is_ready)).await;
}
// Send final ready
let _ = output.lock().await.send(Message::SearchReady(true)).await;
let _ = output.send(Message::SearchReady(true)).await;
std::future::pending().await
},