В версии Go 1.5 появилась возможность собирать c-shared библиотеки. На Хабре была статья на эту тему, но там был достаточно примитивный пример. Оттого стало интересно, а можно ли прокинуть в Go callback функцию из под Ruby, чтобы дергать ее по мере необходимости ну или мало ли для чего еще. Погуглил что есть в FFI, что пишет документация в CGO на тему внешних функций и получился следующий код:
package main
import (
"unsafe"
"fmt"
"time"
)
import "C"
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
//export asay
func asay(str string) {
go say(str)
}
//export ssay
func ssay(str string) {
say(str)
}
//export remote
func remote(pfoo unsafe.Pointer) {
foo := *(*())(pfoo)
foo()
}
func main() {}
require 'ffi'
#typedef struct { char *p; GoInt n; } GoString;
module Go
extend FFI::Library
ffi_lib 'libadd.so'
class String < FFI::Struct
layout :p, :pointer,
:n, :int
end
attach_function :asay, [Go::String.by_value], :void
attach_function :ssay, [Go::String.by_value], :void
callback :f_function, [], :void
attach_function :remote, [:f_function], :void
def self.say(text, async = false)
btext = text.dup.force_encoding 'binary'
str = Go::String.new
str[:p] = FFI::MemoryPointer.from_string(btext)
str[:n] = btext.size
if async
self.asay str
else
self.ssay str
end
end
def self._remote(&block)
func = FFI::Function.new(:void, []) { puts 'remote call' }
puts func.inspect
self.remote func
end
end
Все компилируется, подключается, но при вызове функции получаю seg fault:
#<FFI::Function address=0x007f85006c8000 size=40>
unexpected fault address 0x0
fatal error: fault
[signal 0xb code=0x80 addr=0x0 pc=0x7f84fc1a2cd9]
goroutine 17 [running, locked to thread]:
runtime.throw(0x7f84fc2a8ac8, 0x5)
/var/user/.gvm/gos/go1.5/src/runtime/panic.go:527 +0x92 fp=0xc820038e88 sp=0xc820038e70
runtime.sigpanic()
/var/user/.gvm/gos/go1.5/src/runtime/sigpanic_unix.go:27 +0x2af fp=0xc820038ed8 sp=0xc820038e88
main.remote(0x7f85006c8000)
/var/user/www/goruby/lib.go:45 +0x19 fp=0xc820038ee0 sp=0xc820038ed8
runtime.call32(0x0, 0x7fff95f89198, 0x7fff95f89220, 0x8)
/var/user/.gvm/gos/go1.5/src/runtime/asm_amd64.s:437 +0x40 fp=0xc820038f08 sp=0xc820038ee0
runtime.cgocallbackg1()
/var/user/.gvm/gos/go1.5/src/runtime/cgocall.go:252 +0x110 fp=0xc820038f40 sp=0xc820038f08
runtime.cgocallbackg()
/var/user/.gvm/gos/go1.5/src/runtime/cgocall.go:177 +0xd9 fp=0xc820038fa0 sp=0xc820038f40
runtime.cgocallback_gofunc(0x0, 0x0, 0x0)
/var/user/.gvm/gos/go1.5/src/runtime/asm_amd64.s:801 +0x5d fp=0xc820038fb0 sp=0xc820038fa0
runtime.goexit()
/var/user/.gvm/gos/go1.5/src/runtime/asm_amd64.s:1696 +0x1 fp=0xc820038fb8 sp=0xc820038fb0
goroutine 18 [syscall, locked to thread]:
runtime.goexit()
/var/user/.gvm/gos/go1.5/src/runtime/asm_amd64.s:1696 +0x1
[1] 22080 abort ruby run.rb
Судя по строке:
main.remote(0x7f85006c8000)
Указатель передан верно. Есть предположение что все же как-то не так вызываю функцию, хотя в документации по FFI приводится именно такой способ передачи.