Description
A v3 app on Linux that updates its system-tray menu at runtime crashes with fatal error: concurrent map read and map write. The runtime aborts inside the godbus callback linuxSystemTray.GetLayout (or GetGroupProperties) on the godbus worker goroutine, and the process is forcibly killed — recover() cannot intercept it.
linuxSystemTray.itemMap and the per-item dbusItem.V1 property maps are written from the main thread (setMenu, update, the systrayMenuItem setters) while the dbusmenu callbacks run on the godbus worker goroutine. The pre-existing // FIXME: RLock? comments at v3/pkg/application/systemtray_linux.go:703,727 already flag the gap.
Reproduced on v3.0.0-alpha.78; the same code is still on master at the time of writing (latest tag v3.0.0-alpha.84).
To Reproduce
- Build any v3 app that has a
SystemTray with a menu and calls SystemTray.SetMenu(...) periodically — e.g. on a 30-second tick, or in response to events (timer started/stopped, sync state changed, …).
- Run it on Linux with the panel/system-tray visible — every panel paint triggers
GetLayout / GetGroupProperties on the dbusmenu interface.
- Wait. A few
SetMenu calls per minute is enough; under heavier load it fires within seconds.
A pure-Go reproducer that triggers the same fatal under go test -race without needing a session bus will be included in the accompanying PR (TestLinuxSystemTrayConcurrentSetMenu in v3/pkg/application/systemtray_linux_race_test.go). Removing the locks on a clean master reproduces the crash in milliseconds with the exact same stack frames as the production trace below.
Expected behaviour
SystemTray.SetMenu must be safe to call concurrently with the dbusmenu callbacks dispatched by godbus. Today it is not, and the resulting fatal is unrecoverable.
Screenshots
No response
Attempted Fixes
A patch with sync.RWMutex on linuxSystemTray plus a -race regression test is ready.
System Details
Wails: v3.0.0-alpha.78 (also reproduces on current master / v3.0.0-alpha.84)
OS: Ubuntu 25.x
Kernel: Linux 6.17.0-20-generic
DE: GNOME on Wayland (also reproduces on X11)
Go: go1.26.1 linux/amd64
Additional context
Stack trace from a real production crash:
fatal error: concurrent map read and map write
goroutine 3450 [running]:
internal/runtime/maps.fatal(...)
runtime/panic.go:1181 +0x18
github.com/wailsapp/wails/v3/pkg/application.(*linuxSystemTray).GetLayout(0x1306ff82e8c0, ...)
github.com/wailsapp/wails/v3@v3.0.0-alpha.78/pkg/application/systemtray_linux.go:728 +0x3f
reflect.Value.call(...)
reflect/value.go:586 +0xf06
github.com/godbus/dbus/v5.exportedMethod.Call(...)
github.com/godbus/dbus/v5@v5.2.2/default_handler.go:128 +0x247
github.com/godbus/dbus/v5.(*Conn).handleCall(...)
github.com/godbus/dbus/v5@v5.2.2/export.go:193 +0x545
created by github.com/godbus/dbus/v5.(*Conn).inWorker in goroutine 35
github.com/godbus/dbus/v5@v5.2.2/conn.go:435 +0x233
GetGroupProperties on the same goroutine type (godbus inWorker) crashes the same way — the test in the linked PR exercises both paths.
Description
A v3 app on Linux that updates its system-tray menu at runtime crashes with
fatal error: concurrent map read and map write. The runtime aborts inside the godbus callbacklinuxSystemTray.GetLayout(orGetGroupProperties) on the godbus worker goroutine, and the process is forcibly killed —recover()cannot intercept it.linuxSystemTray.itemMapand the per-itemdbusItem.V1property maps are written from the main thread (setMenu,update, thesystrayMenuItemsetters) while the dbusmenu callbacks run on the godbus worker goroutine. The pre-existing// FIXME: RLock?comments atv3/pkg/application/systemtray_linux.go:703,727already flag the gap.Reproduced on
v3.0.0-alpha.78; the same code is still onmasterat the time of writing (latest tagv3.0.0-alpha.84).To Reproduce
SystemTraywith a menu and callsSystemTray.SetMenu(...)periodically — e.g. on a 30-second tick, or in response to events (timer started/stopped, sync state changed, …).GetLayout/GetGroupPropertieson the dbusmenu interface.SetMenucalls per minute is enough; under heavier load it fires within seconds.A pure-Go reproducer that triggers the same fatal under
go test -racewithout needing a session bus will be included in the accompanying PR (TestLinuxSystemTrayConcurrentSetMenuinv3/pkg/application/systemtray_linux_race_test.go). Removing the locks on a cleanmasterreproduces the crash in milliseconds with the exact same stack frames as the production trace below.Expected behaviour
SystemTray.SetMenumust be safe to call concurrently with the dbusmenu callbacks dispatched by godbus. Today it is not, and the resultingfatalis unrecoverable.Screenshots
No response
Attempted Fixes
A patch with
sync.RWMutexonlinuxSystemTrayplus a-raceregression test is ready.System Details
Additional context
Stack trace from a real production crash:
GetGroupPropertieson the same goroutine type (godbus inWorker) crashes the same way — the test in the linked PR exercises both paths.