英文:
golang qml (go-qml) cgo argument has Go pointer to Go pointer
问题
嗨,我正在使用qml库来创建UI界面。我正在尝试学习如何将信息从UI(qml)传递给go,然后"做一些事情"。
如果只是UI,QML是可以工作的。当我运行以下代码时,它可以正常运行:
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
}
然而,当我添加一些代码,尝试从UI中"学习"一些东西时,我会得到运行时错误:
panic: runtime error: cgo argument has Go pointer to Go pointer
我添加的代码是:
window.On("visibleChanged", func(visible bool) {
if (visible) {
fmt.Println("Width:", window.Int("width"))
}
})
我在OSX El Capitan上运行"go version go1.6 darwin/amd64"。
有任何想法为什么会出现这个错误吗?谷歌搜索表明这是Go 1.6 Beta中的错误,但我正在运行最新的稳定版本(几天前安装的)。
如果这不是一个简单的修复,有人能解释一下为什么会发生这种情况吗?
英文:
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?
答案1
得分: 6
问题在于,当C代码存储Go指针(在这种情况下,指向回调函数的指针)时,垃圾收集器无法在C代码中跟踪该指针,并且如果没有Go代码引用它,可能会回收指针所指向的内存。当C代码尝试访问该内存时,这将导致C代码崩溃。运行时只知道C代码保留了指针(这就是为什么它会发生panic),但它不知道C代码将来会对指针做什么以及它将保留多长时间。
为了避免这种情况,大多数库使用的技巧是在Go中也保留一个指针(例如在全局映射中),以确保内存不受垃圾收集器的影响。go-qml也使用了这个技巧。这个技巧是有效的,但是编译器和垃圾收集器不知道它的存在,它们无法验证您是否犯了错误(例如删除了Go指针,而C代码仍然保留了它的指针)。
在Go 1.6中,Go开发人员决定对此非常严格,不再允许C代码保留Go指针。然而,如果您禁用此检查,在这种情况下仍然可以正常工作,因为go-qml正确实现了这个技巧(但是如果go实现了移动垃圾收集器,它可能在将来出现问题)。
这是相关问题的链接:https://github.com/go-qml/qml/issues/170
顺便提一下:在这种特定情况下,传递给C的是一个指向interface{}
的指针,它本身包含一个指向函数的指针。这就是为什么会出现错误“cgo argument has Go pointer to Go pointer”的原因。不允许这样做的原因是更难保护这些指针在C调用期间免受垃圾收集器的影响,并且这并不值得,因此被禁止了(https://github.com/golang/go/issues/12416#issuecomment-136473697)。然而,即使允许这样做,代码仍然会违反C代码保留Go指针的规则。在Go 1.6中,这实际上不是一个问题,因为它没有实现移动垃圾收集器,但是这些规则是为了以后可以实现移动垃圾收集器。
英文:
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 (e.g. 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 (e.g. 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, e.g. 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.
答案2
得分: 1
如果你只是在玩耍,我建议尝试使用Go 1.5.3版本。Go 1.6引入了一组不同的约束条件,用于在使用cgo时指向内存的指针,这是一组更严格的约束条件,有可能一些为旧版本Go设计的包现在违反了一两个Go规则。
如果是这种情况,让旧版本的包与Go 1.6一起工作可能会更难修复。但我还没有亲身经历过这个问题。
英文:
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.
答案3
得分: -2
感谢在这里提供的所有帮助。我已经撰写了一份关于使用QML和Go的初学者教程。可以在这里查看。如果我遇到更多错误并找到解决方法,我将不断更新它。感谢大家的帮助。QML/GO是一个很棒的组合。
https://golog.co/blog/article/Using_Go_with_QML_part_1
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论