fix: make all subscriptions resistant to being restarted

many of the errors we've been seeing the last few days are because of subscriptions which are restarting
This commit is contained in:
Ashley Wulber 2024-01-18 21:02:35 -05:00 committed by Ashley Wulber
parent ebe688c747
commit d4e0dd8fb8
12 changed files with 184 additions and 138 deletions

View file

@ -16,6 +16,7 @@ once_cell = "1.9"
futures = "0.3.21"
xdg = "2.4.0"
anyhow = "1.0"
tokio = "1.35"
# Application i18n
i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-requester"] }
i18n-embed-fl = "0.6.4"

View file

@ -222,7 +222,7 @@ impl cosmic::Application for IcedWorkspacesApplet {
fn subscription(&self) -> Subscription<Message> {
Subscription::batch(vec![
workspaces(0).map(Message::WorkspaceUpdate),
workspaces().map(Message::WorkspaceUpdate),
event::listen_with(|e, _| match e {
Mouse(mouse::Event::WheelScrolled { delta }) => Some(Message::WheelScrolled(delta)),
_ => None,

View file

@ -5,7 +5,11 @@ use cosmic::iced::{
futures::{channel::mpsc, SinkExt, StreamExt},
subscription,
};
use std::hash::Hash;
use once_cell::sync::Lazy;
use tokio::sync::Mutex;
pub static WAYLAND_RX: Lazy<Mutex<Option<mpsc::Receiver<WorkspaceList>>>> =
Lazy::new(|| Mutex::new(None));
#[derive(Debug, Clone)]
pub enum WorkspacesUpdate {
@ -14,16 +18,18 @@ pub enum WorkspacesUpdate {
Errored,
}
pub fn workspaces<I: 'static + Hash + Copy + Send + Sync>(
id: I,
) -> iced::Subscription<WorkspacesUpdate> {
subscription::channel(id, 50, move |mut output| async move {
let mut state = State::Ready;
pub fn workspaces() -> iced::Subscription<WorkspacesUpdate> {
subscription::channel(
std::any::TypeId::of::<WorkspacesUpdate>(),
50,
move |mut output| async move {
let mut state = State::Waiting;
loop {
state = start_listening(state, &mut output).await;
}
})
loop {
state = start_listening(state, &mut output).await;
}
},
)
}
async fn start_listening(
@ -31,22 +37,23 @@ async fn start_listening(
output: &mut futures::channel::mpsc::Sender<WorkspacesUpdate>,
) -> State {
match state {
State::Ready => {
if let Ok(watcher) = WorkspacesWatcher::new() {
_ = output
.send(WorkspacesUpdate::Started(watcher.get_sender()))
.await;
State::Waiting(watcher)
} else {
_ = output.send(WorkspacesUpdate::Errored).await;
State::Error
}
}
State::Waiting(mut t) => {
if let Some(w) = t.workspaces().await {
State::Waiting => {
let mut guard = WAYLAND_RX.lock().await;
let rx = {
if guard.is_none() {
if let Ok(WorkspacesWatcher { rx, tx }) = WorkspacesWatcher::new() {
*guard = Some(rx);
_ = output.send(WorkspacesUpdate::Started(tx)).await;
} else {
_ = output.send(WorkspacesUpdate::Errored).await;
return State::Error;
}
}
guard.as_mut().unwrap()
};
if let Some(w) = rx.next().await {
_ = output.send(WorkspacesUpdate::Workspaces(w)).await;
State::Waiting(t)
State::Waiting
} else {
_ = output.send(WorkspacesUpdate::Errored).await;
State::Error
@ -57,8 +64,7 @@ async fn start_listening(
}
pub enum State {
Ready,
Waiting(WorkspacesWatcher),
Waiting,
Error,
}