From 4b915e4a5a14674a133f696ee31c54396cf9c3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Papp?= Date: Tue, 28 Jul 2026 16:25:52 +0200 Subject: [PATCH] [client] Hold menuMu across tray item setters --- client/ui/tray.go | 9 ++++----- client/ui/tray_session.go | 14 ++++++-------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/client/ui/tray.go b/client/ui/tray.go index 08741911f..7a1f74770 100644 --- a/client/ui/tray.go +++ b/client/ui/tray.go @@ -67,8 +67,8 @@ type Tray struct { loc *Localizer // menu and the *Item/*Submenu fields below are reassigned by buildMenu - // on every relayout (which destroys the replaced tree) — touch them only - // with menuMu held; snapshot under the lock, then call the item. + // on every relayout, which destroys the replaced tree — locate items and + // call them with menuMu held, so a relayout can't destroy one mid-call. menu *application.Menu statusItem *application.MenuItem // sessionExpiresItem shows the SSO deadline as a remaining-time label, @@ -507,9 +507,8 @@ func (t *Tray) handleConnect() { func (t *Tray) setItemEnabled(get func() *application.MenuItem, enabled bool) { t.menuMu.Lock() - item := get() - t.menuMu.Unlock() - if item != nil { + defer t.menuMu.Unlock() + if item := get(); item != nil { item.SetEnabled(enabled) } } diff --git a/client/ui/tray_session.go b/client/ui/tray_session.go index 885fdb348..45caf2a37 100644 --- a/client/ui/tray_session.go +++ b/client/ui/tray_session.go @@ -104,21 +104,19 @@ func sessionRefreshInterval(remaining time.Duration) time.Duration { } // refreshSessionExpiresLabel updates only the countdown label, no relayout, to avoid disturbing an open menu. -// The item is snapshotted under menuMu since buildMenu reassigns it on every relayout. +// menuMu is held across the SetLabel so a relayout can't destroy the item mid-call. func (t *Tray) refreshSessionExpiresLabel() { - t.menuMu.Lock() - item := t.sessionExpiresItem - t.menuMu.Unlock() - if item == nil { - return - } t.sessionMu.Lock() deadline := t.sessionExpiresAt t.sessionMu.Unlock() if deadline.IsZero() { return } - item.SetLabel(t.sessionRowLabel(deadline)) + t.menuMu.Lock() + defer t.menuMu.Unlock() + if t.sessionExpiresItem != nil { + t.sessionExpiresItem.SetLabel(t.sessionRowLabel(deadline)) + } } func (t *Tray) sessionRowLabel(deadline time.Time) string {