Skip to content

cosmic-applet-status-area: layout & interaction freeze #1245

@Choruptian

Description

@Choruptian

I'm not sure what the best way of reproducing this issue is but I found that by running arch-update (https://github.com/Antiz96/arch-update) you get a tray (status notifier item) application that when interacted with will ultimately freeze interaction (visuals and clicks) for every tray application.

I diagnosed the issue down to (w/ dmesg-monitor and way too many debug messages to include here):

https://github.com/pop-os/cosmic-applets/blame/2852f3cc16a3e20d018a7e4ddf31111be81fc56e/cosmic-applet-status-area/src/subscriptions/status_notifier_item.rs#L88

and had my best friend (ChatGPT) write something that fixed it, not sure if the optimal solution would look something like this but here's the rationale too:

                // Some StatusNotifierItem menus emit LayoutUpdated signals very frequently (e.g. a
                // timer updating every second). If we await GetLayout for each signal in the same
                // stream combinator, we stop polling the signal stream while GetLayout is in-flight.
                // That can allow the D-Bus receive buffer to fill up with signals, delaying (or
                // deadlocking) the GetLayout reply and causing the menu UI to appear "stuck".
                //
                // Drain LayoutUpdated signals continuously on a side task and coalesce bursts into a
                // single pending refresh.
                let (update_tx, update_rx) = tokio::sync::mpsc::channel::<()>(1);
                tokio::spawn(async move {
                    while layout_updated_stream.next().await.is_some() {
                        match update_tx.try_send(()) {
                            Ok(()) | Err(TrySendError::Full(())) => {}
                            Err(TrySendError::Closed(())) => return,
                        }
                    }
                });

                let updates = futures::stream::unfold(
                    (menu_proxy, update_rx),
                    |(menu_proxy, mut update_rx)| async move {
                        update_rx.recv().await?;
                        Some((get_layout(menu_proxy.clone()).await, (menu_proxy, update_rx)))
                    },
                );

I can attempt an upstream of this if desired but that's (^) the crux of it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions