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

Strange drag and drop crash bug #1125

Closed
ravener opened this issue Sep 23, 2024 · 2 comments · Fixed by #1128
Closed

Strange drag and drop crash bug #1125

ravener opened this issue Sep 23, 2024 · 2 comments · Fixed by #1128
Labels
bug Something isn't working
Milestone

Comments

@ravener
Copy link

ravener commented Sep 23, 2024

I'm dealing with a weird crash happening with the use of Gtk.DropTarget

Strangely enough this only seems to happen inside Flatpak runtime. On both GNOME SDK 46 and 47. But I cannot reproduce it on my own machine locally (Debian 12, GNOME 43).

The problem seems related to garbage collection.

I've narrowed it down to a simple reproducible window that shows this:

namespace MyApp
{
    public class BugWindow : Adw.ApplicationWindow
    {
        private readonly Gtk.Button _button;
        private readonly Gtk.Button _button2;
        private readonly Gtk.DropTarget _drop;
        private readonly Gtk.DropTarget _drop2;
        public BugWindow(Gtk.Application app)
        {
            Application = app;

            var box = Gtk.Box.New(Gtk.Orientation.Vertical, 6);
            box.Append(Adw.HeaderBar.New());
            _button = Gtk.Button.NewWithLabel("File 1");
            _button2 = Gtk.Button.NewWithLabel("File 2");
            _button.OnClicked += OnFileSelect;
            _button2.OnClicked += OnFileSelect;
            box.Append(_button);
            box.Append(_button2);

            SetContent(box);

            _drop = Gtk.DropTarget.New(Gio.FileHelper.GetGType(), Gdk.DragAction.Copy);
            _drop.OnDrop += OnDrop;
            _button.AddController(_drop);

            _drop2 = Gtk.DropTarget.New(Gio.FileHelper.GetGType(), Gdk.DragAction.Copy);
            _drop2.OnDrop += OnDrop;
            _button2.AddController(_drop2);
        }

        private bool OnDrop(Gtk.DropTarget drop, Gtk.DropTarget.DropSignalArgs e)
        {
            System.GC.Collect(); // Force gc for stress testing.
            Console.WriteLine("dropped!");
            var file = new Gio.FileHelper(e.Value.GetObject()!.Handle, false);
            var path = file.GetPath();
            Console.WriteLine(path);

            if (File.Exists(path))
            {
                Console.WriteLine("File exists.");
                if (drop == _drop)
                {
                    _button.SetLabel(file.GetBasename()!);
                    Console.WriteLine("Set label");
                }
                else if (drop == _drop2)
                {
                    _button2.SetLabel(file.GetBasename()!);
                    Console.WriteLine("Set label 2");
                }

                return true;
            }

            return false;
        }

        private void OnFileSelect(Gtk.Button sender, EventArgs args)
        {
            Console.WriteLine("here we are.");
            var chooser = Gtk.FileChooserNative.New("Open stuff", this, Gtk.FileChooserAction.Open, null, null);
            chooser.Show();
        }
    }
}
  • Press File 1 button and when the file chooser opens, do not select the file with it but use it to drag and drop files into the File 1 button, this should update the file 1 button's label to the dragged file's name.
  • Continue to drag a file into the File 2 button and watch the app crash.

This is the error logs

Process terminated. A callback was made on a garbage collected delegate of type 'GObject-2.0!GObject.Internal.ToggleNotify::Invoke'.
Repeat 2 times:
--------------------------------
   at GObject.Internal.Object.RemoveToggleRef(IntPtr, GObject.Internal.ToggleNotify, IntPtr)
--------------------------------
   at GObject.Internal.ObjectMapper+ToggleRef.<Dispose>b__9_0()
   at GLib.Internal.SourceFuncAsyncHandler.<.ctor>b__3_0(IntPtr)
   at Gio.Internal.Application.Run(IntPtr, Int32, System.String[])
   at Gio.Internal.Application.Run(IntPtr, Int32, System.String[])
   at Gio.Application.Run(Int32, System.String[])
   at Gio.Application.RunWithSynchronizationContext(System.String[])
   at Oto.Program.Run(System.String[])
   at Oto.Program.Main(System.String[])
>>> Error: Child process exited with code 134

Again emphasizing this happens in Flatpak runtime and only when drag and drop was used.

Here's a video demonstration:

Screencast.from.2024-09-24.03-32-04.webm
@badcel badcel added the bug Something isn't working label Sep 24, 2024
@badcel
Copy link
Member

badcel commented Sep 24, 2024

This sounds a bit like while disposing the ToggleRef there is some invocation of the notify callback.

This needs to be investigated.

@badcel
Copy link
Member

badcel commented Sep 29, 2024

Good news, I can reproduce the bug on my system. I will work on the fix here: #1128

badcel added a commit that referenced this issue Oct 2, 2024
It can happen that calling "RemoveToggleRef" leads to a delayed invocation of it's toggle notify callback. If "RemoveToggleRef" is called the garbage collector is free to free the ToggleRef in which case the managed "ToggleNotify" callback is freed, too.

This change uses an "UnmanagedCallersOnly" function which is out of scope of the GC and can always be called. In case it is called after the ToggleRef was freed it just does nothing.

Fixes: #1125
badcel added a commit that referenced this issue Oct 2, 2024
It can happen that calling "RemoveToggleRef" leads to a delayed invocation of it's toggle notify callback. If "RemoveToggleRef" is called the garbage collector is free to free the ToggleRef in which case the managed "ToggleNotify" callback is freed, too.

This change uses an "UnmanagedCallersOnly" function which is out of scope of the GC and can always be called. In case it is called after the ToggleRef was freed it just does nothing.

Fixes: #1125
badcel added a commit that referenced this issue Oct 3, 2024
It can happen that calling "RemoveToggleRef" leads to a delayed invocation of it's toggle notify callback. If "RemoveToggleRef" is called the garbage collector is free to free the ToggleRef in which case the managed "ToggleNotify" callback is freed, too.

This change uses an "UnmanagedCallersOnly" function which is out of scope of the GC and can always be called. In case it is called after the ToggleRef was freed it just does nothing.

Fixes: #1125
badcel added a commit that referenced this issue Oct 3, 2024
It can happen that calling "RemoveToggleRef" leads to a delayed invocation of it's toggle notify callback. If "RemoveToggleRef" is called the garbage collector is free to free the ToggleRef in which case the managed "ToggleNotify" callback is freed, too.

This change uses an "UnmanagedCallersOnly" function which is out of scope of the GC and can always be called. In case it is called after the ToggleRef was freed it just does nothing.

Fixes: #1125
@badcel badcel closed this as completed in 6cb9fe0 Oct 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants