Does the code:
unsafe.Pointer(&du)
where du
is some interface
satisfy the rule 1 from the following list?
https://github.com/golang/go/issues/12416
Go code may pass a Go pointer to C provided that the Go memory to which it points does not contain any Go pointers. That rule must be preserved during C execution, in that the program must not store any Go pointers into that memory.
In other words, is the C-pointer to Go interface
considered as a "pointer to Go memory containing a Go pointers"?
UPDATE:
My issue is the following code:
type Receiver interface {
Signal()
}
func WowFunction(data []byte, du Receiver) {
C.wow_function( (*C.char)( unsafe.Pointer(&data[0]) ),
(C.size_t)(len(data)),
unsafe.Pointer(&du)) // this is my suspect
}
My idea is to make C-code calling the Receiver
's "method" Signal()
. I achieve this by exporting a Go-callback and passing that &du
as an argument to the callback:
//export go_callback
func go_callback(object_ptr unsafe.Pointer) {
object := *(*Receiver)(object_ptr);
object.Signal()
}
Is there another way to reach that?
In follow up to @JimB, yes, this is considered a pointer to Go memory containing a Go pointer, so in go >= 1.6 you're going to get "cgo argument has Go pointer to Go pointer" panics when you run your program.
If you want to use something like this at runtime, you can disable the panic by running your program with GODEBUG=cgocheck=0
.
I've actually written code like this before in go < 1.6 to wrap object-oriented handler code that's called asynchronously from threaded C code--so I don't think the use-case is that crazy.
One possible alternative to passing the pointer directly to the underlying C code is to make a threadsafe global registry for your handlers, so you would basically pass some index to the registry into the C code, receive it back in your callback, look up the handler for that index and then invoke the function on it.
These are a bit lengthy, but give an actual working example. If you want to just take a look at the registry implementation example, jump to the bottom.
Not the best C in the world, but here's a quick simplification of other code I've done this with before
Library Code
Makefile:
libtesting:
gcc -fPIC -c library/testing.c -o library/testing.o
gcc -dynamiclib library/testing.o -o library/libtesting.dylib
C Header:
/* library/testing.h */
#ifndef __TESTING_H__
#define __TESTING_H__
#include <pthread.h>
struct worker_node {
pthread_t worker;
struct worker_node *next;
};
// Structs for publisher
struct publisher {
void (* callback)(void *, char *, int);
void *context;
struct worker_node *workers;
};
struct publisher * publisher_new(void *, void (*)(void *, char *, int));
void publisher_cleanup(struct publisher *);
void publisher_finish(struct publisher *);
void publisher_publish(struct publisher *, char *, int);
#endif // __TESTING_H__
C Source:
/* library/testing.c */
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "testing.h"
struct message_wrapper {
void * context;
char * message;
int message_len;
void (* callback)(void *, char *, int);
};
struct publisher * publisher_new(void *context, void (*callback)(void *, char *, int)) {
struct publisher * self = (struct publisher *)malloc(sizeof(struct publisher));
assert(self);
assert(self->callback = callback);
self->context = context;
self->workers = NULL;
return self;
}
void publisher_cleanup(struct publisher * self) {
struct worker_node * next_node;
struct worker_node * node = self->workers;
while (node != NULL) {
next_node = node->next;
free(node);
node = next_node;
}
free(self);
self = NULL;
}
static void * publisher_worker_thread(void * args) {
struct message_wrapper * wrapper = (struct message_wrapper *)args;
wrapper->callback(wrapper->context, wrapper->message, wrapper->message_len);
free(wrapper->message);
free(wrapper);
pthread_exit(NULL);
}
void publisher_publish(struct publisher *self, char * message, int message_len) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
struct worker_node * new_node = (struct worker_node *)malloc(sizeof(struct worker_node));
new_node->next = self->workers;
self->workers = new_node;
struct message_wrapper *wrapper = (struct message_wrapper *)malloc(sizeof(struct message_wrapper));
wrapper->message = malloc(message_len);
memcpy(wrapper->message, message, message_len);
wrapper->message_len = message_len;
wrapper->context = self->context;
wrapper->callback = self->callback;
assert(!pthread_create(&self->workers->worker, &attr, publisher_worker_thread, (void *)wrapper));
}
void publisher_finish(struct publisher *self) {
struct worker_node * node = self->workers;
while (node != NULL) {
assert(!pthread_join(node->worker, NULL));
node = node->next;
}
}
Go Code
C wrapper:
/* testing_c.c */
#include "_cgo_export.h"
void cgo_callback_wrapper(void * context, char *message, int message_len) {
callbackWrapper(context, message, message_len);
}
Go:
package main
/*
#cgo LDFLAGS: -lpthread -Llibrary -ltesting
#include "library/testing.h"
extern void cgo_callback_wrapper(void * context, char *message, int message_len);
*/
import "C"
import (
"fmt"
"unsafe"
)
type Handler interface {
HandleMessage([]byte)
}
type Publisher struct {
base *C.struct_publisher
}
//export callbackWrapper
func callbackWrapper(cContext unsafe.Pointer, cMessage *C.char, cMessageSize C.int) {
handler := *(*Handler)(cContext)
message := C.GoBytes(unsafe.Pointer(cMessage), cMessageSize)
handler.HandleMessage(message)
}
func (p *Publisher) Publish(message []byte) {
cMessage := (*C.char)(unsafe.Pointer(&message[0]))
cMessageLen := C.int(len(message))
C.publisher_publish(p.base, cMessage, cMessageLen)
}
func CreatePublisher(handler Handler) *Publisher {
return &Publisher{
base: C.publisher_new(unsafe.Pointer(&handler), (*[0]byte)(C.cgo_callback_wrapper)),
}
}
func (p *Publisher) Finish() {
C.publisher_finish(p.base)
}
//////// EXAMPLE ////////
type TestHandler struct {
name string
}
func (h TestHandler) HandleMessage(message []byte) {
fmt.Printf("%s received %v", h.name, message)
}
func main() {
handler := TestHandler{name: "Test"}
publisher := CreatePublisher(handler)
publisher.Publish([]byte("test"))
publisher.Finish()
}
Disregard not cleaning up memory allocations...
If you put the go, c wrappers, and Makefile in the top-level directory, the "C library" in a folder named library and run make && go build
(on OS X, adjust the makefile compiler flags for Linux) you should get a panic of "cgo argument has Go pointer to Go pointer" using go >= 1.6 and no panic for go < 1.6 when running the binary. Building with go 1.6 and running with GODEBUG=cgocheck=0
should output Test received [116 101 115 116]
.
To make this example run under 1.6 without disabling cgocheck
add a registry kind of like this:
package main
/*
#cgo LDFLAGS: -lpthread -Llibrary -ltesting
#include "library/testing.h"
extern void cgo_callback_wrapper(void * context, char *message, int message_len);
*/
import "C"
import (
"fmt"
"sync"
"unsafe"
)
var registry map[int]Handler
var handlers int
var mutex = sync.Mutex{}
type Handler interface {
HandleMessage([]byte)
}
type Publisher struct {
base *C.struct_publisher
}
//export callbackWrapper
func callbackWrapper(cContext unsafe.Pointer, cMessage *C.char, cMessageSize C.int) {
mutex.Lock()
handler := registry[*(*int)(cContext)]
mutex.Unlock()
message := C.GoBytes(unsafe.Pointer(cMessage), cMessageSize)
handler.HandleMessage(message)
}
func (p *Publisher) Publish(message []byte) {
cMessage := (*C.char)(unsafe.Pointer(&message[0]))
cMessageLen := C.int(len(message))
C.publisher_publish(p.base, cMessage, cMessageLen)
}
func CreatePublisher(handler Handler) *Publisher {
mutex.Lock()
index := handlers
handlers++
if registry == nil {
registry = make(map[int]Handler)
}
registry[index] = handler
mutex.Unlock()
return &Publisher{
base: C.publisher_new(unsafe.Pointer(&index), (*[0]byte)(C.cgo_callback_wrapper)),
}
}
func (p *Publisher) Finish() {
C.publisher_finish(p.base)
}
//////// EXAMPLE ////////
type TestHandler struct {
name string
}
func (h TestHandler) HandleMessage(message []byte) {
fmt.Printf("%s received %v", h.name, message)
}
func main() {
handler := TestHandler{name: "Test"}
publisher := CreatePublisher(handler)
publisher.Publish([]byte("test"))
publisher.Finish()
}
Notice the addition of registry code in CreatePublisher
and callbackWrapper
and now instead of passing a pointer to an interface we now just pass a pointer to the index of the interface in the registry. Compile the same way and no more panics!
Here is another hacky to workaround this. It is not encouraged unless the content of whatever is passing around is not accessed by your C library (like C library only pass it around for callbacks).
Idea is to cast your unsafe.Pointer to C.ulonglong
Calling C
C.function(C.ulonglong(uintptr(unsafe.Pointer(&something))))
Casting it back
(*SomethingType)(unsafe.Pointer(uintptr(the_c_long_value)))