https://bugs.kde.org/show_bug.cgi?id=520762
Bug ID: 520762
Summary: AccessPoint construction does a synchronous D-Bus
GetAll (retrieveInitialProperties), pinning the
consumer's thread at 100% CPU / hanging the UI in
dense Wi-Fi environments
Classification: Frameworks and Libraries
Product: frameworks-networkmanager-qt
Version First unspecified
Reported In:
Platform: Fedora RPMs
OS: Linux
Status: REPORTED
Severity: major
Priority: NOR
Component: general
Assignee: [email protected]
Reporter: [email protected]
CC: [email protected], [email protected]
Target Milestone: ---
Created attachment 192697
--> https://bugs.kde.org/attachment.cgi?id=192697&action=edit
Plasma Crash bundle (core dump, main thread, eu stack)
AccessPoint construction does a synchronous D-Bus GetAll
(retrieveInitialProperties),
pinning the consumer's thread at 100% CPU / hanging the UI in dense Wi-Fi
environments
## Severity / Priority
Major (UI freeze + sustained 100% CPU; not a data-loss crash)
## Versions
- networkmanager-qt (KF6): 6.26.0
- Qt: 6.11.1
- plasma-workspace (plasmashell): 6.6.5
- NetworkManager: 1.56.0
- OS: Fedora 44, kernel 7.0.9, Wayland session
---
## Summary
I traced a recurring plasmashell hang on my workstation to a synchronous D-Bus
call in
networkmanager-qt. `NetworkManager::AccessPoint`'s constructor calls
`NetworkManagerPrivate::retrieveInitialProperties()`, which issues a
**blocking** D-Bus
`GetAll` on the calling thread:
```cpp
// networkmanager-qt/src/manager.cpp (I checked master — still present)
QVariantMap NetworkManagerPrivate::retrieveInitialProperties(const QString
&interfaceName, const QString &path)
{
QDBusMessage message = QDBusMessage::createMethodCall(DBUS_SERVICE, path,
FDO_DBUS_PROPERTIES, "GetAll");
message << interfaceName;
QDBusMessage resultMessage = QDBusConnection::systemBus().call(message);
// <-- BLOCKING, not asyncCall()
...
}
```
`AccessPoint` objects are constructed eagerly from
`WirelessDevicePrivate::accessPointAdded()`, which fires once per
AccessPointAdded D-Bus
signal. In my environment, with many access points and normal scan churn, each
periodic
scan produces a burst of AccessPointAdded signals, and the wrapper performs one
blocking
system-bus round-trip per AP on the thread that runs NetworkManagerQt's event
handling.
On my machine the consumer is plasmashell, so those blocking calls land on its
GUI/main
thread.
The effect is that plasmashell's main thread serializes hundreds of blocking
D-Bus calls,
saturating one core at 100% and starving its own event loop. In my Wayland
session this
also makes plasmashell lose its outputs (journal: "There are no outputs -
creating
placeholder screen"), so all panels (taskbar, clock, system tray) disappear
while the
compositor and other apps keep running. I can only recover the shell with
`systemctl --user restart plasma-plasmashell.service`.
## Steps to reproduce
1. Use a workstation in a dense RF environment (in my case ~91 visible APs).
2. Wi-Fi radio enabled but not connected (I'm on wired Ethernet);
NetworkManager does
periodic background scans.
3. Run a NetworkManagerQt consumer that lives on the GUI thread — for me,
plasmashell
with the Networks/plasma-nm applet.
4. Leave it running. Over time, during scans, plasmashell pegs one core at
100%.
## What I observed
- plasmashell's main thread at 100% CPU, UI unresponsive / panels gone.
- One plasmashell instance on my machine had consumed ~1 full core
*continuously for
~2.7 days*.
- When I run `nmcli radio wifi off`, that thread drops from 100% to 0% within
seconds;
re-enabling the radio brings the load back. That's what isolated the cause to
AP
handling for me.
## Expected behavior
Adding/enumerating access points should not block the calling thread. Initial
property
retrieval should be asynchronous (or batched/lazy), so a busy scan in a dense
environment
cannot saturate or freeze a GUI thread.
## Backtrace (the hang site)
The SIGABRT in my capture is incidental — that was systemd killing the hung
unit on a
stop timeout. The relevant frames are the synchronous D-Bus call on the main
thread:
```
#7 pthread_cond_wait
#8 QWaitCondition::wait
#9 QDBusPendingCallPrivate::waitForFinished()
#10 QDBusConnectionPrivate::sendWithReply(..., QDBus::CallMode, int)
#11 QDBusConnection::call(...) <-- blocking
#12 NetworkManager::NetworkManagerPrivate::retrieveInitialProperties(QString,
QString)
#13 NetworkManager::AccessPoint::AccessPoint(QString const&, QObject*)
#14 NetworkManager::WirelessDevicePrivate::accessPointAdded(QDBusObjectPath
const&)
#15 doActivate<false>(...)
#16
OrgFreedesktopNetworkManagerDeviceWirelessInterface::qt_static_metacall(...)
#18 QDBusConnectionPrivate::deliverCall(...)
#19 QObject::event -> ... -> QCoreApplication::exec -> main
```
All other ~57 threads were idle (Mesa worker pool, Qt Quick render threads,
Wayland/DBus
helper threads). I have the full backtrace, 15 identical main-thread profiler
samples,
and live CPU before/after measurements attached.
## Possible directions
I'm not sure which approach is best — leaving that to the maintainers — but
some options:
- Make initial property retrieval asynchronous (GetAll via `asyncCall` + handle
the
reply) so AccessPoint construction doesn't block the caller's thread; or
- Defer/lazy-load AP properties until they're actually accessed; or
- Coalesce/rate-limit AccessPointAdded handling so a single scan burst in a
dense
environment can't issue hundreds of synchronous round-trips on the UI thread.
## Workaround I'm using
On this wired machine I keep the Wi-Fi radio off (`nmcli radio wifi off`) and
toggle it
on only when I actually need it.
--
You are receiving this mail because:
You are watching all bug changes.