镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

power applet: disconnect signals and cancel pending calls on removal - #13949

Merged
mtwebster merged 1 commit into
linuxmint:masterfrom
rumours86:power-applet-debounce-rebuild
Oct 2, 2026
Merged

mtwebster merged 1 commit into
linuxmint:masterfrom
rumours86:power-applet-debounce-rebuild

Conversation

@rumours86

@rumours86 rumours86 commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

Reworked after review: this PR is now the signal/Gio.Cancellable cleanup you suggested, and the debounce is gone from it. Details on the original "burst" question are in the comment below.

on_applet_removed_from_panel() only disconnected the power-profiles proxy. Everything else the constructor connects to objects that outlive the applet stayed connected:

  • two handlers on global.settings (panel-edit-mode, device-aliases)
  • one on the LoginManager singleton (prepare-for-sleep)
  • one on the csd-power proxy (g-properties-changed → _devicesChanged)
  • one on the brightness proxy
  • the csd-power bus watch, if the applet is removed before the name appears

So every reload leaves another live _devicesChanged() listener behind, and they all keep firing.

Measured by counting GetDevices calls on the session bus caused by a single device-aliases change — three samples per state, shell restarted before each run:

one instance after reloading the applet 3×
stock 1 / 1 / 3 4 / 4 / 12
with this patch 3 / 1 / 1 1 / 1 / 1

(The 1-vs-3 spread on a single instance is dconf/settings-binding noise and is present either way; what matters is that the stock applet keeps climbing with each reload while the patched one stays at one rebuild.)

Pending GetPrimaryDeviceRemote / GetDevicesRemote replies could also land on a destroyed applet. They now take a Gio.Cancellable that is cancelled on removal, and both callbacks return early on CANCELLED.

The handlers touched here are also converted from the deprecated Lang.bind() to arrow functions, as flagged by the best-practices scanner.

🤖 Generated with Claude Code

@rumours86

Copy link
Copy Markdown
Contributor Author

Now verified against git master (built muffin + cinnamon 6.7.5, run nested, master power applet loaded on the panel): 10 rapid _devicesChanged() calls result in exactly 2 full menu rebuilds — one immediate, one trailing with the final state — instead of 10. The applet loads and functions normally with the patch.

🤖 Addressed by Claude Code

@rumours86

Copy link
Copy Markdown
Contributor Author

Retested against freshly built current master: Cinnamon 61c1b3d1cb1d83e263507fd59aede221bce9abf8 and Muffin 963a573eff827d740ba8ce9592f3b2873aa0b952 (both 6.7.8), plus current cinnamon-desktop and cinnamon-settings-daemon. This used the real applet and St actors in an isolated X11 session. A controlled service on the private session bus supplied power-device signals and delayed replies; it was not a physical charger test.

I compared unpatched master and this PR in separate native sessions, both at UI scale 1. The same burst of 40 real D-Bus property-change signals caused 40 GetPrimaryDevice/GetDevices pairs, menu rebuilds and panel updates on master, versus 2 of each with the PR. Both actual panel labels reached the final 71%. Request/reply counts were measured at the real Gio proxy and agreed with the service-side counts; the test did not assume a particular rebuild count. Sustained updates also reached the final 82%.

I added efd6d99 to cancel the pending timeout and ignore notifications/in-flight replies after removal. Three native lifecycle tests failed with the original PR: removing with a pending trailing refresh, removing before GetPrimaryDevice returned, and removing before GetDevices returned. All seven power tests now pass, including repeated removal/re-addition and final-state checks.

The Build notification was an expired approval request, not a compiler/test failure: the old run started zero jobs. Could a maintainer approve the new Build run? Pattern Check and CodeFactor already pass. The separate dependency failures on current master's own Build run remain outside this applet change.

@mtwebster

Copy link
Copy Markdown
Member

Where is this 'burst' coming from? Are you generating it synthetically?

Are you on a laptop with an ambient light sensor? cinnamon-settings-daemon 6.7.x has a bug where it's probably hammering the applet right now, but other than that I can't think of how this would happen in the wild - I don't want to add this for a theoretical problem.

on_applet_removed_from_panel should be used to disconnect signals - that's not being done here, which is likely the reason for any issues after the applet gets reloaded/removed for some reason.

The only loose end is if it gets removed/reloaded while waiting on a reply from GetPrimaryDeviceRemote or GetDevicesRemote - you can use a Gio.Cancellable for that:

...
    constructor(...) {
        this._cancellable = new Gio.Cancellable();
    }
...
...
this._proxy.GetDevicesRemote(this._cancellable, (result, error) => {
    if (error?.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
        return;
    ...
    ...
});
...
this._proxy.GetPrimaryDeviceRemote(this._cancellable, (result, error) => {
    if (error?.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
        return;
    ...
    ...
});
...
   on_applet_removed_from_panel() {
       this._cancellable.cancel();
   }
...

@rumours86
rumours86 force-pushed the power-applet-debounce-rebuild branch from efd6d99 to 1f2c8a2 Compare October 1, 2026 16:47
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown

Best-practices scanner

This is a regex-based check for API usage that can pose security, performance or
maintainability issues, or that may already be provided by Cinnamon. Most findings
are advisory and do not automatically disqualify a pull request.

This check is not perfect and will not replace a normal review.


Found 5 potential issue(s):

⚠️ WARNING

⚠️ lang_bind

files/usr/share/cinnamon/applets/power@cinnamon.org/applet.js:370

Lang.bind(this, this._onPanelEditModeChanged));

Lang.bind() is deprecated. Use arrow functions (() => {}) or Function.prototype.bind() instead.

files/usr/share/cinnamon/applets/power@cinnamon.org/applet.js:385

Lang.bind(this, this._devicesChanged));

Lang.bind() is deprecated. Use arrow functions (() => {}) or Function.prototype.bind() instead.

files/usr/share/cinnamon/applets/power@cinnamon.org/applet.js:387

Lang.bind(this, this._on_device_aliases_changed));

Lang.bind() is deprecated. Use arrow functions (() => {}) or Function.prototype.bind() instead.

files/usr/share/cinnamon/applets/power@cinnamon.org/applet.js:599

this._proxy.GetPrimaryDeviceRemote(this._cancellable, Lang.bind(this, function (device, error) {

Lang.bind() is deprecated. Use arrow functions (() => {}) or Function.prototype.bind() instead.

files/usr/share/cinnamon/applets/power@cinnamon.org/applet.js:616

this._proxy.GetDevicesRemote(this._cancellable, Lang.bind(this, function (result, error) {

Lang.bind() is deprecated. Use arrow functions (() => {}) or Function.prototype.bind() instead.


Automated pattern check.

@rumours86 rumours86 changed the title power applet: debounce device menu rebuilds power applet: disconnect signals and cancel pending calls on removal Oct 1, 2026
@rumours86

Copy link
Copy Markdown
Contributor Author

Thanks — you were right on the cleanup, and it turned out to be the more important half. I've reworked the PR to be exactly that (signals + Gio.Cancellable) and dropped the debounce from it. Measurements are in the updated description: reloading the applet three times took one device-aliases change from 1 to 4 GetDevices calls on stock, and stays at 1 with the patch.

On your questions, because they changed my own picture of this:

Where the burst came from — not synthetic. A dying 65 W PSU on this laptop: the charger drops out and comes back under load. I've been logging every real AC online transition since 23 August — 13 724 transitions, of which 991 bursts of ≥3 transitions less than 3 s apart, the worst being 98 transitions in 162 seconds (19 Sep 12:40). csd-power emits g-properties-changed for each one, and at the time the screen went black for seconds at a time (that's what I was originally chasing). So the trigger is a hardware fault — but a failing charger or a loose barrel jack isn't exotic, and the applet rebuilt the whole device menu on every single signal.

Ambient light sensor — yes, there is one (acpi-als, iio-sensor-proxy running), so your hunch about hardware was right too; but this box is on 6.6.4, not 6.7.x, and csd-power is quiet at idle here (8 PropertiesChanged in 45 s), so the 6.7 ALS bug isn't what I was seeing.

What I got wrong. My debounce also masked the leak you pointed out: with it applied, the leaked listeners collapse back into one rebuild, so the same three-reload test reads 1 instead of 4. I was treating a symptom and partly hiding the real bug.

If you'd still like coalescing afterwards — as a cheap guard for both a flapping charger and the 6.7 ALS hammering you mentioned — I'm happy to open it as a separate PR on top of this one, or to leave it out entirely. Your call.

🤖 Addressed by Claude Code

@rumours86
rumours86 force-pushed the power-applet-debounce-rebuild branch from 1f2c8a2 to 8a53017 Compare October 1, 2026 16:58
@rumours86

Copy link
Copy Markdown
Contributor Author

Scanner findings addressed: all five Lang.bind() hits were on lines this PR already touches, so they're now arrow functions. The three remaining Lang.bind() uses elsewhere in the file are untouched, to keep the diff on topic.

While re-verifying I also tightened the measurement — the first numbers I posted were single samples. Three samples per state, shell restarted before each run:

one instance after reloading the applet 3×
stock 1 / 1 / 3 4 / 4 / 12
with this patch 3 / 1 / 1 1 / 1 / 1

The 1-vs-3 spread on a single instance is settings-binding noise present in both, and background GetDevices traffic measured zero in a 6 s idle window. The leak itself is unambiguous: stock keeps climbing with each reload, patched stays at one rebuild.

🤖 Addressed by Claude Code

Comment thread files/usr/share/cinnamon/applets/power@cinnamon.org/applet.js
on_applet_removed_from_panel() only disconnected the power-profiles
proxy. Everything else the constructor connects to objects that outlive
the applet stayed alive: two handlers on global.settings, one on the
LoginManager singleton, one on the csd-power proxy, one on the
brightness proxy, plus the csd-power bus watch when the applet is
removed before the name appears.

Each reload therefore leaves another live _devicesChanged() listener
behind, and they all keep firing. Measured by counting GetDevices calls
on the session bus caused by a single 'device-aliases' change, three
samples per state, shell restarted before each run:

              one instance   after reloading the applet 3x
  stock       1 / 1 / 3      4 / 4 / 12
  patched     3 / 1 / 1      1 / 1 / 1

The AppletSettings object is finalized as well, so the labelinfo and
showmulti bindings stop calling _devicesChanged() on a removed instance.

Pending GetPrimaryDeviceRemote/GetDevicesRemote replies could also land
on a destroyed applet; they now take a Gio.Cancellable that is cancelled
on removal, and the callbacks return early on CANCELLED.

The handlers touched here are converted from the deprecated Lang.bind()
to arrow functions, as flagged by the best-practices scanner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rumours86
rumours86 force-pushed the power-applet-debounce-rebuild branch from 8a53017 to c28d7c2 Compare October 1, 2026 20:10
@rumours86

Copy link
Copy Markdown
Contributor Author

FYI on the red Mint 22 check: it isn't from this change. The job fails in mk-build-deps before any code is built — apt can't satisfy a debian/control build-dependency on the mint22.3 image and rolls back cinnamon-build-deps. The same check is currently failing on unrelated PRs (#13995, #13992, #13947); LMDE 7 and Mint 27 pass here.

🤖 Addressed by Claude Code

@mtwebster
mtwebster merged commit 528fbed into linuxmint:master Oct 2, 2026
5 of 6 checks passed
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