简体   繁体   中英

golang qml (go-qml) cgo argument has Go pointer to Go pointer

Hi I am using the qml library for go to create UI's. I am trying to learn how to pass information from the UI (qml) to go to then "do something" with. QML is working if it is just a UI. I can run that fine when I do:

func main() {
    if len(os.Args) != 2 {
        fmt.Fprintf(os.Stderr, "usage: %s <qml file>\n", os.Args[0])
        os.Exit(1)
    }
    if err := qml.Run(run); err != nil {
        fmt.Fprintf(os.Stderr, "error: %v\n", err)
        os.Exit(1)
    }
}

func run() error {
    engine := qml.NewEngine()

    engine.On("quit", func() { os.Exit(0) })

    component, err := engine.LoadFile(os.Args[1])
    if err != nil {
        return err
    }
    window := component.CreateWindow(nil)
    window.Show()
    window.Wait()
    return nil
}

However when I add some code, to try and "learn" something from the UI I get the run time error:

panic: runtime error: cgo argument has Go pointer to Go pointer

The code I am adding is:

window.On("visibleChanged", func(visible bool) {
    if (visible) {
            fmt.Println("Width:", window.Int("width"))
    }
})

I am running "go version go1.6 darwin/amd64" on OSX El Capitan

Any ideas why? A google suggests this was an error in Go 1.6 Beta, but I am running the latest stable version (installed a couple of days ago).

If it's not a simple fix, can someone explain "why" this is occuring?

The problem is that when C code stores a Go pointer (in this case, a pointer to your callback function), the garbage collector cannot track that pointer in the C code, and may garbage collect the memory that the pointer is pointing to if no Go code is referencing it. This will cause the C code to crash when it attempts to access that memory. All the runtime knows is that the C code retained the pointer (that's why it can panic), but it doesn't know what the C code will do with it later and for how long it will keep it.

To avoid this, the trick used by most libraries was to hold on to a pointer in Go as well (eg in a global map), to ensure that the memory is protected from the garbage collector. go-qml uses this trick as well . This trick works, but the compiler and garbage collector have no idea that it does, they cannot verify that you're not making a mistake (eg deleting the Go pointer, while the C code still has its pointer).

With Go 1.6, the Go developers decided to be very strict about this, and they no longer allow C code to retain a Go pointer at all. However, if you disable this check, everything will still work in this case, because go-qml implements the trick correctly (it may break in the future however, eg if go implements a moving garbage collector).

Here's the issue about it: https://github.com/go-qml/qml/issues/170

Side note: In this specific case, what gets passed to C is a pointer to an interface{} , which itself contains a pointer to the function. That's why you get the error "cgo argument has Go pointer to Go pointer". The reason this isn't allowed is that it's more difficult to protect these pointers from the GC for the duration of the C call, and it's not worth it, so it's forbidden instead ( https://github.com/golang/go/issues/12416#issuecomment-136473697 ). However, even if this were allowed, the code would still be violating the rule about C code keeping a copy of the Go pointer. This isn't actually a problem in Go 1.6, since it doesn't implement a moving garbage collector, but the rules were made so that it can be implemented later.

If you're just playing around, I suggest trying with go 1.5.3. Go 1.6 introduced a different set of constraints on pointers to memory when using cgo, a more restrictive set, and it's possible some go packages that were designed for the older version of go are now breaking a go rule or two.

If this is the case, getting the older package to work with go 1.6, where C is allowed to call go closures, could be harder to fix. But I don't have first hand experience with that yet.

Thanks for all the help here. I have written up a beginners tutorial on using QML with Go. It can be viewed here. I will continually update it if I run into any more errors and find fixes. Thanks everyone for your help. QML/GO is an awesome combination.

https://golog.co/blog/article/Using_Go_with_QML_part_1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM