从go调用python回调指针

时间:2018-05-25 22:58:45

标签: python go

我收到此错误:

Ticker ticked
unexpected fault address 0xb01dfacedebac1e
fatal error: fault
[signal SIGSEGV: segmentation violation code=0x1 addr=0xb01dfacedebac1e pc=0x105c4152e]

goroutine 17 [running, locked to thread]:
runtime.throw(0x105c74358, 0x5)
/usr/local/go/src/runtime/panic.go:616 +0x81 fp=0xc420050d48 sp=0xc420050d28 pc=0x105bd6951
runtime.sigpanic()
/usr/local/go/src/runtime/signal_unix.go:395 +0x211 fp=0xc420050d98 sp=0xc420050d48 pc=0x105beabf1
main.callme(0x105baefc0)
/Users/xxx/go/src/test2/test2.go:29 +0xce fp=0xc420050e90 sp=0xc420050d98 pc=0x105c4152e
main._cgoexpwrap_f53316e445a2_callme(0x105baefc0)
_cgo_gotypes.go:45 +0x2b fp=0xc420050ea8 sp=0xc420050e90 pc=0x105c4144b
runtime.call32(0x0, 0x7ffeea2482b0, 0x7ffeea248348, 0x8)
/usr/local/go/src/runtime/asm_amd64.s:573 +0x3b fp=0xc420050ed8 sp=0xc420050ea8 pc=0x105bfe7eb
runtime.cgocallbackg1(0x0)
/usr/local/go/src/runtime/cgocall.go:316 +0x19c fp=0xc420050f58 sp=0xc420050ed8 pc=0x105bb284c
runtime.cgocallbackg(0x0)
/usr/local/go/src/runtime/cgocall.go:194 +0xda fp=0xc420050fc0 sp=0xc420050f58 pc=0x105bb261a
 runtime.cgocallback_gofunc(0x0, 0x0, 0x0, 0x0)
/usr/local/go/src/runtime/asm_amd64.s:826 +0x9b fp=0xc420050fe0 sp=0xc420050fc0 pc=0x105bffdbb
 runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:2361 +0x1 fp=0xc420050fe8 sp=0xc420050fe0 pc=0x105c00a31

go代码是:

    package main
    import "time"
    import "fmt"
    import "C"

    type convert func()

    //export callme
    func callme(fn convert) {
        doneChan := make(chan bool)
go func() {
    time.Sleep(time.Second * 5)
    doneChan <- true
}()
ticker := time.NewTicker(time.Millisecond * 500)
for {
    select {
    case <- ticker.C:
        fmt.Println("Ticker ticked")
        fn()
    case <- doneChan:
        fmt.Println("Done")
        return
  }
}
}
func main() {
ticker := time.NewTicker(time.Millisecond * 1000)
time.Sleep(time.Millisecond * 1500)
ticker.Stop()
fmt.Println("Ticker stopped")
}

python代码是:

from ctypes import *
def print_tick():
   print("hey")
   return 0
lib = cdll.LoadLibrary("./test2.so")


CMPFUNC = CFUNCTYPE(c_int)
cmp_func = CMPFUNC(print_tick)

callme = lib.callme
callme(cmp_func)

这只是一个测试代码,看看如何在python和go之间使用回调,如果可能的话。 理想情况下,python端会启动go代码以在后台运行。 go引擎正在接收消息流,并在收到消息后,go引擎将从python调用回调函数并将这些消息传递给python。

2 个答案:

答案 0 :(得分:1)

这里的问题是“ callme”的参数类型。 python代码使用ctypes为其提供C样式函数指针。您的Go代码会将其作为Go函数接收,而实际上不是。

首先,需要这样定义“ callme”:

func callme(fn C.convert)

然后需要定义C.convert。 Go文档https://golang.org/cmd/cgo/介绍了如何向Go源文件中添加一段C代码。

/*
typedef void (*convert) ();
*/
import "C"

由于不可能直接调用函数指针,因此需要创建一个将指针作为参数并调用它的C函数。

/*
typedef void (*convert) ();

static inline void call_c_func(convert ptr) {
    (ptr)();
}
*/
import "C"

现在C.call_c_func可用于调用从python接收到的回调函数,从而代替对fn()的直接调用。

//fn()
C.call_c_func(fn)

答案 1 :(得分:0)

如果不使用C作为中介从返回到python,这将无法工作。

go函数指针与python函数指针看起来不一样。当代码期望将fn视为go函数时,go代码崩溃。但那是一个python函数指针。粗体崩溃消息很有趣 - 他们已经清楚地预料到了这个错误,并使用0xb01dfacedebac1e作为指针值。

您可以尝试在python中使用callme的返回值。当一条消息进来时,从callme返回它,然后再从python call callme返回下一条消息。 python中的返回值需要是python可以理解的。

否则,如果可以从c调用到python,那么您可以选择使用c函数作为中介,并将该c函数作为回调提供。