Skip to content

[wails3] deadlock - wails v3 alpha.74 #5106

@wayneforrest

Description

@wayneforrest

Description

Potential deadlock in EventIPCTransport.DispatchWailsEvent
(transport_event_ipc.go)

We hit a deadlock that traces back to a lock ordering
issue in the Wails event dispatch path.

In transport_event_ipc.go:7-17, DispatchWailsEvent acquires
windowsLock.RLock() and then calls window.DispatchWailsEvent() for each
window — which calls ExecJS → InvokeSync, blocking until the main
thread executes the JS.

func (t *EventIPCTransport) DispatchWailsEvent(event *CustomEvent) {
     t.app.windowsLock.RLock()          // holds read lock
     defer t.app.windowsLock.RUnlock()
     for _, window := range t.app.windows {
         if event.IsCancelled() {
             return
         }
         window.DispatchWailsEvent(event)  // → ExecJS → InvokeSync
 (blocks on main thread)
     }
 }

If the main thread needs windowsLock for any reason (e.g.
processURLRequest → WindowManager.GetByID → RLock, or any write lock
from NewWithOptions/Remove), you get a deadlock:

  • Event dispatch goroutines: hold windowsLock.RLock(), waiting for main
    thread via InvokeSync
  • Main thread: waiting for windowsLock (blocked because a write lock is
    pending, which blocks behind the active readers)

In our case this froze the entire app for 4+ hours. We confirmed the
deadlock via pprof goroutine dump — goroutine 1 (main thread) blocked
on windowsLock.RLock for 261 minutes, with ~30 event dispatch
goroutines piled up behind it.

Would it be possible to snapshot the window list under the lock and
then release it before calling InvokeSync? Something like:

func (t *EventIPCTransport) DispatchWailsEvent(event *CustomEvent) {
    t.app.windowsLock.RLock()
    snapshot := make([]Window, 0, len(t.app.windows))
    for _, w := range t.app.windows {
        snapshot = append(snapshot, w)
    }
    t.app.windowsLock.RUnlock()

    for _, window := range snapshot {
        if event.IsCancelled() {
            return
        }
        window.DispatchWailsEvent(event)
    }
}

This is on Wails v3 alpha.74, macOS (darwin/arm64).

To Reproduce

...

Expected behaviour

...

Screenshots

No response

Attempted Fixes

No response

System Details

...

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSomething isn't workingawaiting feedbackMore information is required from the requestorv3

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions