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.