超时、系统超时&终止符不适用于基于 FFI 的功能

发布于 2024-10-21 06:46:49 字数 248 浏览 2 评论 0原文

我通过 FFI 为共享库函数(第三方函数)编写了一个包装器。该共享库尝试与服务器建立连接。在连接建立过程中,当服务器无法访问时,第三方功能会等待 3 分钟。为了避免这种情况,在调用 Rails 时我尝试使用以下超时,但不幸的是它不起作用。

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

注意:当我使用终结器时,由它创建的附加进程将变成已失效的进程。

我使用的是 ruby​​ 企业版 1.8

I have wrote a wrapper through FFI for a shared library function(third party function). This shared library tries to establish a connection with a server. During connection establishment when the server is not reachable third party function waits for 3 mins. In order to avoid that while calling in rails I used tried to use the following timeouts but unfortunately it did not work.

  1. Native Timeout
  2. System timeout
  3. Terminator

Note: when I use Terminator the additional process that is created by it was turning as defunct process.

I am using ruby enterprise version 1.8

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦言归人 2024-10-28 06:46:49

似乎通过 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 调用。

在父进程中,我们只需要让子进程超时,并杀死它,除非它按时完成。

Seems that calls via FFI block Ruby's scheduler completely, not allowing any threading. This may be related to Ruby's green threads.

The below example illustrates how Ruby concurrency behaves when using FFI:

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?

One way to overcome this, is to fork a separate process to carry out the FFI call, and have a timeout on that instead:

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

In the forked child process, all we're interested in doing, is listening for the INT signal to shutdown gently if the timeout is reached, and of course to do the FFI call.

In the parent process, we simply need to timeout the child process, and kill it unless it is done on time.

以酷 2024-10-28 06:46:49

更干净一点:

require 'ffi'

module Sleep
  extend FFI::Library

  ffi_lib FFI::Library::LIBC

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

A bit cleaner:

require 'ffi'

module Sleep
  extend FFI::Library

  ffi_lib FFI::Library::LIBC

  attach_function :sleep, [:uint], :void, :blocking => true
end
寄居者 2024-10-28 06:46:49

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

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

You can mark the functions which will block in the C library as 'blocking' functions, and FFI will unlock the GIL around calls to those functions. (Requires 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 is not sticky - you need to set it before every 'attach_function' call that you want to mark as blocking.

And its not a 100% sure-fire solution. Interrupting a function which is blocked in native code will work for functions that are interruptible (e.g. sleep, read, write, etc), but won't for some native code (e.g. cpu intensive computations, possibly many other types as well).

Be warned: on ruby 1.8.x, blocking function calls are really slow (compared to blocking calls on 1.9 or JRuby).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文