Use private non-GLib-integrated D-Bus connection to fix main-loop CPU spin#33
Open
km1tch wants to merge 1 commit into
Open
Use private non-GLib-integrated D-Bus connection to fix main-loop CPU spin#33km1tch wants to merge 1 commit into
km1tch wants to merge 1 commit into
Conversation
… spin The shared dbus.SessionBus() singleton attaches its watch sources to the default GLib main context, because StreamController sets DBusGMainLoop(set_as_default=True) at startup. MediaController then makes blocking MPRIS calls (list_names, Properties.Get, ...) from the tick_actions thread. The blocking thread and the GTK main loop end up sharing one connection: the connection's GSource keeps reporting messages pending dispatch while the blocking thread owns the connection, so the main loop wakes, dispatches nothing, and immediately re-polls - forever. The result is one core pegged at 100% for the lifetime of the process. Confirmed with py-spy: the main thread is always inside g_main_context_iteration with no Python callback on the stack; a native profile shows ~43% of main-thread time in g_wakeup_signal eventfd writes and ~33% in ppoll. Fix: give MediaController a private connection with no main-loop integration (mainloop=dbus.mainloop.NULL_MAIN_LOOP). Blocking calls then poll their own socket, which is thread-safe and never touches the GLib context. This plugin registers no D-Bus signal receivers, so nothing here needs main-loop integration. Tested on a live deployment (GNOME Wayland, Python 3.14, dbus-python 1.4.0, GLib 2.88): main-thread CPU went from a sustained 99.9% to idle, and MPRIS control/metadata calls kept working against Firefox and MPD. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #32 — StreamController's GTK main thread spins at ~100% CPU whenever MediaPlugin is active.
What
One functional line:
MediaControllernow opens a private D-Bus connection with no main-loop integration (mainloop=dbus.mainloop.NULL_MAIN_LOOP) instead of the shareddbus.SessionBus()singleton.Why
StreamController core sets
DBusGMainLoop(set_as_default=True), so the shared session bus attaches its watch sources to the GTK main context. MediaController's MPRIS calls are all blocking calls from thetick_actionsthread, which leaves the connection's GSource permanently "ready" while the tick thread owns the connection — the main loop wakes, dispatches nothing, and re-polls forever, pegging one core for the lifetime of the process. Full analysis with py-spy native profiles in the linked issue.With a private non-integrated connection, blocking calls poll their own socket (thread-safe in libdbus) and never touch the GLib context.
Safety
connect_to_signal/add_signal_receiver), so nothing in this repo needs main-loop integration.private=Trueavoids mutating behavior of the shared singleton for core or other plugins.Tested
Live deployment on GNOME Wayland (Python 3.14, dbus-python 1.4.0, GLib 2.88): main-thread CPU drops from a sustained 99.9% to idle after this change; play/pause/next, metadata, album art, and player discovery verified working against Firefox and MPD.
Diagnosed and fixed with help from Claude (Fable 5) — attribution retained in the commit trailer.