Clayton Craft 9885ded858 testing/cosmic-applet: add patch to fix bluetooth high cpu usage
This patch was merged upstream but isn't in a release yet. Without
this, the panel keeps the CPU very busy when bluetooth is disabled.
https://github.com/pop-os/cosmic-applets/pull/933
2025-05-16 17:27:42 +00:00

200 lines
8.7 KiB
Diff

From 400418f52f1bc03cb7513b361a6c753cec55ed2c Mon Sep 17 00:00:00 2001
From: Ashley Wulber <ashley@system76.com>
Date: Tue, 6 May 2025 19:15:28 -0400
Subject: [PATCH] fix(bluetooth): cpu usage
---
cosmic-applet-bluetooth/src/app.rs | 16 ++++++-
cosmic-applet-bluetooth/src/bluetooth.rs | 55 +++++++++++++++++++-----
2 files changed, 58 insertions(+), 13 deletions(-)
diff --git a/cosmic-applet-bluetooth/src/app.rs b/cosmic-applet-bluetooth/src/app.rs
index eb03ddb7..a3798edd 100644
--- a/cosmic-applet-bluetooth/src/app.rs
+++ b/cosmic-applet-bluetooth/src/app.rs
@@ -1,7 +1,7 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
-use crate::bluetooth::{BluerDeviceStatus, BluerRequest, BluerState, DeviceProperty};
+use crate::bluetooth::{set_tick, BluerDeviceStatus, BluerRequest, BluerState, DeviceProperty};
use cosmic::{
app,
applet::token::subscription::{activation_token_subscription, TokenRequest, TokenUpdate},
@@ -24,6 +24,7 @@ use cosmic::{
Element, Task,
};
use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
+use futures::FutureExt;
use std::{collections::HashMap, time::Duration};
use tokio::sync::mpsc::Sender;
@@ -112,7 +113,13 @@ impl cosmic::Application for CosmicBluetoothApplet {
match message {
Message::TogglePopup => {
if let Some(p) = self.popup.take() {
- return destroy_popup(p);
+ return Task::batch(vec![
+ destroy_popup(p),
+ cosmic::task::future(
+ set_tick(Duration::from_secs(10))
+ .map(|_| cosmic::Action::App(Message::Ignore)),
+ ),
+ ]);
} else {
// TODO request update of state maybe
let new_id = window::Id::unique();
@@ -138,6 +145,8 @@ impl cosmic::Application for CosmicBluetoothApplet {
|_| cosmic::action::app(Message::Ignore),
),
get_popup(popup_settings),
+ cosmic::task::future(set_tick(Duration::from_secs(3)))
+ .map(|_: ()| cosmic::Action::App(Message::Ignore)),
]);
}
}
@@ -286,6 +295,9 @@ impl cosmic::Application for CosmicBluetoothApplet {
if Some(id) == self.popup {
self.popup = None;
}
+ return cosmic::task::future(
+ set_tick(Duration::from_secs(10)).map(|_| cosmic::Action::App(Message::Ignore)),
+ );
}
Message::OpenSettings => {
let exec = "cosmic-settings bluetooth".to_string();
diff --git a/cosmic-applet-bluetooth/src/bluetooth.rs b/cosmic-applet-bluetooth/src/bluetooth.rs
index 762d0919..549f654b 100644
--- a/cosmic-applet-bluetooth/src/bluetooth.rs
+++ b/cosmic-applet-bluetooth/src/bluetooth.rs
@@ -1,7 +1,14 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
-use std::{collections::HashMap, fmt::Debug, hash::Hash, mem, sync::Arc, time::Duration};
+use std::{
+ collections::HashMap,
+ fmt::Debug,
+ hash::Hash,
+ mem,
+ sync::{Arc, LazyLock},
+ time::Duration,
+};
pub use bluer::DeviceProperty;
use bluer::{
@@ -24,11 +31,26 @@ use tokio::{
spawn,
sync::{
mpsc::{channel, Receiver, Sender},
- Mutex,
+ Mutex, RwLock,
},
task::JoinHandle,
};
+static TICK: LazyLock<RwLock<Duration>> = LazyLock::new(|| RwLock::new(Duration::from_secs(10)));
+
+pub async fn set_tick(duration: Duration) {
+ let mut guard = TICK.write().await;
+ *guard = duration;
+}
+
+pub async fn tick(interval: &mut tokio::time::Interval) {
+ let guard = TICK.read().await;
+ if *guard != interval.period() {
+ *interval = tokio::time::interval(*guard);
+ interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
+ }
+ interval.tick().await;
+}
// Copied from https://github.com/bluez/bluez/blob/39467578207889fd015775cbe81a3db9dd26abea/src/dbus-common.c#L53
#[inline]
fn device_type_to_icon(device_type: &str) -> &'static str {
@@ -68,8 +90,10 @@ pub fn bluetooth_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
}
retry_count = retry_count.saturating_add(1);
- _ = tokio::time::sleep(Duration::from_millis(2_u64.saturating_pow(retry_count)))
- .await;
+ _ = tokio::time::sleep(Duration::from_millis(
+ 2_u64.saturating_pow(retry_count).max(68719476734),
+ ))
+ .await;
};
let state = bluer_state(&session_state.adapter).await;
@@ -116,8 +140,8 @@ pub fn bluetooth_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
_ = output.send(message).await;
};
- let mut interval = tokio::time::interval(Duration::from_secs(1));
-
+ let mut interval = tokio::time::interval(Duration::from_secs(10));
+ interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
loop {
let Some(mut session_rx) = session_state.rx.take() else {
break;
@@ -126,8 +150,13 @@ pub fn bluetooth_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
if let Some(event) = session_rx.recv().await {
event_handler(event).await;
// Consume any additional available events.
+ let mut count = 0;
while let Some(event) = session_rx.try_recv().ok() {
event_handler(event).await;
+ count += 1;
+ if count == 100 {
+ break;
+ }
}
} else {
break;
@@ -531,10 +560,11 @@ impl BluerSessionState {
let wake_up_discover_tx = self.wake_up_discover_tx.clone();
let _handle: JoinHandle<anyhow::Result<()>> = spawn(async move {
let mut status = adapter_clone.is_powered().await.unwrap_or_default();
- let mut interval = tokio::time::interval(Duration::from_secs(3));
+ let mut interval = tokio::time::interval(Duration::from_secs(10));
+ interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
let mut devices = Vec::new();
loop {
- interval.tick().await;
+ tick(&mut interval).await;
let new_status = adapter_clone.is_powered().await.unwrap_or_default();
devices = build_device_list(devices, &adapter_clone).await;
if new_status != status {
@@ -569,19 +599,23 @@ impl BluerSessionState {
let _monitor_devices: tokio::task::JoinHandle<Result<(), anyhow::Error>> =
spawn(async move {
let mut devices: Vec<BluerDevice> = Vec::new();
+ let mut interval = tokio::time::interval(Duration::from_secs(1));
+ interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
loop {
+ interval.tick().await;
let wakeup_fut = wake_up.recv();
// Listens for process changes and builds edvice lists.
let listener_fut = async {
let mut new_devices = Vec::new();
- let mut interval = tokio::time::interval(Duration::from_secs(1));
+ let mut interval = tokio::time::interval(Duration::from_secs(10));
+ interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
let mut change_stream =
match adapter_clone.discover_devices_with_changes().await {
Ok(stream) => stream,
Err(_) => {
- interval.tick().await;
+ tick(&mut interval).await;
return;
}
};
@@ -768,7 +802,6 @@ async fn bluer_state(adapter: &Adapter) -> BluerState {
#[inline(never)]
async fn build_device_list(mut devices: Vec<BluerDevice>, adapter: &Adapter) -> Vec<BluerDevice> {
let addrs = adapter.device_addresses().await.unwrap_or_default();
-
devices.clear();
if addrs.len() > devices.capacity() {
devices.reserve(addrs.len() - devices.capacity());