超时,系统超时&终结器不适用于基于FFI的功能

时间:2011-03-12 07:30:08

标签: ruby-on-rails ruby ffi

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

  1. 原生超时
  2. 系统超时
  3. 终止
  4. 注意:当我使用终结者时,由它创建的附加进程正在变为失效进程。

    我正在使用ruby enterprise version 1.8

3 个答案:

答案 0 :(得分:2)

似乎通过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呼叫。

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

答案 1 :(得分:1)

有点清洁:

require 'ffi'

module Sleep
  extend FFI::Library

  ffi_lib FFI::Library::LIBC

  attach_function :sleep, [:uint], :void, :blocking => true
end

答案 2 :(得分:0)

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

e.g。

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%肯定的解决方案。中断在本机代码中被阻塞的函数将适用于可中断的函数(例如,睡眠,读取,写入等),但不适用于某些本机代码(例如,cpu密集型计算,也可能是许多其他类型)。 / p>

警告:在ruby 1.8.x上,阻塞函数调用真的慢(与阻止1.9或JRuby上的调用相比)。