passing function pointer to the C code using cgo

后端 未结 2 1236
囚心锁ツ
囚心锁ツ 2021-02-06 04:04

Starting from Go v1.6 cgo changed the rules of passing pointers to the C code golang/go#12416. The example of invoking a dynamic Go callback from C code from the wiki doesn\'t

2条回答
  •  清歌不尽
    2021-02-06 05:01

    Starting from Go 1.6 cgo has new rules.

    Go code may pass a Go pointer to C provided that the Go memory to which it points does not contain any Go pointers.

    [source]

    These rules are checked during the runtime, and if violated program crashes. At the moment it is possible to disable checks using GODEBUG=cgocheck=0 environment variable. But in the future, that might stop working.

    So it is not possible anymore to pass a pointer to C code, if the memory to which it is pointing stores a Go function/method pointer. There are several ways to overcome this limitations, but I guess in most of them you should store a synchronized data structure which represents the correspondence between a certain id and the actual pointer. This way you can pass an id to the C code, not a pointer.

    The code solving this problem might look like this:

    package gocallback
    
    import (
        "fmt"
        "sync"
    )
    
    /*
    extern void go_callback_int(int foo, int p1);
    
    // normally you will have to define function or variables
    // in another separate C file to avoid the multiple definition
    // errors, however, using "static inline" is a nice workaround
    // for simple functions like this one.
    static inline void CallMyFunction(int foo) {
        go_callback_int(foo, 5);
    }
    */
    import "C"
    
    //export go_callback_int
    func go_callback_int(foo C.int, p1 C.int) {
        fn := lookup(int(foo))
        fn(p1)
    }
    
    func MyCallback(x C.int) {
        fmt.Println("callback with", x)
    }
    
    func Example() {
        i := register(MyCallback)
        C.CallMyFunction(C.int(i))
        unregister(i)
    }
    
    var mu sync.Mutex
    var index int
    var fns = make(map[int]func(C.int))
    
    func register(fn func(C.int)) int {
        mu.Lock()
        defer mu.Unlock()
        index++
        for fns[index] != nil {
            index++
        }
        fns[index] = fn
        return index
    }
    
    func lookup(i int) func(C.int) {
        mu.Lock()
        defer mu.Unlock()
        return fns[i]
    }
    
    func unregister(i int) {
        mu.Lock()
        defer mu.Unlock()
        delete(fns, i)
    }
    

    This code comes from the (updated) wiki page.

提交回复
热议问题