golang-ui / nuklear

This project provides Go bindings for nuklear.h — a small ANSI C GUI library.
https://github.com/vurtun/nuklear
MIT License
1.57k stars 98 forks source link

error: signal arrived during external code execution #30

Open Loafter opened 6 years ago

Loafter commented 6 years ago

I creating gui application with multiple goroute functionality. Some of goroute execute another application (exec.Command) with parsing output. At this momen application have only one goroute for rendering and dispatch event. When i try dispatch event application raise exeption signal arrived during external code execution

github.com/golang-ui/nuklear/nk._Cfunc_nk_convert(0x307fd0, 0xe0aa70, 0x7a94930, 0x7a949b0, 0x7a958d0, 0xc000000000) github.com/golang-ui/nuklear/nk/_obj/_cgo_gotypes.go:3065 +0x54 github.com/golang-ui/nuklear/nk.NkConvert.func1(0x307fd0, 0xe0aa70, 0x7a94930, 0x7a949b0, 0x7a958d0, 0x20000) C:/Users/212402712/go/src/github.com/golang-ui/nuklear/nk/nk.go:155 +0x167 github.com/golang-ui/nuklear/nk.NkConvert(0x307fd0, 0xe0aa70, 0x7a94930, 0x7a949b0, 0xc04206fd18, 0xc04206fce0) C:/Users/212402712/go/src/github.com/golang-ui/nuklear/nk/nk.go:155 +0x68 github.com/golang-ui/nuklear/nk.NkPlatformRender(0x1, 0x80000, 0x20000) C:/Users/212402712/go/src/github.com/golang-ui/nuklear/nk/impl_glfw_gl3.go:99 +0x448

xlab commented 6 years ago

Hi, make sure the GFX goroutine (where GL initialisation, Nuklear initialisation are happening) is locked to the thread using runtime.LockOSThread()

If your loop is in the main goroutine (as in example), the following code is sufficient

func init() {
    runtime.LockOSThread()
}

If this doesn't help, I'd need the sample code to reproduce this error.

Loafter commented 6 years ago

this is minimal code for reproduce this effect please be patient, it hapen randomly. For me it take 20 times click for reproduce this effect Windows 7 x64 go1.9 package main

package main

import (
    "github.com/go-gl/gl/v3.2-core/gl"
    "github.com/go-gl/glfw/v3.2/glfw"
    "github.com/golang-ui/nuklear/nk"
    "log"
    "os/exec"
    "runtime"
)

const (
    maxVertexBuffer  = 512 * 1024
    maxElementBuffer = 128 * 1024
)

func main() {
    runtime.LockOSThread()
    defer runtime.UnlockOSThread()
    winWidth := 500
    winHeight := 200
    win := initWindow(winWidth, winHeight)
    win.MakeContextCurrent()
    log.Printf("glfw: created window %dx%d", winWidth, winHeight)
    ctx := InitNK(winWidth, winHeight, win)
    initFont(ctx)
    for !win.ShouldClose() {
        glfw.WaitEvents()
        nk.NkPlatformNewFrame()
        w, h := win.GetSize()
        bounds := nk.NkRect(0, 0, float32(w), float32(h))
        update := nk.NkBegin(ctx, "Demo", bounds, nk.WindowNoScrollbar)

        if update > 0 {
            nk.NkLayoutRowDynamic(ctx, 30, 2)
            nk.NkLabel(ctx, "Test:", nk.TextCentered)
            if nk.NkButtonLabel(ctx, "Click here") > 0 {
                out, err := exec.Command("ping", "ya.ru", "-n", "2").Output()
                log.Println(string(out), err)

            }
        }
        nk.NkEnd(ctx)
        glRender(win)
    }

}

func glRender(win *glfw.Window) {
    width, height := win.GetSize()
    gl.Viewport(0, 0, int32(width), int32(height))
    gl.Clear(gl.COLOR_BUFFER_BIT)
    nk.NkPlatformRender(nk.AntiAliasingOn, maxVertexBuffer, maxElementBuffer)
    win.SwapBuffers()
}
func initFont(ctx *nk.Context) {
    atlas := nk.NewFontAtlas()
    nk.NkFontStashBegin(&atlas)
    sansFont := nk.NkFontAtlasAddDefault(atlas, 30, nil)
    nk.NkFontStashEnd()
    if sansFont != nil {
        nk.NkStyleSetFont(ctx, sansFont.Handle())
    }
}
func InitNK(winWidth int, winHeight int, win *glfw.Window) *nk.Context {
    if err := gl.Init(); err != nil {
        log.Fatal(err)
    }
    gl.Viewport(0, 0, int32(winWidth), int32(winHeight))
    ctx := nk.NkPlatformInit(win, nk.PlatformInstallCallbacks)
    return ctx
}
func initWindow(winWidth int, winHeight int) *glfw.Window {
    if err := glfw.Init(); err != nil {
        log.Fatal(err)
    }
    glfw.WindowHint(glfw.ContextVersionMajor, 3)
    glfw.WindowHint(glfw.ContextVersionMinor, 2)
    glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
    glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
    win, err := glfw.CreateWindow(winWidth, winHeight, "DICOM CD Writer GUI", nil, nil)
    if err != nil {
        log.Fatal(err)
    }
    return win
}
Loafter commented 6 years ago

Any suggestions?

oov commented 6 years ago

I don't know the root cause but probably you can avoid it by using GOGC=off.

xlab commented 6 years ago

@oov not an option in long run.. but can be harmless to use for now :(

@Loafter sorry, got some work to do. I have to boot my Windows 10 machine, because I cannot reproduce the issue on OS X. Make sure you're on HEAD master. Because in the latest commit I disabled memory-related features that caused crashes on OS X.

I'm balancing between a crash and possible memory leaks, don't want to get those leaks back.

Loafter commented 6 years ago

same problem at this time i used Windows 10 x64 go1.9.1 windows/amd64 gcc (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.2.0 May be problem with posix thread model in gcc?

Exception 0xc0000005 0x8 0xc04200a2ca 0xc04200a2ca
PC=0xc04200a2ca
signal arrived during external code execution

github.com/golang-ui/nuklear/nk._Cfunc_nk_convert(0x5c81d40, 0x5c86e50, 0x5ccca00, 0x5ccc800, 0x5cc7f20, 0xc000000000)
    github.com/golang-ui/nuklear/nk/_obj/_cgo_gotypes.go:3065 +0x54
github.com/golang-ui/nuklear/nk.NkConvert.func1(0x5c81d40, 0x5c86e50, 0x5ccca00, 0x5ccc800, 0x5cc7f20, 0x20000)
    C:/Users/alex/go/src/github.com/golang-ui/nuklear/nk/nk.go:155 +0x167
github.com/golang-ui/nuklear/nk.NkConvert(0x5c81d40, 0x5c86e50, 0x5ccca00, 0x5ccc800, 0xc042031dd8, 0xc042020000)
    C:/Users/alex/go/src/github.com/golang-ui/nuklear/nk/nk.go:155 +0x68
github.com/golang-ui/nuklear/nk.NkPlatformRender(0x1, 0x80000, 0x20000)
    C:/Users/alex/go/src/github.com/golang-ui/nuklear/nk/impl_glfw_gl3.go:99 +0x448
main.glRender(0xc042072000)
    C:/Users/alex/go/src/crashtest/main.go:53 +0x7b
oov commented 6 years ago

I've found the root cause.

package main

import (
    "log"
    "os/exec"
    "runtime"

    "github.com/go-gl/gl/v3.2-core/gl"
    "github.com/go-gl/glfw/v3.2/glfw"
    "github.com/golang-ui/nuklear/nk"
)

const (
    maxVertexBuffer  = 512 * 1024
    maxElementBuffer = 128 * 1024
)

func main() {
    runtime.LockOSThread()
    defer runtime.UnlockOSThread()
    winWidth := 500
    winHeight := 200
    win := initWindow(winWidth, winHeight)
    win.MakeContextCurrent()
    log.Printf("glfw: created window %dx%d", winWidth, winHeight)
    ctx := InitNK(winWidth, winHeight, win)
    fontHandle := initFont(ctx)
    for !win.ShouldClose() {
        glfw.WaitEvents()
        nk.NkPlatformNewFrame()
        w, h := win.GetSize()
        bounds := nk.NkRect(0, 0, float32(w), float32(h))
        update := nk.NkBegin(ctx, "Demo", bounds, nk.WindowNoScrollbar)

        if update > 0 {
            nk.NkLayoutRowDynamic(ctx, 30, 2)
            nk.NkLabel(ctx, "Test:", nk.TextCentered)
            if nk.NkButtonLabel(ctx, "Click here") > 0 {
                out, err := exec.Command("ping", "ya.ru", "-n", "2").Output()
                log.Println(string(out), err)

            }
        }
        nk.NkEnd(ctx)
        glRender(win)
    }
    runtime.KeepAlive(fontHandle) // <-- We have to keep this.
}

func glRender(win *glfw.Window) {
    width, height := win.GetSize()
    gl.Viewport(0, 0, int32(width), int32(height))
    gl.Clear(gl.COLOR_BUFFER_BIT)
    nk.NkPlatformRender(nk.AntiAliasingOn, maxVertexBuffer, maxElementBuffer)
    win.SwapBuffers()
}
func initFont(ctx *nk.Context) *nk.UserFont {
    atlas := nk.NewFontAtlas()
    nk.NkFontStashBegin(&atlas)
    sansFont := nk.NkFontAtlasAddDefault(atlas, 30, nil)
    nk.NkFontStashEnd()
    if sansFont != nil {
        sansFontHandle := sansFont.Handle()
        nk.NkStyleSetFont(ctx, sansFontHandle)
        return sansFontHandle
    }
    return nil
}
func InitNK(winWidth int, winHeight int, win *glfw.Window) *nk.Context {
    if err := gl.Init(); err != nil {
        log.Fatal(err)
    }
    gl.Viewport(0, 0, int32(winWidth), int32(winHeight))
    ctx := nk.NkPlatformInit(win, nk.PlatformInstallCallbacks)
    return ctx
}
func initWindow(winWidth int, winHeight int) *glfw.Window {
    if err := glfw.Init(); err != nil {
        log.Fatal(err)
    }
    glfw.WindowHint(glfw.ContextVersionMajor, 3)
    glfw.WindowHint(glfw.ContextVersionMinor, 2)
    glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
    glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
    win, err := glfw.CreateWindow(winWidth, winHeight, "DICOM CD Writer GUI", nil, nil)
    if err != nil {
        log.Fatal(err)
    }
    return win
}
Loafter commented 6 years ago

oov looks like you solve my problem! Thanks a lot)

jakecoffman commented 6 years ago

I am having the same crash but in a different scenario, maybe this will shed some light on the issue (also I am stuck).

Here I add the example GUI to my project: https://github.com/jakecoffman/tanklets/commit/baf4e7e5e731e2e2ce0dbeb12fc2edffcf5dd8e8

When I run cmd/testlet, which starts the server and two clients, it crashes immediately or sometimes after I drag a window. Sometimes the other window crashes, not the one I am dragging in!

If I modify cmd/testlet to only launch one client then it doesn't crash... which seems to indicate there is some shared state between the two GUI processes somehow?!

Also I tried @oov's fix above but that didn't seem to help the crashing.

Any ideas? Happens on Windows 10 and OSX.

jakecoffman commented 6 years ago

My mistake, @oov's fix does fix it for me too!

tmm1 commented 6 years ago

So the fix is to add:

runtime.KeepAlive(fontHandle) // <-- We have to keep this.

Do you know why this is required?

oov commented 6 years ago

Because C references to Go's *nk.UserFont pointer that passed at nk.NkStyleSetFont. So it is necessary to prevent it being collected by GC.