fix(bluetooth): cpu usage

This commit is contained in:
Ashley Wulber 2025-05-06 19:15:28 -04:00 committed by Michael Murphy
parent 9eb4053d31
commit b6bb982f2d
2 changed files with 58 additions and 13 deletions

View file

@ -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();

View file

@ -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());