diamondburned/gotk4

Segmentation fault when calling widget functions inside a goroutine

Closed this issue · 4 comments

Hi,

if call a widget function inside a goroutine, i get segfaults. F.e.

list is *gtk.ListView (field in a global struct)

list.SetVisible(false)

works, but:

go func() {list.SetVisible(false)}()

breaks.

SIGSEGV: segmentation violation
PC=0x7e961aef472f m=0 sigcode=2 addr=0x7e961a2e8600
signal arrived during cgo execution

goroutine 1 gp=0xc0000061c0 m=0 mp=0xe78fc0 [syscall, locked to thread]:
runtime.cgocall(0x819ef0, 0xc000029de0)
	/usr/lib/go/src/runtime/cgocall.go:157 +0x4b fp=0xc000029db8 sp=0xc000029d80 pc=0x4ce18b
github.com/diamondburned/gotk4/pkg/gio/v2._Cfunc_g_application_run(0x21b84b0, 0x1, 0x21b9e60)
	_cgo_gotypes.go:13793 +0x4b fp=0xc000029de0 sp=0xc000029db8 pc=0x60a68b
github.com/diamondburned/gotk4/pkg/gio/v2.(*Application).Run.func3(0x21b84b0, 0x1, 0x21b9e60)
	/home/andrej/go/pkg/mod/github.com/diamondburned/gotk4/pkg@v0.2.2/gio/v2/gio.go:41611 +0x67 fp=0xc000029e10 sp=0xc000029de0 pc=0x628627
github.com/diamondburned/gotk4/pkg/gio/v2.(*Application).Run(0xc0001e9530, {0xc000016140, 0x1, 0x1})
	/home/andrej/go/pkg/mod/github.com/diamondburned/gotk4/pkg@v0.2.2/gio/v2/gio.go:41611 +0x1f1 fp=0xc000029eb8 sp=0xc000029e10 pc=0x628511
main.main()
	/home/andrej/Documents/walker/main.go:67 +0x2ef fp=0xc000029f50 sp=0xc000029eb8 pc=0x7c9caf
runtime.main()
	/usr/lib/go/src/runtime/proc.go:271 +0x29d fp=0xc000029fe0 sp=0xc000029f50 pc=0x50527d
runtime.goexit({})
	/usr/lib/go/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc000029fe8 sp=0xc000029fe0 pc=0x536ea1

goroutine 17 gp=0xc000006700 m=1 mp=0xc00008e008 [syscall, locked to thread]:
runtime.goexit({})
	/usr/lib/go/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc00009cfe8 sp=0xc00009cfe0 pc=0x536ea1

goroutine 2 gp=0xc000006c40 m=nil [force gc (idle)]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
	/usr/lib/go/src/runtime/proc.go:402 +0xce fp=0xc000088fa8 sp=0xc000088f88 pc=0x5056ce
runtime.goparkunlock(...)
	/usr/lib/go/src/runtime/proc.go:408
runtime.forcegchelper()
	/usr/lib/go/src/runtime/proc.go:326 +0xb3 fp=0xc000088fe0 sp=0xc000088fa8 pc=0x505533
runtime.goexit({})
	/usr/lib/go/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc000088fe8 sp=0xc000088fe0 pc=0x536ea1
created by runtime.init.6 in goroutine 1
	/usr/lib/go/src/runtime/proc.go:314 +0x1a

goroutine 3 gp=0xc000007180 m=nil [GC sweep wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
	/usr/lib/go/src/runtime/proc.go:402 +0xce fp=0xc000089780 sp=0xc000089760 pc=0x5056ce
runtime.goparkunlock(...)
	/usr/lib/go/src/runtime/proc.go:408
runtime.bgsweep(0xc0000220e0)
	/usr/lib/go/src/runtime/mgcsweep.go:278 +0x94 fp=0xc0000897c8 sp=0xc000089780 pc=0x4ef894
runtime.gcenable.gowrap1()
	/usr/lib/go/src/runtime/mgc.go:203 +0x25 fp=0xc0000897e0 sp=0xc0000897c8 pc=0x4e41e5
runtime.goexit({})
	/usr/lib/go/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc0000897e8 sp=0xc0000897e0 pc=0x536ea1
created by runtime.gcenable in goroutine 1
	/usr/lib/go/src/runtime/mgc.go:203 +0x66

goroutine 4 gp=0xc000007340 m=nil [GC scavenge wait]:
runtime.gopark(0xc0000220e0?, 0xada720?, 0x1?, 0x0?, 0xc000007340?)
	/usr/lib/go/src/runtime/proc.go:402 +0xce fp=0xc000089f78 sp=0xc000089f58 pc=0x5056ce
runtime.goparkunlock(...)
	/usr/lib/go/src/runtime/proc.go:408
runtime.(*scavengerState).park(0xe787e0)
	/usr/lib/go/src/runtime/mgcscavenge.go:425 +0x49 fp=0xc000089fa8 sp=0xc000089f78 pc=0x4ed289
runtime.bgscavenge(0xc0000220e0)
	/usr/lib/go/src/runtime/mgcscavenge.go:653 +0x3c fp=0xc000089fc8 sp=0xc000089fa8 pc=0x4ed81c
runtime.gcenable.gowrap2()
	/usr/lib/go/src/runtime/mgc.go:204 +0x25 fp=0xc000089fe0 sp=0xc000089fc8 pc=0x4e4185
runtime.goexit({})
	/usr/lib/go/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc000089fe8 sp=0xc000089fe0 pc=0x536ea1
created by runtime.gcenable in goroutine 1
	/usr/lib/go/src/runtime/mgc.go:204 +0xa5

goroutine 5 gp=0xc000007c00 m=nil [finalizer wait]:
runtime.gopark(0xc000088648?, 0x4d7d85?, 0xa8?, 0x1?, 0xc0000061c0?)
	/usr/lib/go/src/runtime/proc.go:402 +0xce fp=0xc000088620 sp=0xc000088600 pc=0x5056ce
runtime.runfinq()
	/usr/lib/go/src/runtime/mfinal.go:194 +0x107 fp=0xc0000887e0 sp=0xc000088620 pc=0x4e31a7
runtime.goexit({})
	/usr/lib/go/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc0000887e8 sp=0xc0000887e0 pc=0x536ea1
created by runtime.createfing in goroutine 1
	/usr/lib/go/src/runtime/mfinal.go:164 +0x3d

goroutine 19 gp=0xc00020a000 m=nil [runnable, locked to thread]:
runtime.gcTrigger.test({0x0?, 0x0?, 0x0?})
	/usr/lib/go/src/runtime/mgc.go:568 +0xdc fp=0xc000099ae8 sp=0xc000099ae0 pc=0x4e459c
runtime.mallocgc(0x20, 0x929660, 0x1)
	/usr/lib/go/src/runtime/malloc.go:1307 +0x805 fp=0xc000099b70 sp=0xc000099ae8 pc=0x4d7b45
runtime.newobject(0x8d82c0?)
	/usr/lib/go/src/runtime/malloc.go:1390 +0x25 fp=0xc000099b98 sp=0xc000099b70 pc=0x4d7d85
github.com/KarpelesLab/weak.(*Map[...]).Set(0xaec080, 0x235f2f0, 0xc000331060)
	/home/andrej/go/pkg/mod/github.com/!karpeles!lab/weak@v0.1.1/map.go:58 +0xc5 fp=0xc000099bf8 sp=0xc000099b98 pc=0x5dd145
github.com/diamondburned/gotk4/pkg/core/intern.makeWeak(0x235f2f0)
	/home/andrej/go/pkg/mod/github.com/diamondburned/gotk4/pkg@v0.2.2/core/intern/intern.go:373 +0x119 fp=0xc000099c88 sp=0xc000099bf8 pc=0x5dc239
github.com/diamondburned/gotk4/pkg/core/intern.goToggleNotify(0xc000099d48?, 0x235f2f0, 0x1)
	/home/andrej/go/pkg/mod/github.com/diamondburned/gotk4/pkg@v0.2.2/core/intern/intern_export.go:25 +0x91 fp=0xc000099d30 sp=0xc000099c88 pc=0x5dc311
_cgoexp_68e4a5e86cd7_goToggleNotify(0x50e731?)
	_cgo_gotypes.go:220 +0x25 fp=0xc000099d58 sp=0xc000099d30 pc=0x5dcd45
runtime.cgocallbackg1(0x5dcd20, 0x7e95ce9ff8e0, 0x0)
	/usr/lib/go/src/runtime/cgocall.go:403 +0x2a5 fp=0xc000099e18 sp=0xc000099d58 pc=0x4ce8c5
runtime.cgocallbackg(0x5dcd20, 0x7e95ce9ff8e0, 0x0)
	/usr/lib/go/src/runtime/cgocall.go:322 +0x136 fp=0xc000099e90 sp=0xc000099e18 pc=0x4ce576
runtime.cgocallbackg(0x5dcd20, 0x7e95ce9ff8e0, 0x0)
	<autogenerated>:1 +0x29 fp=0xc000099eb8 sp=0xc000099e90 pc=0x539289
runtime.cgocallback(0xc000099f18, 0x4ce1b5, 0x7ff7a0)
	/usr/lib/go/src/runtime/asm_amd64.s:1079 +0xcc fp=0xc000099ee0 sp=0xc000099eb8 pc=0x536c4c
runtime.systemstack_switch()
	/usr/lib/go/src/runtime/asm_amd64.s:474 +0x8 fp=0xc000099ef0 sp=0xc000099ee0 pc=0x534e88
runtime.cgocall(0x7ff7a0, 0xc000099f50)
	/usr/lib/go/src/runtime/cgocall.go:175 +0x75 fp=0xc000099f28 sp=0xc000099ef0 pc=0x4ce1b5
github.com/diamondburned/gotk4/pkg/gtk/v4._Cfunc_gtk_widget_set_visible(0x223e7c0, 0x0)
	_cgo_gotypes.go:57876 +0x45 fp=0xc000099f50 sp=0xc000099f28 pc=0x705405
github.com/diamondburned/gotk4/pkg/gtk/v4.(*Widget).SetVisible.func1(0x223e7c0, 0x0)
	/home/andrej/go/pkg/mod/github.com/diamondburned/gotk4/pkg@v0.2.2/gtk/v4/gtk.go:117989 +0x45 fp=0xc000099f80 sp=0xc000099f50 pc=0x74cc85
github.com/diamondburned/gotk4/pkg/gtk/v4.(*Widget).SetVisible(0xc0000b2200, 0x0)
	/home/andrej/go/pkg/mod/github.com/diamondburned/gotk4/pkg@v0.2.2/gtk/v4/gtk.go:117989 +0x45 fp=0xc000099fa8 sp=0xc000099f80 pc=0x74cc05
github.com/abenz1267/walker/ui.process.func1(0xc00032b890)
	/home/andrej/Documents/walker/ui/interactions.go:291 +0x2a fp=0xc000099fc8 sp=0xc000099fa8 pc=0x7c620a
github.com/abenz1267/walker/ui.process.gowrap1()
	/home/andrej/Documents/walker/ui/interactions.go:293 +0x24 fp=0xc000099fe0 sp=0xc000099fc8 pc=0x7c3e04
runtime.goexit({})
	/usr/lib/go/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc000099fe8 sp=0xc000099fe0 pc=0x536ea1
created by github.com/abenz1267/walker/ui.process in goroutine 1
	/home/andrej/Documents/walker/ui/interactions.go:290 +0x85

rax    0xfa1e0ff3
rbx    0x239d1f0
rcx    0xd
rdx    0x1
rdi    0x7e961a2e8600
rsi    0x7e961a0d54b0
rbp    0x2e1ec10
rsp    0x7ffebf39da18
r8     0x212a010
r9     0x7
r10    0x2e46170
r11    0x855b42760f7f0183
r12    0x7e961a0d54b0
r13    0x2e40f90
r14    0x1
r15    0x2e1ec10
rip    0x7e961aef472f
rflags 0x10286
cs     0x33
fs     0x0
gs     0x0
exit status 2

Any idea why?

Regards

these seems to happen if the surrounding function exists, adding a waitgroup and waiting for the routine to finish fixes it. am i being dumb?

Your widget code must execute in the main UI thread. glib.IdleAdd can be used to run a function on the main thread.

So instead of

go func() {
    list.SetVisible(false)
}()

do this

go func() {
    // some code leads to a decision to change visibility of list
    ...

    glib.IdleAdd(func() {
        list.SetVisible(false)
    })
}()

The gotk4-examples repo has an example of using glib.IdleAdd.
https://github.com/diamondburned/gotk4-examples/blob/66e0fb4a758fc5ad75073319f67d5efc8d3630b4/gtk4/goroutines/main.go#L50

Aaahhhh.... thanks you a ton.

A bit more information about gtk and threads, and the need to use glib.IdleAdd.

https://docs.gtk.org/gtk4/question_index.html

How do I use GTK with threads?

GTK requires that all GTK API calls are made from the same thread in which the GtkApplication was created, or gtk_init() was called (the main thread).

If you want to take advantage of multi-threading in a GTK application, it is usually best to send long-running tasks to worker threads, and feed the results back to the main thread using g_idle_add() or GAsyncQueue. GIO offers useful tools for such an approach such as GTask.