fix: read CurrentDNSServer over a private, mainloop-less bus #119

Merged
mysticalsoap merged 1 commit from fix/dns-watchdog-dbus-thread into trunk 2026-08-22 13:45:17 -04:00
Owner

Problem. The #95 DNS watchdog has never worked in the running service. current_dns_server() called dbus.SystemBus() from the watchdog thread and got the process-shared connection glued to the Qt main loop (DBusQtMainLoop(set_as_default=True) in the service), which the main thread owns. Qt refuses cross-thread timers, the CurrentDNSServer read failed on every tick, and watch_dns_primary looped on continue forever — a stranded fallback selection was never returned to the primary. Only trace: QObject::startTimer: Timers cannot be started from another thread in the journal every 15s while a tunnel is up.

Fix. Read over a private, mainloop-less connection (dbus.SystemBus(private=True, mainloop=dbus.mainloop.NULL_MAIN_LOOP)), closed after each poll. Blocking calls need no mainloop, and this stops sharing one connection between two threads.

Verification. Repro harness under the service's exact conditions (DBusQtMainLoop default on the main thread, read from a worker thread) against the live system:

fixed path: 192.168.0.205
old path:   DBusException: org.freedesktop.DBus.Error.NoMemory

274 tests pass.

Closes #117

🤖 Generated with Claude Code

**Problem.** The #95 DNS watchdog has never worked in the running service. `current_dns_server()` called `dbus.SystemBus()` from the watchdog thread and got the process-shared connection glued to the Qt main loop (`DBusQtMainLoop(set_as_default=True)` in the service), which the main thread owns. Qt refuses cross-thread timers, the CurrentDNSServer read failed on every tick, and `watch_dns_primary` looped on `continue` forever — a stranded fallback selection was never returned to the primary. Only trace: `QObject::startTimer: Timers cannot be started from another thread` in the journal every 15s while a tunnel is up. **Fix.** Read over a private, mainloop-less connection (`dbus.SystemBus(private=True, mainloop=dbus.mainloop.NULL_MAIN_LOOP)`), closed after each poll. Blocking calls need no mainloop, and this stops sharing one connection between two threads. **Verification.** Repro harness under the service's exact conditions (DBusQtMainLoop default on the main thread, read from a worker thread) against the live system: ``` fixed path: 192.168.0.205 old path: DBusException: org.freedesktop.DBus.Error.NoMemory ``` 274 tests pass. Closes #117 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix: read CurrentDNSServer over a private, mainloop-less bus
All checks were successful
ci / test (pull_request) Successful in 2m31s
58baf74754
The service installs DBusQtMainLoop as the process default, so the
shared dbus.SystemBus() the watchdog thread picked up was glued to a
Qt main loop owned by the main thread. Qt refuses cross-thread timers,
the property read failed every time, and watch_dns_primary saw None on
every tick -- the #95 anti-sticky behaviour has never once corrected a
stranded fallback in the running service. The only trace was a
QObject::startTimer warning in the journal each 15s interval.

Blocking calls need no mainloop, so take a private connection with
dbus's null mainloop for the read and close it after each poll. This
also stops sharing one connection between two threads, which
dbus-python never promised to survive.

Closes #117

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mysticalsoap force-pushed fix/dns-watchdog-dbus-thread from 58baf74754
All checks were successful
ci / test (pull_request) Successful in 2m31s
to 825e25d324
Some checks failed
ci / test (pull_request) Failing after 8m36s
2026-08-22 13:12:59 -04:00
Compare
mysticalsoap force-pushed fix/dns-watchdog-dbus-thread from 825e25d324
Some checks failed
ci / test (pull_request) Failing after 8m36s
to 6486066824
All checks were successful
ci / test (pull_request) Successful in 33s
ci / test (push) Successful in 41s
2026-08-22 13:39:57 -04:00
Compare
mysticalsoap deleted branch fix/dns-watchdog-dbus-thread 2026-08-22 13:45:17 -04:00
Sign in to join this conversation.
No description provided.