-1

I am using cgo to calling C method which accept struct pointer as below:

package main

/*
typedef struct Client{
    struct real_client c;
} Client;

int doSomething(struct real_client *c ) {
    ....
}

*/
import "C"
import "fmt"

type Client struct {
    client C.Client
}

func main() {
    cl := Client{}
    C.doSomething(&cl.client.c);   
 
    // no compile error

}

However, I get an error : cgo argument has Go pointer to Go pointer.

I am using go version go1.16.13.

Is that any way to do make it work ?

kiwi
  • 67
  • 1
  • 1
  • 6
  • This code does not compile as you cannot pass `&cl.client` as is—its type is incompatible with the argument. So I think you have elided something essential from your real code. – kostix Feb 25 '22 at 09:56
  • If I change the call to use `&cl.client.c` (and provided I have `struct real_client {};` as well) the code works for me when built with 1.16.12. Since I'm not sure you have come up with a proper [MCVE](https://meta.stackoverflow.com/a/367019/720999), I'll stop here for now. – kostix Feb 25 '22 at 09:59
  • My Mistake @kostix , it should be &cl.client.c, the code compile with no issue, but when it actual run, it get error as mentioned above. – kiwi Feb 25 '22 at 12:05
  • 1
    I still think you have removed something "interesting" from your real code. Here's [the gist](https://gist.github.com/kostix/4ef0cfddc4f06a06f3c9c3fa73cb278a) which demonstrates it works OK with the most strict `cgo` checks under `linux/amd64` and Go 1.16.13. – kostix Feb 25 '22 at 12:22
  • @kostix You are right, the sample code should work as expected. I had try to stripe down my code until the part which could reproduce error. My code slightly complicated, image "real_client " is a nested struct object, which when I call the C library code ( contain the initialization of nested struct object and all related method ) to initial the object, it work ok, but when the nested struct object reference pass to other method, it trigger the error "cgo argument has Go pointer to Go pointer", I guess maybe the library not work well with cgo. – kiwi Feb 26 '22 at 04:34
  • However, If i NOT put "real_client" under struct Client, but as a global variable, it work correctly. – kiwi Feb 26 '22 at 04:34
  • Well, have you actually pondered what that "Go pointer to Go pointer" really means? `cgo` has to cope with the fact Go features GC and has no way of "pinning" allocated memory blocks (marking them as not eligible for garbage collection). This means `cgo` must ensure it never "leaks" pointers to Go-allocated memory to the C side. So it's OK to pass a pointer to, say, an array of `int`s in a `cgo` call because even if the C side would copy the contents of the array, it'd copy pain, "dead" integers… – kostix Feb 26 '22 at 17:31
  • …Now suppose you pass a pointer to a memory block which contains a pointer to another memory block allocated by Go. If the C side copies that memory it also copies that pointer. Now when the `cgo` call returns, there are two problems: 1) The GC is only able to detect memory reachability from the stacks of the active goroutines; it does not know anything about C; so if it collects the memory a pointer to which is still retained by the C side, everything can happen: a classical "use after free" case; 2) If the Go devs were to allow such thing to happen, they would have precluded the GC… – kostix Feb 26 '22 at 17:34
  • …from ever implementing so-called "compaction"—where some allocated memory blocks are physically moved in memory to reduce heap fragmentation. Presently (Go ≤ 1.18) the GC does not implement it, but it may, eventually, if needed. Movement of memory blocks requires updating all the pointer variables which contain the address of that memory block. The runtime can do that on the Go side but it cannot do so to the pointers on the C side. – kostix Feb 26 '22 at 17:37
  • @kostix Thx for the explanation. My code suppose keep the pointer of C, however I still no clue why it could work under global variable. After some performance evaluation, seem like cgo is slower a lot compare to C. I am planning to create a REST api with C and see if this is better choice. ( also much easy to isolate difference technologies ) – kiwi Feb 27 '22 at 10:08

1 Answers1

-1

Is that any way to do make it work ?

You can set the environment variable GODEBUG to cgocheck=0.

See https://pkg.go.dev/cmd/cgo#hdr-Passing_pointers.

Keep in mind the mechanism exists to prevent C code accessing managed memory not pinned for the duration of the C function call. This can become relevant in the future when changes to Go's garbage collector pertaining memory relocation may be made.

Zyl
  • 2,690
  • 2
  • 22
  • 27