I've got a newbie CGO question I was wondering if someone could help me with. When running with GODEBUG set to cgocheck=2 my application crashes with the following
write of Go pointer 0xc0003b72c0 to non-Go memory 0x7fefa0016810
fatal error: Go pointer stored into non-Go memory
The code causing the issue is
cArray := C.malloc(C.size_t(len(fd.Faces)) * C.size_t(unsafe.Sizeof(uintptr(0))))
defer C.free(unsafe.Pointer(cArray))
a := (*[1<<30 - 1]*C.struct_Box)(cArray)
for index, value := range fd.GetFaceRectangles() {
box := &C.struct_Box{
left: C.int(value.Min.X),
top: C.int(value.Min.Y),
right: C.int(value.Max.X),
bottom: C.int(value.Max.Y),
}
a[index] = box
}
cBoxCount := C.int(len(fd.Faces))
ret := C.facerec_compute_multi(rec.ptr, cImgData, cLen, &a[0], cBoxCount)
Specifically this row:
a[index] = box
I understand the memory for the array is allocated in C using malloc. I'm trying to add C Box to the array before passing it to a C function. Would the fix for this be for me to write a function in C which can receive the array and the items needed to create the struct and I do that part in C instead? I'm trying to minimise the number of calls to C so If I can create the array from Go that would be great. Really struggling with how to pass an array of data through to a function in C safely.
You've allocated C memory to hold many individual pointers. Each individual pointer can be set to a separate value. That's fine as far as it goes, but as you noted, the a[index] = box
is a problem, because box
itself holds a pointer to a Go-memory C.struct_Box
.
I'm trying to add C Box to the array before passing it to a C function.
To do that literally, you need a C.malloc
call. As written, you'll need one per C.struct_Box
instance.
Would the fix for this be for me to write a function in C which can receive the array and the items needed to create the struct and I do that part in C instead?
While you could try that, it seems ... suboptimal.
I'm trying to minimise the number of calls to C so If I can create the array from Go that would be great. Really struggling with how to pass an array of data through to a function in C safely.
C is never really safe 😀 but I suspect your best bet here is to make use of the way C implements "arrays", which is as a sort of poor-man's slice. (Or, perhaps more accurately, a Go slice is a "C array" done right.) Remember that a slice is implemented in Go as a three-element header:
which can then hold some run-time computed number n of items of some type. C's dynamic "arrays" work the same way, only without the safety of a proper slice header.
What this means is that you can write your C code—provided you have control of this C code—to take a pointer to a base of a storage area, and an int
or size_t
value that counts the number n of items in that area. The signature of such a function is:
void f(T *base, size_t n);
where n
is the number of items. (Add a second size_t
if appropriate, if you want to provide a capacity as well, though if the memory has capacity, and the function modifies it to contain a different number of items, then either the function must return the new number of items—i.e., f
would not be void
—or n
itself has to be provided by reference or something along those lines.)
In this case, if you can arrange the C code to take a pointer to the first of n
struct Box
-es (instead of the first of n
pointers to struct Box
-es), you can allocate that in a single C.malloc
call.
If you can't rework the C code, you're stuck with a minimum of two C.malloc
calls. You can, however, allocate all the struct Box
es in one C.malloc
again, and make them all adjacent—i.e., a C "array" / poor-man's-slice—and then make each pointer point to the next struct Box
:
cArray := C.malloc(C.size_t(len(fd.Faces)) * C.size_t(unsafe.Sizeof(uintptr(0))))
defer C.free(unsafe.Pointer(cArray))
cBoxes := C.malloc(C.size_t(len(fd.Faces)) * C.size_t(unsafe.Sizeof(C.struct_Box)))
defer C.free(unsafe.Pointer(cBoxes))
a := (*[1<<30 - 1]*C.struct_Box)(cArray)
b := (*[1<<30 - 1]C.struct_Box)(cBoxes)
for index, value := range fd.GetFaceRectangles() {
b[index] = C.struct_Box{
left: C.int(value.Min.X),
top: C.int(value.Min.Y),
right: C.int(value.Max.X),
bottom: C.int(value.Max.Y),
}
a[index] = &b[index]
}
cBoxCount := C.int(len(fd.Faces))
ret := C.facerec_compute_multi(rec.ptr, cImgData, cLen, &a[0], cBoxCount)
(I've tried to keep the code structure as similar as possible here, and this is quite untested since I don't have some of the parts.)
Aside: you have len(fd.Faces)
and fd.GetFaceRectangles()
and the code here assumes that the number of iterations of a for ... range fd.GetFaceRectangles()
is always len(fd.Faces)
. I'm calling this out because I have made the same assumption, without anything to back it other than your example.