Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 "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 code:

#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);
}

Do I run the risk of foo->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?

like image 383
James Henstridge Avatar asked Oct 20 '22 19:10

James Henstridge


1 Answers

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.

like image 98
Intermernet Avatar answered Oct 27 '22 10:10

Intermernet