2007年10月02日
open-uriは便利だけど、timeoutの設定とかはできないので自前で準備する必要がある。
知っている人にはそれが大したものではないのだろうけど、調べるとなると若干時間が必要なのでここにメモしておく。
単純なコードですがポイントは2つ。
この問題は以下を参照していただけると一番良くわかります。
http://www.ruby-lang.org/ja/man/?cmd=view;name=trap%3A%3Atimeout
RubyのThreadは、インタプリタがユーザモードで動くプリエンプティブな性質を作っているが、それを動かすC言語のレベルではノンプリエンプティブなので、gethostbynameはユーザモードで動くRubyのThread切り替えが機能しない。
timeout.rbは、RubyのThreadを起こしているので、gethostbynameがタイムアウトし、プリエンプティブな処理が可能になって初めてタイムアウトを捕捉するようになるわけです。
つまり、この手の機能はCのライブラリに実装を依存させず、Rubyで記述すれば解決する。
それが冒頭のrequire 'resolv-replace'になります。
この辺はコードを追いかけてみると面白いと思います。
rescueは省略して記述したりする事もあるかと思うのですが、この時StandardErrorのサブクラスなら何でも捕捉します。
実はTimeoutErrorはStandardErrorのサブクラスではないので、捕捉できないことになります。
知っている人にはそれが大したものではないのだろうけど、調べるとなると若干時間が必要なのでここにメモしておく。
open-uriにtimeoutを設定したコード:
require 'open-uri'
require "resolv-replace"
require 'timeout'
begin
timeout(1){
open(uri){|f| # ここで接続に時間がかかるケースを想定します
# 必要な処理
}
}
rescue TimeoutError
# 捕捉
end
単純なコードですがポイントは2つ。
1. gethostbynameの問題を解決するresolv-replace
この問題は以下を参照していただけると一番良くわかります。
http://www.ruby-lang.org/ja/man/?cmd=view;name=trap%3A%3Atimeout
RubyのThreadは、インタプリタがユーザモードで動くプリエンプティブな性質を作っているが、それを動かすC言語のレベルではノンプリエンプティブなので、gethostbynameはユーザモードで動くRubyのThread切り替えが機能しない。
timeout.rbは、RubyのThreadを起こしているので、gethostbynameがタイムアウトし、プリエンプティブな処理が可能になって初めてタイムアウトを捕捉するようになるわけです。
つまり、この手の機能はCのライブラリに実装を依存させず、Rubyで記述すれば解決する。
それが冒頭のrequire 'resolv-replace'になります。
この辺はコードを追いかけてみると面白いと思います。
2. rescue TimeoutErrorと捕捉するクラスを明記
rescueは省略して記述したりする事もあるかと思うのですが、この時StandardErrorのサブクラスなら何でも捕捉します。
実はTimeoutErrorはStandardErrorのサブクラスではないので、捕捉できないことになります。

