status/network: Fix WirelessNetwork related leaks

NetworkManager frequently refreshes the list of available access points.
For some reason this often ends up removing some or all access points
only to add them back in a later refresh later. With the exception of
the currently connected access point, which is never removed.

When all access points of a WirelessNetwork have been removed, it gets
destroyed by NMWirelessDeviceItem::_removeAccessPoint(). This however
does not happen for the currently connected network due to the always
present access point. If this network now happens to consist of multiple
access points, the "unused" NMAccessPoints will get removed and added
in these refreshes, without the WirelessNetwork getting destroyed.

Whenever such an unused access point is added, due to the use of signal
tracking this leaks the NMAccessPoint and SignalTracker until the
WirelessNetwork is destroyed.

However when the NMWirelessDeviceItem is destroyed, for example due to
suspending, it stops tracking access point changes, ensuring that the
condition for the WirelessNetwork being destroyed can not occur anymore.

Even with just two access points, such as can be found in 2.4GHz+5GHz
home routers this issue leaks hundreds of NMAccessPoints and
SignalTrackers per day. As well as a small number of WirelessNetworks
which are also kept alive by the SignalTrackers.

To fix this disconnect from the access point when it gets removed and
destroy all remaining networks when the NMWirelessDeviceItem is
destroyed.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2652>
This commit is contained in:
Sebastian Keller 2023-02-14 21:29:43 +01:00 committed by Marge Bot
parent 0c9daefada
commit feb1c57dde

View File

@ -876,6 +876,7 @@ const WirelessNetwork = GObject.registerClass({
if (!this._accessPoints.delete(ap))
return false;
ap.disconnectObject(this);
this._updateBestAp();
if (wasActive !== this.is_active)
@ -1084,6 +1085,11 @@ const NMWirelessDeviceItem = GObject.registerClass({
this._activeConnectionChanged();
this._availableConnectionsChanged();
this._updateItemsVisibility();
this.connect('destroy', () => {
for (const net of this._networkItems.keys())
net.destroy();
});
}
get icon_name() {