FFI 経由の呼び出しは、Ruby のスケジューラを完全にブロックし、スレッド化を許可しないようです。これは、Ruby のグリーン スレッドに関連している可能性があります。
以下の例は、FFI を使用した場合の Ruby の同時実行の動作を示しています。
require 'ffi'
module Sleep
extend FFI::Library
ffi_lib FFI::Library::LIBC
attach_function :sleep, [:uint], :void
end
thread = Thread.start do
count = 1
while count <= 10
puts count
count += 1
sleep 0.5
end
end
puts "FFI sleep"
Sleep.sleep 5 # Everything blocks, second thread is run after sleep
puts "Ruby sleep"
sleep 5 # Scheduling works, other thread runs simultaneously
thread.join if thread.alive?
これを克服する 1 つの方法は、別のプロセスを fork して FFI 呼び出しを実行し、代わりにタイムアウトを設定することです。
require 'ffi'
require 'timeout'
module Sleep
extend FFI::Library
ffi_lib FFI::Library::LIBC
attach_function :sleep, [:uint], :void
end
child_pid = Process.fork do
Signal.trap("INT") do
exit
end
Sleep.sleep 5
exit
end
begin
Timeout::timeout(2) do
Process.wait(child_pid)
end
rescue Timeout::Error
Process.kill("INT", child_pid)
end
フォークされた子プロセスではINT
、タイムアウトに達した場合に穏やかにシャットダウンするための信号をリッスンし、もちろん FFI 呼び出しを実行することに関心があります。
親プロセスでは、子プロセスをタイムアウトして、時間内に終了しない限り強制終了するだけです。