Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GC is eating my widgets #105

Open
phil294 opened this issue Dec 4, 2022 · 4 comments
Open

GC is eating my widgets #105

phil294 opened this issue Dec 4, 2022 · 4 comments

Comments

@phil294
Copy link

phil294 commented Dec 4, 2022

Hello,

consider this demo code which simply adds 100 buttons:

require "gobject/gtk/autorun"

window = Gtk::Window.new
window.connect "destroy", &->Gtk.main_quit
fixed = Gtk::Fixed.new
window.add fixed
50.times do |i|
    20.times do |j|
        btn = Gtk::Button.new label: i.to_s+"/"+j.to_s
        fixed.put btn, i*10, j*10
    end
end
window.show_all

Expected:
img
Actual:
img

Bug only appears with GC active (default).

In this case, GC starts killing off widgets every ~130 widget additions or so. This bug can also appear much faster, however. I am building AHK_X11, AutoHotkey for Linux, a scripting language. When I translate the above Crystal code into AHK, widgets are disappearing even after 3-20 widgets have been placed. I will add a demo code snippet + GIF below. Its internals aren't important I assume (because there is a lot of other stuff going on too in the background which probably affects GC behavior), I only want to show this to somewhat prove the severity of this bug: It definitely occurs with only a few buttons as well.

out-4

Same behavior with CheckButtons. Did not observe such behavior with ComboBoxes or Labels.

Did not investigate much further than all of this. I will gladly provide more input as needed, however. Any idea on how to at least circumvent this bug (other than -Dgc_none, of course) would be much appreciated, as this is kind of a blocker for me now.

On a related note, I am seeing a few different GLib null pointers (exceptions) and invalid memory access errors (process abort) then and again. For example, the above GIF window crashed (it always does at some point). Those appear seemlingly random, so I guess they are GC-related too, but I haven't tested this yet, and it's not a show stopper. I also cannot use malloc_pthread_shim because I am using -Dpreview_mt which does not seem to work together. Might need to open up new issue(s) at some point.

Thank you once again for all your work on this!

@phil294
Copy link
Author

phil294 commented Dec 5, 2022

Aha! Solvable by maintaining a separate btn_refs = [] of Gtk::Button and keeping a ref of all buttons in there (btn_refs << btn).

Surely this is just a hacky workaround...?

@phil294
Copy link
Author

phil294 commented Dec 5, 2022

I've now also learned it is important to call GC manually once after destroying windows:

window.destroy
GC.collect
window = nil

If I don't do this, the GC runs at a later point in time, tries to run Window.finalize, but because the main window reference is gone, things run out of control. At least that's what I think is going on, based on stack traces like this one:

Invalid memory access (signal 11) at address 0x0
[0x556ccaf8e0a6] *Exception::CallStack::print_backtrace:Nil +118 in bin/ahk_x11
[0x556ccaf66a86] ~procProc(Int32, Pointer(LibC::SiginfoT), Pointer(Void), Nil) +310 in bin/ahk_x11
?
#0  0x0000556ccaf8e0b5 in print_backtrace () at /usr/lib/crystal/exception/call_stack/libunwind.cr:103
#1  0x0000556ccaf66a86 in -> () at /usr/lib/crystal/signal.cr:151
#2  0x00007f33f1184a00 in <signal handler called> () at /usr/lib/libc.so.6
#3  0x00007f33f1c10947 in  () at /usr/lib/libgobject-2.0.so.0
#4  0x00007f33f1c00f75 in g_signal_emit_valist () at /usr/lib/libgobject-2.0.so.0
#5  0x00007f33f1c01204 in g_signal_emit () at /usr/lib/libgobject-2.0.so.0
#6  0x00007f33f231f35e in  () at /usr/lib/libgtk-3.so.0
#7  0x00007f33f232eec1 in  () at /usr/lib/libgtk-3.so.0
#8  0x00007f33f1bf1104 in g_object_unref () at /usr/lib/libgobject-2.0.so.0
#9  0x0000556ccb10db5a in finalize () at /b/ahk_x11/lib/gobject/src/gtk/gobject-cache-gtk.cr:18657
#10 0x0000556ccaf73732 in -> () at /usr/lib/crystal/gc/boehm.cr:193
#11 0x00007f33f13b523f in GC_invoke_finalizers () at /usr/lib/libgc.so.1
#12 0x00007f33f13b546e in  () at /usr/lib/libgc.so.1
#13 0x00007f33f13bf713 in GC_generic_malloc_many () at /usr/lib/libgc.so.1
#14 0x00007f33f13cdf6e in GC_malloc_kind () at /usr/lib/libgc.so.1
#15 0x0000556ccafe2776 in malloc () at /usr/lib/crystal/gc/boehm.cr:129
#16 0x0000556ccaf4353e in __crystal_malloc64 () at /usr/lib/crystal/gc.cr:24
#17 0x0000556ccb1c9ffb in new () at /usr/lib/crystal/channel.cr:101
#18 0x0000556ccb19df7b in create_context_and_wait () at /usr/lib/crystal/channel.cr:46
#19 0x0000556ccb1c48fb in select_impl () at /usr/lib/crystal/channel.cr:457
#20 0x0000556ccb1c36f6 in select () at /usr/lib/crystal/channel.cr:408
#21 0x0000556ccb1603d1 in clock () at /b/ahk_x11/src/run/runner.cr:127
#22 0x0000556ccaf70319 in -> () at /b/ahk_x11/src/run/runner.cr:87
#23 0x0000556ccb01cc62 in run () at /usr/lib/crystal/fiber.cr:146
#24 0x0000556ccaf665a6 in -> () at /usr/lib/crystal/fiber.cr:98
#25 0x0000000000000000 in  ()

with /gobject-cache-gtk.cr:18657 being Gtk::Window.finalize.

The same necessity applies to widgets:

window.destroy
widgets.each &.destroy
GC.collect
window = nil
widgets = nil
GC.collect

Just a few more crashes to solve...

@hugopl
Copy link
Contributor

hugopl commented Dec 6, 2022

It worked with GTK4 bindings.

require "gtk4"

app = Gtk::Application.new("hello.example.com", Gio::ApplicationFlags::None)
app.activate_signal.connect do
  window = Gtk::ApplicationWindow.new(app)

  fixed = Gtk::Fixed.new
  50.times do |i|
      20.times do |j|
          btn = Gtk::Button.new label: i.to_s+"/"+j.to_s
          fixed.put btn, i*10, j*10
      end
  end
  window.child = fixed
  window.present
end

exit(app.run)

image

@phil294
Copy link
Author

phil294 commented Dec 6, 2022

Interesting, thanks. This speaks in favor of the approach taken by your GI Crystal shard 👍

I can't really use Gtk 4 though, as I need cross-distro binaries with as much long-lasting compatibility as possible (issue). I'd probably do it if I could statically link gtk4 somehow

Edit: I've now switched from this shard to https://github.com/phil294/gtk3.cr instead where I am facing less issues

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants