超时,系统超时和终结器不适用于基于FFI的function

我已经通过FFI为共享库函数(第三方函数)编写了一个包装器。 此共享库尝试与服务器建立连接。 在连接建立期间,当服务器无法访问时,第三方function等待3分钟。 为了避免在调用rails时我曾尝试使用以下超时但不幸的是它没有用。

  1. 本机超时
  2. 系统超时
  3. 终结者

注意:当我使用终结者时,由它创建的附加进程正在变为失效进程。

我使用的是ruby企业版1.8

似乎通过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? 

解决此问题的一种方法是分叉一个单独的进程来执行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调用。

在父进程中,我们只需要超时子进程,除非按时完成,否则将其终止。

有点清洁:

 require 'ffi' module Sleep extend FFI::Library ffi_lib FFI::Library::LIBC attach_function :sleep, [:uint], :void, :blocking => true end 

您可以将在C库中阻塞的函数标记为“阻塞”函数,FFI将围绕对这些函数的调用解锁GIL。 (需要ffi-1.0.x)。

例如

 require 'ffi' module Sleep extend FFI::Library ffi_lib FFI::Library::LIBC # Tell FFI that this function may block @blocking = true attach_function :sleep, [:uint], :void end 

@blocking不粘 – 您需要在每个要标记为阻止的’attach_function’调用之前设置它。

它不是一个100%肯定的解决方案。 中断在本机代码中被阻塞的function将适用于可中断的function(例如,睡眠,读取,写入等),但不适用于某些本机代码(例如,CPU密集型计算,也可能是许多其他类型)。

请注意:在ruby 1.8.x上,阻塞函数调用非常慢(与阻止1.9或JRuby上的调用相比)。