[Gtk4][Bug?] Creating a window in response to a `glib::MainContext::channel` event after the application was created doesn't work

In my usecase I need my app to lie dormant until I get an event from a socket.
I chose to implement this using a networking thread that sends an event over a glib::MainContext::channel to the main loop, which creates the window.

It seems that if the event is sent after my application is initialized (Application::run_with_args is called), the window simply doesn’t get created. I also ran this with G_MESSAGES_DEBUG=all and nothing interesting came up.

Interestingly, if I run it with GTK_DEBUG=interactive (the gtk inspector), the window DOES appear, which might suggest that the fact I don’t create any window initially breaks things.

I have created a reproduction for this bug here:

Thanks :slight_smile:

Myself and another person tried to debug that and were unable to reproduce. I see the window show up.

Just a hunch: what is the gtk4 and libadwaita versions installed on your system? Have you tried to update them?

I have changed all references of adw to gtk and it still happens, testing on a newer version now (arch based liveusb).
I’m on Ubuntu 22.04, which seems to use libgtk 4.6.6:

$ apt show libgtk-4-1
Package: libgtk-4-1
Version: 4.6.6+ds-0ubuntu1
Priority: optional
Section: libs
Source: gtk4
Origin: Ubuntu
# ...

Also works fine here with Fedora 38. What exactly happens for you though? run_with_args() immediately returns after the activate and shutdown signal handlers were called?

Ah wait, you have to run with cargo run -- wait to reproduce the bug. A plain cargo run works just fine. If I pass wait then the window does not show indeed.

That seems like a bug in GTK to me. It deadlocks somewhere in Wayland when realizing the renderer:

Thread 1 (Thread 0x7ffff5f22a00 (LWP 574602) "gtk-delayed-win"):
#0  0x00007ffff6dc41d9 in __futex_abstimed_wait_common () at /lib64/libc.so.6
--Type <RET> for more, q to quit, c to continue without paging--
#1  0x00007ffff6dc6b79 in pthread_cond_wait@@GLIBC_2.3.2 () at /lib64/libc.so.6
#2  0x00007ffff67b20bb in wl_display_read_events () at /lib64/libwayland-client.so.0
#3  0x00007ffff67b3639 in wl_display_dispatch_queue () at /lib64/libwayland-client.so.0
#4  0x00007ffff67b486f in wl_display_roundtrip_queue () at /lib64/libwayland-client.so.0
#5  0x00007fffcbfe375d in dri2_initialize_wayland () at /lib64/libEGL_mesa.so.0
#6  0x00007fffcbfd6a90 in dri2_initialize () at /lib64/libEGL_mesa.so.0
#7  0x00007fffcbfc857d in eglInitialize () at /lib64/libEGL_mesa.so.0
#8  0x00007ffff7a21686 in gdk_display_init_egl () at /lib64/libgtk-4.so.1
#9  0x00007ffff79dfec9 in gdk_wayland_display_init_gl () at /lib64/libgtk-4.so.1
#10 0x00007ffff7a21ed7 in gdk_display_prepare_gl () at /lib64/libgtk-4.so.1
#11 0x00007ffff7a38cdf in gdk_surface_create_gl_context () at /lib64/libgtk-4.so.1
#12 0x00007ffff7a610a0 in gsk_gl_renderer_realize () at /lib64/libgtk-4.so.1
#13 0x00007ffff7a40fd6 in gsk_renderer_realize () at /lib64/libgtk-4.so.1
#14 0x00007ffff7a4117d in gsk_renderer_new_for_surface () at /lib64/libgtk-4.so.1
#15 0x00007ffff7883358 in gtk_window_realize () at /lib64/libgtk-4.so.1
#16 0x00007ffff7f7adba in g_signal_emit_valist () at /lib64/libgobject-2.0.so.0
#17 0x00007ffff7f7af33 in g_signal_emit () at /lib64/libgobject-2.0.so.0
#18 0x00007ffff786bcef in gtk_widget_realize () at /lib64/libgtk-4.so.1
#19 0x00007ffff788098d in gtk_window_show () at /lib64/libgtk-4.so.1
#20 0x00007ffff7f7adba in g_signal_emit_valist () at /lib64/libgobject-2.0.so.0
#21 0x00007ffff7f7af33 in g_signal_emit () at /lib64/libgobject-2.0.so.0
#22 0x00007ffff786981a in gtk_widget_show () at /lib64/libgtk-4.so.1
#23 0x00005555555caf2b in gtk4::auto::widget::{impl#2}::show<libadwaita::auto::window::Window> (self=0x7fffffffc860) at /home/slomo/.cargo/registry/src/index.crates.io-6f17d22bba15001f/gtk4-0.6.6/src/auto/widget.rs:1874
#24 0x00005555555cf599 in gtk_delayed_window_bug_repro::create_window (app=0x55555564c8d8) at src/main.rs:85
#25 0x00005555555ca0d8 in gtk_delayed_window_bug_repro::main_loop::{closure#0} () at src/main.rs:45
#26 0x00005555555d1681 in glib::main_context_channel::dispatch<(), gtk_delayed_window_bug_repro::main_loop::{closure_env#0}> (source=0x55555564c840, callback=..., _user_data=0x0) at /home/slomo/.cargo/registry/src/index.crates.io-6f17d22bba15001f/glib-0.17.9/src/main_context_channel.rs:231
#27 0x00007ffff707a39c in g_main_context_dispatch () at /lib64/libglib-2.0.so.0
#28 0x00007ffff70d8438 in g_main_context_iterate.isra () at /lib64/libglib-2.0.so.0
#29 0x00007ffff7077a23 in g_main_context_iteration () at /lib64/libglib-2.0.so.0
#30 0x00007ffff724effd in g_application_run () at /lib64/libgio-2.0.so.0
#31 0x00005555555c640e in gio::application::{impl#0}::run_with_args<libadwaita::auto::application::Application, &str> (self=0x7fffffffdc60, args=...) at /home/slomo/.cargo/registry/src/index.crates.io-6f17d22bba15001f/gio-0.17.9/src/application.rs:37
#32 0x00005555555cf350 in gtk_delayed_window_bug_repro::main_loop (event_recv=...) at src/main.rs:65
#33 0x00005555555cf10b in gtk_delayed_window_bug_repro::main () at src/main.rs:24
#34 0x00005555555cc15b in core::ops::function::FnOnce::call_once<fn() -> core::result::Result<(), alloc::boxed::Box<dyn core::error::Error, alloc::alloc::Global>>, ()> () at /rustc/8b07c8821ba91091af2a3185f950621e836bcb04/library/core/src/ops/function.rs:250
#35 0x00005555555c897e in std::sys_common::backtrace::__rust_begin_short_backtrace<fn() -> core::result::Result<(), alloc::boxed::Box<dyn core::error::Error, alloc::alloc::Global>>, core::result::Result<(), alloc::boxed::Box<dyn core::error::Error, alloc::alloc::Global>>> (f=0x5555555cf070 <gtk_delayed_window_bug_repro::main>) at /rustc/8b07c8821ba91091af2a3185f950621e836bcb04/library/std/src/sys_common/backtrace.rs:134
#36 0x00005555555d22b1 in std::rt::lang_start::{closure#0}<core::result::Result<(), alloc::boxed::Box<dyn core::error::Error, alloc::alloc::Global>>> () at /rustc/8b07c8821ba91091af2a3185f950621e836bcb04/library/std/src/rt.rs:166
#37 0x000055555560746e in core::ops::function::impls::{impl#2}::call_once<(), (dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> () at library/core/src/ops/function.rs:287
#38 std::panicking::try::do_call<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> () at library/std/src/panicking.rs:485
#39 std::panicking::try<i32, &(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> () at library/std/src/panicking.rs:449
#40 std::panic::catch_unwind<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> () at library/std/src/panic.rs:140
#41 std::rt::lang_start_internal::{closure#2} () at library/std/src/rt.rs:148
--Type <RET> for more, q to quit, c to continue without paging--
#42 std::panicking::try::do_call<std::rt::lang_start_internal::{closure_env#2}, isize> () at library/std/src/panicking.rs:485
#43 std::panicking::try<isize, std::rt::lang_start_internal::{closure_env#2}> () at library/std/src/panicking.rs:449
#44 std::panic::catch_unwind<std::rt::lang_start_internal::{closure_env#2}, isize> () at library/std/src/panic.rs:140
#45 std::rt::lang_start_internal () at library/std/src/rt.rs:148
#46 0x00005555555d228a in std::rt::lang_start<core::result::Result<(), alloc::boxed::Box<dyn core::error::Error, alloc::alloc::Global>>> (main=0x5555555cf070 <gtk_delayed_window_bug_repro::main>, argc=2, argv=0x7fffffffe5d8, sigpipe=0) at /rustc/8b07c8821ba91091af2a3185f950621e836bcb04/library/std/src/rt.rs:165
#47 0x00005555555cf62e in main ()
#48 0x00007ffff6d62b4a in __libc_start_call_main () at /lib64/libc.so.6
#49 0x00007ffff6d62c0b in __libc_start_main_impl () at /lib64/libc.so.6
#50 0x00005555555c5da5 in _start ()

Can you report that? Should be easy to reproduce also with a C testcase.

Using GDK_BACKEND=x11 works fine, so it’s something specific to the Wayland backend.

Ah sorry for that, in hindsight that was confusing.

I’m kinda stumped on how to implement this in C, since channel is not a thing (or I’ve missed it), the closest thing I saw was:

You could do that from the callback to g_timeout_add() for example. That seems like the easiest way.

When I tried that with python it didn’t reproduce :confused:

import gi

gi.require_version("Gtk", "4.0")

from gi.repository import Gtk, Gio, GLib


def create_window(app):
    window = Gtk.Window()
    window.set_application(app)
    window.set_title("It Works!")
    window.set_default_size(300, 300)
    window.show()


def main():
    app = Gtk.Application.new(
        "com.example.GtkBugRepro",
        Gio.ApplicationFlags.FLAGS_NONE,
    )
    GLib.timeout_add(1000, lambda: create_window(app))
    app.connect("activate", lambda _: print("activate"))
    app.connect("shutdown", lambda _: print("shutdown"))
    app.hold()
    app.run()
    print("done")


if __name__ == "__main__":
    main()

Same here. It must be some kind of race condition.

This seems to reproduce, I’m working on getting my debug symbols to work to confirm:

import threading
import time

import gi

gi.require_version("Gtk", "4.0")

from gi.repository import Gtk, Gio, GLib


def create_window(app):
    window = Gtk.Window()
    window.set_application(app)
    window.set_title("It Works!")
    window.set_default_size(300, 300)
    window.show()


def main():
    app = Gtk.Application.new(
        "com.example.GtkBugRepro",
        Gio.ApplicationFlags.FLAGS_NONE,
    )

    main_ctx = GLib.main_context_default()

    def thread():
        time.sleep(1)
        print("thread is triggering")
        main_ctx.invoke_full(
            GLib.PRIORITY_HIGH,
            lambda: create_window(app),
        )

    threading.Thread(target=thread).start()
    app.connect("activate", lambda _: print("activate"))
    app.connect("shutdown", lambda _: print("shutdown"))
    app.hold()
    app.run()
    print("done")


if __name__ == "__main__":
    main()
1 Like

Here are the backtraces on a fresh fedora install:

Looks identical to your backtrace. Where should I report this? Can I do this here or do I have to use a mailing list?

Yes, looks like the same problem. Not clear in what way this changes something for this to happen, compared to your first Python version :slight_smile:

Here would be the correct place: Sign in · GitLab

Ok, I narrowed it down to my use of PRIORITY_HIGH while I was rewriting it in C. I’ll report it right away.
Thanks for the help!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.