From c49333e48a55482497e2b050aeea39e704b99b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Papp?= Date: Wed, 29 Jul 2026 11:00:22 +0200 Subject: [PATCH] [client] Serialize status replay with live push dispatch --- client/ui/main.go | 10 ++++++---- client/ui/services/daemon_feed.go | 15 ++++++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/client/ui/main.go b/client/ui/main.go index 656acd61f..e053aba5d 100644 --- a/client/ui/main.go +++ b/client/ui/main.go @@ -165,11 +165,13 @@ func main() { // desktops, macOS, and Windows. windowManager.SetRecenterOnShow(recenterOnShowPredicate()) // Replay the latest snapshot into a window on (re)show, so a webview that - // was hidden while pushes flowed never paints stale state. + // was hidden while pushes flowed never paints stale state. ReplayLast keeps + // the feed's status lock across the dispatch, so a racing live push can't + // slip in between and then be overwritten by this older cached snapshot. windowManager.SetShowReplay(func(w application.Window) { - if st := daemonFeed.LastStatus(); st != nil { - w.DispatchWailsEvent(&application.CustomEvent{Name: services.EventStatusSnapshot, Data: *st}) - } + daemonFeed.ReplayLast(func(st services.Status) { + w.DispatchWailsEvent(&application.CustomEvent{Name: services.EventStatusSnapshot, Data: st}) + }) }) app.RegisterService(application.NewService(windowManager)) diff --git a/client/ui/services/daemon_feed.go b/client/ui/services/daemon_feed.go index 082edfc7b..07750709b 100644 --- a/client/ui/services/daemon_feed.go +++ b/client/ui/services/daemon_feed.go @@ -215,7 +215,7 @@ func (s *DaemonFeed) SetWindowDispatcher(fn func(Status)) { } // pushStatus delivers a snapshot to the Go-side subscribers and the frontend, -// and caches it for LastStatus replays. Hidden windows are skipped by the +// and caches it for ReplayLast. Hidden windows are skipped by the // window dispatcher; they catch up via the show replay (WindowManager). func (s *DaemonFeed) pushStatus(st Status) { s.statusSubsMu.Lock() @@ -233,12 +233,17 @@ func (s *DaemonFeed) pushStatus(st Status) { s.emitter.Emit(EventStatusSnapshot, st) } -// LastStatus returns the most recently pushed snapshot, or nil before the -// first push. Windows becoming visible replay it so they never paint stale. -func (s *DaemonFeed) LastStatus() *Status { +// ReplayLast feeds the most recently pushed snapshot to dispatch; no-op before +// the first push. The status lock is held across the dispatch so a concurrent +// pushStatus cannot deliver a newer snapshot in between the read and the +// dispatch — the replayed value is never older than anything already delivered. +func (s *DaemonFeed) ReplayLast(dispatch func(Status)) { s.statusSubsMu.Lock() defer s.statusSubsMu.Unlock() - return s.lastStatus + if s.lastStatus == nil { + return + } + dispatch(*s.lastStatus) } // BeginProfileSwitch arms suppression for a switch from Connected/Connecting,