Repository navigation
GTK/X11 crashes from GTK calls inside @async_function worker threads #409
Description
Activity
Still present in master (0e0fa1c), and not X11-specific — here is a Wayland
reproduction with a core dump that shows the damage at the memory level.Environment
hypnotix 5.6 · GTK 3.24.52 · Python 3.14.7 · PyGObject 3.56.3 · glib 2.88.3
Arch Linux, Wayland (Hyprland), no X11 session — mpv runs through XWayland.The violations this issue lists are still unmarshalled in master
before_playandafter_playare@idle_function, but these are not:- L885
self.info_menu_item.set_sensitive(False)— called directly from the
@async_functionworker inplay_async(L877). - L887
self.reinit_mpv()— also from that worker.reinit_mpv(L1612) then calls
Gtk.events_pending()(L1630) andself.mpv_drawing_area.get_window().get_xid()
(L1646) off the main thread.Gtk.events_pending()inspects main-loop state from
a thread that does not own it. - L1549/L1551
self.window.get_window().get_cursor()/
set_cursor(Gdk.Cursor.new_from_name(...))— from the@async_functionreload
(L1500).
What the crash looks like on Wayland: heap corruption, not a BadRequest
SIGSEGV, logged by the kernel as a general protection fault (a non-canonical
pointer, not a null dereference):traps: hypnotix[1683518] general protection fault ip:7fd5627d344f in libgtk-3.so.0.2420.32[1d344f,...] #0 gtk_label_set_label (libgtk-3.so.0 + 0x1d344f) #1 gtk_header_bar_set_title (libgtk-3.so.0 + 0x1b17da) #2 ffi_call -> _gi -> libpython3.14 ... #14 g_main_context_iteration #15 g_application_runThe faulting instruction is the inlined
GTK_LABEL()cast in
gtk_label_set_label:mov (%rbx),%rax ; cmp %rsi,(%rax), with%rbx=
priv->title_label. Its class pointer read back as0x69f1699a694368ec, which is
not an address at all.Inspecting the core: the
GtkHeaderBaritself is intact and live —
priv->title= the provider name,priv->subtitle="Movies > <category>"— but all three of
its internal widgets (title_label,subtitle_label,label_box) have been
freed while priv still points at them. A search of the whole 56 MB heap finds
exactly one surviving pointer to the dead title label: the header bar's own private
struct. The freed label's memory had already been reissued to the image loader —
it now holds a 16-bit monotonic gamma curve (fitted exponent ≈ 2.4), i.e. an lcms2
tone-response table from glycin decoding channel artwork.So the widget was destroyed out from under a live parent, and the crash surfaced on
the nextheaderbar.set_title()fromnavigate_to.What was happening at the time
Browsing an Xtream provider, Movies → a category, with 188 channel-logo JPEGs
downloading concurrently (download_channel_logosworker threads) while navigating
between pages and starting playback. 29 threads live at the moment of the crash:
the GTK main thread, ~5 glycin image threads, several mpv threads, several
requests/SSL download threads.Caveat, stated plainly
The core proves the corruption and localises it precisely. It does not prove which
pair of racing calls caused it — I could not pin the freeing thread from a single
dump. I am reporting it here rather than as a new issue because the mechanism
(GObject state mutated from@async_functionworkers) is the one this issue
already identifies, and because it shows the failure is not limited to X11.Authored by Claude Opus 5 via Claude Code, from a systemd-coredump core dump.
- L885
Hypnotix 4.3 has GTK thread-safety violations that can crash on X11.
Environment:
Symptoms:
Gdk-ERROR: received X Window System error
BadRequest (invalid request code or no such operation)
request_code 0 minor_code 0
Root causes found:
LiveMainWindow.play_asyncis decorated with@async_functionbut calls GTK/GDK-sensitive code:self.info_menu_item.set_sensitive(False)self.before_play(channel)self.reinit_mpv()self.mpv_drawing_area.get_window().get_xid()LiveMainWindow.reloadis decorated with@async_function; in the Xtream provider path it directly calls GTK/GDK window cursor APIs from the worker thread:self.window.get_window().get_cursor()self.window.get_window().set_cursor(...)Gdk.Cursor.new_from_name(...)GTK must only be touched from the GTK main loop.
Proposed fixes:
GLib.idle_add/ the existing@idle_function.Patch direction:
@async_functionfromplay_async.mpv.wait_until_playing()into a worker helper.