Returning pointer to struct from cgo to C cause panic: runtime error: cgo result has Go pointer

1,322 views
Skip to first unread message

eran....@gmail.com

unread,
Dec 18, 2018, 9:27:32 AM12/18/18
to golang-nuts
Hi,

I have to projects. One is cgo and one is C application. 
The cgo has a function which needs to return pointer to struct.
The C app receives this pointer and then calls other function in cgo with this pointer.
I build the cgo as shared lib: 

go build -o ebsdk.so -buildmode=c-shared

The cgo function is this:

//export Eb_TcpSecureStreamCreateListener
func
Eb_TcpSecureStreamCreateListener(listenIp string, listenPort int) unsafe.Pointer {
 
pmap
:= &ThreadSafePeerMap{
     peerMap: make(map[string]*Peer, 0),
     Mutex: sync.RWMutex{},
 
}

 server
:= &Server{
     ID
: cert.Subject.CommonName,
  listenAddress
: listenIp + ":" + strconv.Itoa(listenPort),
  libSecConn
: secConn,
 
PeerMap: pmap,
 
InboundChannel: make(chan *TcpMessage, channelSize),
 
OutgoingChannel: make(chan *TcpMessage, channelSize),
 
ClosedConnections: make(chan *TcpMessage, channelSize),
  shutdown
: false,
 
}

 server
.listener = secConn.NewListener(server.listenAddress, server.ID)

 
return unsafe.Pointer(server)
}

C code calls this cgo function with the returned unsafe.Pointer:

func Eb_TcpSecureStreamCloseListener(listenerHandle unsafe.Pointer) (retStatus int) {

 
if listenerHandle == nil {
 
return C.BadInput
 
}

 server
:= *(*Server)(listenerHandle)

 err
:= server.listener.Close()
 
if err != nil {
 
return C.Error
 
}

 
return C.Success
}


My problem is that Eb_TcpSecureStreamCreateListener() return statement cause panic: "runtime error: cgo result has Go pointer"

Any help would be great.
Thanks,


Ian Lance Taylor

unread,
Dec 18, 2018, 10:14:45 AM12/18/18
to eran....@gmail.com, golang-nuts
See https://golang.org/cmd/cgo/#hdr-Passing_pointers . "A Go function
called by C code may not return a Go pointer."

I'm not sure what to suggest because I'm not sure what you are trying
to do. One approach that works for some programs is to store the
pointer in a Go map that uses integer keys, and return the map key.
Since it looks like the struct can only be used from Go, the Go code
can use the map key to look up the struct. Of course you then need to
know when you can remove the struct from the map so that the garbage
collector can release it.

Ian

eran....@gmail.com

unread,
Dec 19, 2018, 8:44:55 AM12/19/18
to golang-nuts
solved it:

//export Eb_TcpSecureStreamCreateListener
func Eb_TcpSecureStreamCreateListener(listenIp string, listenPort int) unsafe.Pointer, retStatus {

 pmap
:= &ThreadSafePeerMap{
   
....
   
....
 
}

 server
:= Server{
   
....
   
....
 
}

 server
.listener = secConn.NewListener(server.listenAddress, server.ID)

 
//allocate memory on C heap. we send the server address in this pointer
 
//allocated memory is freed in Eb_TcpSecureStreamCloseListener()
 serverMemAlloc
:= C.malloc(C.size_t(unsafe.Sizeof(uintptr(0))))

 
//create array to write the address in the array
 a
:= (*[1]*Server)(serverMemAlloc)

 
//save the address in index 0 of the array
 a
[0] = &(*(*Server)(unsafe.Pointer(&server)))

 
return serverMemAlloc, C.Success
}

and the unsafe.Pointer receiver:

//export Eb_TcpSecureStreamCloseListener
func Eb_TcpSecureStreamCloseListener(listenerHandle unsafe.Pointer) (retStatus int) {

 
if listenerHandle == nil {
     
return C.BadInput
 
}


 server
:= *(*Server)((*[1]*Server)(listenerHandle)[0])
 err
:= server.listener.Close()

 
//freeing listenerHandle regardless of Close() returned status
 C
.free(listenerHandle)

 
if err != nil {
 
return C.Error
 
}

 
return C.Success
}

thanks,
Reply all
Reply to author
Forward
0 new messages