fix: wrap GTK widget calls in GLib.idle_add() to prevent SIGSEGV on startup#567
Conversation
MediaPlayerThread is a 30fps background thread that was calling GTK widget methods directly, violating GTK's single-thread requirement. C-level memory corruption from the race caused a SIGSEGV ~7 seconds after startup (after the main window maps and image updates begin). Wrap the three offending calls in GLib.idle_add() to schedule them on the main loop thread: - ControllerKey.set_ui_key_image() in DeckController.py - ControllerTouchScreen.set_ui_image() in DeckController.py - ScreenBarImage.set_image() in ScreenBar.py (sidebar icon selector) Fixes StreamController#566
Three more GTK threading violations found after further testing: - LabelManager.set_action_label() called update_label_editor() unconditionally even when reached from plugin rpyc threads - LayoutManager.update() called update_layout_editor() directly - BackgroundManager.update() called update_background_editor() directly All three functions call GTK widget methods (label_editor, image_editor, background_editor load_for_identifier()) that must run on the main loop thread. Wrapping with GLib.idle_add() fixes the remaining SIGSEGV triggered on page-load events after extended runtime.
|
Update after real-hardware testing: After the initial fix, the app survived ~46 minutes before crashing again (vs the original ~7s). The second crash was triggered by a page-load event (likely sleep/resume). A deeper audit found 3 more violations in the same file:
All three are now wrapped with |
Page.initialize_actions() is called from a background thread during load_page(). It called load_initial_generative_ui() synchronously, which invoked ComboRow.set_selected() and other GTK widget methods from that background thread — corrupting GtkListItemManager state and causing an abort with: Gtk:ERROR:gtklistitemmanager.c:1039:gtk_list_tile_split: assertion failed: (n_items < tile->n_items) Route the actual widget initialization through GLib.idle_add() so it runs on the GTK main loop thread. The public method schedules the work; a private helper performs it.
|
Third round of testing found another violation (commit
Fix: Full list of fixes so far (7 total):
|

Summary
Fixes #566 — SIGSEGV ~7 seconds after startup caused by GTK threading violations in
MediaPlayerThread.MediaPlayerThreadruns at 30fps and was calling GTK widget methods directly from a background thread. GTK is not thread-safe; this causes C-level memory corruption that Python exception handling cannot catch, resulting inSIGSEGV.Three calls were missing
GLib.idle_add()guards:src/backend/DeckManagement/DeckController.pyControllerKey.set_ui_key_image()buttons[x][y].set_image(image)src/backend/DeckManagement/DeckController.pyControllerTouchScreen.set_ui_image()screenbar.image.set_image(image)src/windows/mainWindow/DeckPlus/ScreenBar.pyScreenBarImage.set_image()icon_selector.set_image(dial_image)Note:
ScreenBarImage.set_image()already correctly usedGLib.idle_add()for the pixbuf update (line 269). Only the sidebar icon selector call at line 284 was missing the guard.Test plan
"dev": {"n-fake-decks": 1}in settings.json)MediaPlayerThreadimage updates