在使用CGo时,是否有一种安全的方法可以在C代码中保留对Go变量的引用?

huangapple go评论159阅读模式
英文:

Is there a safe way to hold on to a reference to a Go variable from C code using CGo?

问题

使用CGo将C代码与Go代码进行接口交互时,如果我在C端保留对Go变量的引用,是否存在垃圾回收器释放该对象的风险,或者垃圾回收器是否会看到C端管理的变量中的指针?

为了说明我的问题,考虑以下示例程序:

Go代码:

package main

/*
typedef struct _Foo Foo;
Foo *foo_new(void);
void foo_send(Foo *foo, int x);
int foo_recv(Foo *foo);
*/
import "C"

//export makeChannel
func makeChannel() chan int {
    return make(chan int, 1)
}

//export sendInt
func sendInt(ch chan int, x int) {
    ch <- x
}

//export recvInt
func recvInt(ch chan int) int {
    return <-ch
}

func main() {
    foo := C.foo_new()
    C.foo_send(foo, 42)
    println(C.foo_recv(foo))
}

C代码:

#include <stdlib.h>
#include "_cgo_export.h"

struct _Foo {
    GoChan ch;
};

Foo *foo_new(void) {
    Foo *foo = malloc(sizeof(Foo));
    foo->ch = makeChannel();
    return foo;
}

void foo_send(Foo *foo, int x) {
    sendInt(foo->ch, x);
}

int foo_recv(Foo *foo) {
    return recvInt(foo->ch);
}

foo_newfoo_send调用之间,foo->ch是否有被垃圾回收器释放的风险?如果有,是否有一种方法可以从C端固定Go变量,以防止在我持有对它的引用时被释放?

英文:

When using CGo to interface C code with Go, if I keep a reference to a Go variable on the C side, do I run the risk of that object being freed by the garbage collector or will the GC see the pointer in the variables managed by the C side?

To illustrate what I'm asking, consider the following sample program:

Go code:

package main

/*
typedef struct _Foo Foo;
Foo *foo_new(void);
void foo_send(Foo *foo, int x);
int foo_recv(Foo *foo);
*/
import &quot;C&quot;

//export makeChannel
func makeChannel() chan int {
	return make(chan int, 1)
}

//export sendInt
func sendInt(ch chan int, x int) {
	ch &lt;- x
}

//export recvInt
func recvInt(ch chan int) int {
	return &lt;-ch
}

func main() {
	foo := C.foo_new()
	C.foo_send(foo, 42)
	println(C.foo_recv(foo))
}

C code:

#include &lt;stdlib.h&gt;
#include &quot;_cgo_export.h&quot;

struct _Foo {
    GoChan ch;
};

Foo *foo_new(void) {
    Foo *foo = malloc(sizeof(Foo));
    foo-&gt;ch = makeChannel();
    return foo;
}

void foo_send(Foo *foo, int x) {
    sendInt(foo-&gt;ch, x);
}

int foo_recv(Foo *foo) {
    return recvInt(foo-&gt;ch);
}

Do I run the risk of foo-&gt;ch being freed by the garbage collector between the foo_new and foo_send calls? If so, is there a way to pin the Go variable from the C side to prevent it from being freed while I hold a reference to it?

答案1

得分: 3

根据gmp CGo示例

垃圾回收是一个大问题。在Go世界中,可以有指向C世界的指针,并在不再需要时释放这些指针。为了帮助处理这个问题,Go代码可以定义持有C指针的Go对象,并在这些Go对象上使用runtime.SetFinalizer函数。

对于C世界来说,要在其中有指向Go世界的指针要困难得多,因为Go垃圾回收器不知道C分配的内存。最重要的考虑是不限制未来的实现,所以规则是Go代码可以将Go指针传递给C代码,但必须单独安排Go在C完成后保留对该指针的引用。

所以我不确定你是否可以从C端固定变量,但你可以尝试使用runtime.SetFinalizer函数从Go端控制变量的垃圾回收。

希望能对你有所帮助。

英文:

According to the gmp CGo example :

>Garbage collection is the big problem. It is fine for the Go world to
have pointers into the C world and to free those pointers when they
are no longer needed. To help, the Go code can define Go objects
holding the C pointers and use runtime.SetFinalizer on those Go objects.

>It is much more difficult for the C world to have pointers into the Go
world, because the Go garbage collector is unaware of the memory
allocated by C. The most important consideration is not to
constrain future implementations, so the rule is that Go code can
hand a Go pointer to C code but must separately arrange for
Go to hang on to a reference to the pointer until C is done with it.

So I'm not sure if you can pin the variable from the C side, but you may be able to control the garbage collection of the variable from the Go side by using the runtime.SetFinalizer function.

Hope that helps.

huangapple
  • 本文由 发表于 2013年12月4日 06:31:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/20363072.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定