<p>Hi, I'm playing around with exposing some Go struct in C (well Python really via ctypes) and I'm wondering my my struct isn't being deallocated correctly. I'm expecting that when I call dealloc in Python in the following that the struct is derefenced then garbage collected but that doesn't seem to be the case. Can anyone tell me why or point me towards reading a better understansing of this?</p>
<pre><code>package main
import "C"
import (
"fmt"
"unsafe"
)
//Table type for foo
type Table struct {
name string
}
//export tableNew
func tableNew() unsafe.Pointer {
return unsafe.Pointer(&Table{"Lala"})
}
//export tableDealloc
func tableDealloc(table unsafe.Pointer) {
t := *((*Table)(table))
fmt.Println(t.name)
}
func main() {}
</code></pre>
<p>Which is compiled with:</p>
<pre><code>go build -buildmode=c-shared -o foo.so foo.go
</code></pre>
<p>And used in Python with:</p>
<pre><code>from ctypes import cdll
import ctypes
qt = cdll.LoadLibrary('foo.so')
qt.tableNew.argtypes = []
qt.tableNew.restype = ctypes.c_void_p // void pointer
qt.tableDealloc.argtypes = [ctypes.c_void_p]
qt.tableDealloc.restype = None
a = qt.tableNew()
qt.tableDealloc(a)
> 'Lala'
qt.tableDealloc(a)
> 'Lala'
</code></pre>
<p>As you can see I can call dealloc multiple times where I would expect a second call to trigger a segmentation fault or something.</p>
<p>Any thoughts?</p>
<hr/>**评论:**<br/><br/>lunixbochs: <pre><p>The Go garbage collector doesn't track pointers passed to the C world (because that would be absurd). Your dealloc function is a no-op.</p>
<p>This is especially true in Go 1.6 which can <em>move</em> pointers without telling you and will likely throw errors about your code above.</p>
<p>The Go recommended way of keeping objects alive while allowing reference passing to C-land is to store a copy of the object in a map and delete it from the map to "deallocate it" (which the GC will do on its own time).</p>
<pre><code>var tableRef = make(map[uintptr]*Table)
//export tableNew
func tableNew() C.uintptr_t {
table := &Table{"Lala"}
utable := C.uintptr_t(uintptr(unsafe.Pointer(table)))
tableRef[utable] = table
return utable
}
//export tableDealloc
func tableDealloc(ptr C.uintptr_t) {
if table, ok := tableRef[ptr]; ok {
fmt.Println(table.name)
delete(tableRef[ptr])
}
}
</code></pre></pre>proudstar_: <pre><p>Thanks for the answer. Are there any implications of converting a uintptr to a c void pointer like this? Is there a more appropriate type in C?</p></pre>lunixbochs: <pre><p>The equivalent type in C is <code>uintptr_t</code>. It's just a number value that's guaranteed to be large enough to hold a pointer. You should be able to return this from a Go function using <code>C.uintptr_t</code>.</p>
<p>I also forgot you need to call <code>uintptr(unsafe.Pointer(obj))</code> instead of just <code>uintptr(obj)</code>. I've updated the original comment.</p></pre>: <pre><p>[deleted]</p></pre>proudstar_: <pre><p>I was hoping that by dereferncing the pointer in dealloc I would trigger Go garbage collector to free the memory.</p>
<p>Is there a way to force this?</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传