From 91496fdb0383d1d2aeec3cfac5d08ffd29eaf3d1 Mon Sep 17 00:00:00 2001 From: wayne Date: Fri, 3 Apr 2026 10:23:15 +1300 Subject: [PATCH 1/3] Fix deadlock in EventIPCTransport.DispatchWailsEvent holding RLock during InvokeSync --- v3/pkg/application/transport_event_ipc.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/v3/pkg/application/transport_event_ipc.go b/v3/pkg/application/transport_event_ipc.go index 4b3309ee6e7..b6f831a87e6 100644 --- a/v3/pkg/application/transport_event_ipc.go +++ b/v3/pkg/application/transport_event_ipc.go @@ -5,10 +5,21 @@ type EventIPCTransport struct { } func (t *EventIPCTransport) DispatchWailsEvent(event *CustomEvent) { - // Snapshot windows under RLock + // Snapshot the window list under the lock, then release before dispatching. + // DispatchWailsEvent calls ExecJS → InvokeSync which blocks until the main + // thread executes the JS. Holding windowsLock.RLock during InvokeSync causes + // a deadlock when the main thread (or any other goroutine) needs windowsLock + // for write operations (NewWithOptions, Remove) — the pending writer blocks + // new readers, and the existing readers can't complete because InvokeSync + // needs the main thread which is waiting for the write lock. t.app.windowsLock.RLock() - defer t.app.windowsLock.RUnlock() - for _, window := range t.app.windows { + windows := make([]Window, 0, len(t.app.windows)) + for _, w := range t.app.windows { + windows = append(windows, w) + } + t.app.windowsLock.RUnlock() + + for _, window := range windows { if event.IsCancelled() { return } From 5f04b191bd4aafdf35495e08b16be0e643944683 Mon Sep 17 00:00:00 2001 From: wayne Date: Fri, 3 Apr 2026 21:25:28 +1300 Subject: [PATCH 2/3] Update changelog --- v3/UNRELEASED_CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/v3/UNRELEASED_CHANGELOG.md b/v3/UNRELEASED_CHANGELOG.md index f4e306493e5..10e896dd6fb 100644 --- a/v3/UNRELEASED_CHANGELOG.md +++ b/v3/UNRELEASED_CHANGELOG.md @@ -51,6 +51,7 @@ After processing, the content will be moved to the main changelog and this file **Fixed:** - Fix memory leak in event system during window close operations (#5678) - Fix crash when using context menus on Linux with Wayland +- Fix deadlock EventIPCTransport.DispatchWailsEvent holding RLock during InvokeSync (#5106) **Security:** - Update dependencies to address CVE-2024-12345 in third-party library From 506d558594f5614a02f8aaf5c3a0377af186e28a Mon Sep 17 00:00:00 2001 From: wayne Date: Thu, 16 Apr 2026 09:12:12 +1200 Subject: [PATCH 3/3] Guard WebviewWindow.DispatchWailsEvent against destroyed windows --- v3/pkg/application/webview_window.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/v3/pkg/application/webview_window.go b/v3/pkg/application/webview_window.go index aec674db830..77e9d0d62cd 100644 --- a/v3/pkg/application/webview_window.go +++ b/v3/pkg/application/webview_window.go @@ -1232,6 +1232,9 @@ func (w *WebviewWindow) SetFrameless(frameless bool) Window { } func (w *WebviewWindow) DispatchWailsEvent(event *CustomEvent) { + if w.impl == nil || w.isDestroyed() { + return + } // Guard against race condition where event fires before runtime is initialized // This can happen during page reload when WindowLoadFinished fires before // the JavaScript runtime has mounted dispatchWailsEvent on window._wails