GTK: AssertionError: xor(prev, current_task() !== g_stack)

I’m getting this exception when calling GAccessor.text(buffer, "", -1) while handling an file-activated signal. I’ve spent the last 2 hours trying to recreate it with a single example, and so far I cannot reproduce it.

Not sure if anyone has a suggestions, but I would really like some idea of what would cause a task switch in this case.

Basically I’m handling the file-activated from a choose file dialog box, and my code does:

    @info "At Start  - Stack: $(Gtk.GLib.g_stack) Current: $(current_task())"
    hide(dialog)
    @info "At Call   - Stack: $(Gtk.GLib.g_stack) Current: $(current_task())"
    GAccessor.text(buffer, "", -1)

    @sigatom @async begin
         @info "At Inside - Stack: $(Gtk.GLib.g_stack) Current: $(current_task())"
         # Do something that takes a few seconds
         @sigatom begin
            GAccessor.text(buffer, result, -1)       
         end
    end

The output ends up:

[ Info: At Start  - Stack: Task (runnable) @0x00007fc22600b820 Current: Task (runnable) @0x00007fc22600b820
[ Info: At Call   - Stack: Task (runnable) @0x00007fc22600b820 Current: Task (runnable) @0x00007fc22600b820
┌ Error: Exception AssertionError("xor(prev, current_task() !== g_stack)")
└ @ Ui ~/.julia/packages/Ui/xc1es/src/gtk/module.jl:97
┌ Warning: AssertionError: xor(prev, current_task() !== g_stack)
│ 
│ g_siginterruptible(::getfield(Gtk.GLib, Symbol("##89#91")){Ptr{Gtk.GLib.GValue},UInt32,Ptr{Gtk.GLib.GValue},Ptr{Any},getfield(Ui, Symbol("##17#19")),Bool,Array{Any,1}}, ::Function) at signals.jl:200
│ GClosureMarshal(::Ptr{Nothing}, ::Ptr{Gtk.GLib.GValue}, ::UInt32, ::Ptr{Gtk.GLib.GValue}, ::Ptr{Nothing}, ::Ptr{Nothing}) at signals.jl:59
│ text(::GtkTextBufferLeaf, ::String, ::Int64) at gbox3:4538

I can work around the issue by prefixing the GAccessor.text(buffer, "", -1) call with @sigatom but I would really like to know why there is an issue. I’ve even tried adding a sleep after the text line to see if it was some sort of scheduling issue, but the problem still happens.

And it only happens the second time I perform the operation, IF the buffer contains existing data. If the buffer is empty because of first use or because I manually delete it, everything is fine…

Finally found it, it appears to be associated with setting text in a text buffer when you are also registered for changed notifications on the buffer. Here is the code:

using Gtk

function test(glade)
    builder = GtkBuilder(buffer=glade)
    showall(builder["main"])

    signal_connect(builder["buffer"], "changed") do widget
        println("Changed!")
    end

    signal_connect(builder["populate"], "clicked") do widget
        GAccessor.text(builder["buffer"], "", -1)
        # @sigatom GAccessor.text(builder["buffer"], "", -1)

        @sigatom @async begin
            sleep(1)
            @sigatom GAccessor.text(builder["buffer"], "abcde", -1)
        end
    end
end

test("""<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkTextBuffer" id="buffer"/>
  <object class="GtkApplicationWindow" id="main">
    <property name="can_focus">False</property>
    <property name="default_width">440</property>
    <property name="default_height">250</property>
    <child>
      <placeholder/>
    </child>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkButton" id="populate">
            <property name="label" translatable="yes">Populate</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkScrolledWindow">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="hexpand">True</property>
            <property name="vexpand">True</property>
            <property name="shadow_type">in</property>
            <child>
              <object class="GtkTextView">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="buffer">buffer</property>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>
""")

Run the script, enter an “a” in the window, click the “Populate” button and drag the window, you will get a crash. If you comment out line 12 and comment in line 13, everything works fine.

It crashes “best” if changing the buffer is done in a signal handler. I can also reproduce it without a signal handler but in that case the crash is less violent and a bit intermittent.

See: https://github.com/JuliaGraphics/Gtk.jl/issues/428