Let's suppose I have a C function with signature
// myclib.h
void modify(double* ptr, int N);
which alters the memory pointed to by the argument pointer ptr
.
Is the following code safe in terms of Go's garbage collector and is the runtimer.Pinner
needed at all?
package main
// #cgo CFLAGS: -g -Wall
// #include "myclib.h"
import "C"
import (
"fmt"
"runtime"
)
func modifyWrapper(v []float64) {
ptr := (*C.double)(&v[0])
N := (C.int)(len(v))
pinner := runtime.Pinner{}
pinner.Pin(ptr)
C.modify(ptr, N)
pinner.Unpin()
}
func main() {
v := []float64{9.0, 2.0, 1.0, 4.0, 5.0}
modifyWrapper(v)
}
the term Go pointer means a pointer to memory allocated by Go and the term C pointer means a pointer to memory allocated by C. Whether a pointer is a Go pointer or a C pointer is a dynamic property determined by how the memory was allocated; it has nothing to do with the type of the pointer.
All Go pointers passed to C must point to pinned Go memory. Go pointers passed as function arguments to C functions have the memory they point to implicitly pinned for the duration of the call.
package main
/*
#cgo CFLAGS: -g -Wall
#include <stdio.h>
void modify(double* ptr, int N) {
if (!ptr || N <= 0) {
return;
}
printf("modify: %g %d\n", *ptr, N);
*ptr = 42;
printf("modify: %g %d\n", *ptr, N);
}
*/
import "C"
import (
"fmt"
"unsafe"
)
func modify(v []float64) {
ptr := (*C.double)(unsafe.SliceData(v))
N := (C.int)(len(v))
C.modify(ptr, N)
}
func main() {
v := []float64{9.0, 2.0, 1.0, 4.0, 5.0}
fmt.Println(v)
modify(v)
fmt.Println(v)
}
[9 2 1 4 5]
modify: 9 5
modify: 42 5
[42 2 1 4 5]