Skip to content

panel.js: Don't leak the panel context menu on monitor changes - #14059

Open
somedev7 wants to merge 1 commit into
linuxmint:masterfrom
somedev7:panel-context-menu-leak
Open

somedev7 wants to merge 1 commit into
linuxmint:masterfrom
somedev7:panel-context-menu-leak

Conversation

@somedev7

Copy link
Copy Markdown

Problem

Panel.updatePosition() runs for every panel on each monitors-changed (monitor sleep/wake, hotplug) and calls addContextMenuToPanel(), which creates a new PanelContextMenu and adds it to this._menus without destroying the previous one. The old menus stay hidden in Main.uiGroup and registered in the PopupMenuManager forever.

Impact

Found while investigating periodic desktop stutter on Linux Mint 22.3 / Cinnamon 6.6.9 / cjs 115.1. After 33 days of uptime the live JS heap (imports.system.dumpMemoryInfo() → gcBytes) was 329 MB. Since cjs schedules a non-incremental "big hammer" GC on toggle-down, which on a live desktop fires every ~10 s, the compositor was blocked 200–234 ms every 10 s (a forced imports.system.gc() took 215 ms). After restarting Cinnamon: 6 MB heap, 7 ms GC. A heap dump showed several leaks; the biggest one is #13956 (fix in #13958), this is another one.

On a two-monitor setup (the DisplayPort monitor goes to sleep several times a day), after 33 days Main.uiGroup held 451 hidden PanelContextMenu instances — about 32,800 of the 37,300 actors on the stage.

Reproduce

In Looking Glass:

for (let i = 0; i < 3; i++) Main.panel.addContextMenuToPanel(Main.panel.panelPosition);
Main.panel._menus._menus.filter(m => m instanceof imports.ui.panel.PanelContextMenu).length

Without the fix the count grows by one per call; with it, it stays at 1.

Fix

Close and destroy the existing menu before creating a new one. PopupMenuManager.addMenu() already connects to the menu's destroy signal and removes it.

Tested on 6.6.9 with the equivalent change applied at runtime: after re-creating the menu it still opens and closes with all 12 items, and only one PanelContextMenu is left in the manager and in uiGroup.

🤖 Generated with Claude Code

@CyberJody

Copy link
Copy Markdown

I tested this on Debian 13 with Cinnamon 6.4.10 (X11), two monitors (DP + HDMI) and an AMD RX 580. Cinnamon 6.4 has the same addContextMenuToPanel() on Panel.prototype, so I applied this change at runtime from Looking Glass and counted the PanelContextMenus in panel._menus and in Main.uiGroup:

step context menus (manager / uiGroup) stage actors
unpatched, after a fresh start and 4 xrandr HDMI off/on cycles 9 / 9 3419
unpatched, after 3 more direct addContextMenuToPanel() calls (your repro) 12 / 12 3629 (+70 per call)
patched, same 3 repro calls 12 / 12 3629 (+0)
patched, 3 real HDMI off/on cycles 12 / 12 3628

Unpatched, each HDMI off/on cycle left 2 more menus (one per monitors-changed). With the patch, the panel context menu still opens and closes with all its items after the monitor changes, and there are no JS errors.

One note: when the context menu is the manager's only menu, destroy() makes PopupMenuManager.removeMenu() hit _menus.length === 0 and call the manager's destroy(). That only disconnects its signals, and the following addMenu() reconnects them for the new menu, so I saw no side effects. It's the same path Panel.destroy() already takes.

I found this while tracking down a separate gTile leak on the same event (linuxmint/cinnamon-spices-extensions#1185). This was the remaining per-monitors-changed growth after that fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants