# timeout

- gethostbyname で timeout できない

  timeout は Thread で実現されているため、C レベルで停止してしまう (Threadの切替えが発生しない)処理に対しては効果がありません。

  以下の例では、gethostbyname(およそ0.6秒処理に時間がかかっている) が終了し た直後((A)の箇所)で TimeoutError 例外があがっています。

      require 'timeout'
      require 'socket'

      start = Time.now
      begin
        timeout(0.1) {
          p TCPSocket.gethostbyname("www.ruby-lang.org")
          # (A)
        }
      ensure
        p Time.now - start
      end

      => ["helium.ruby-lang.org", [], 2, "210.251.121.214"]
         0.689331
         /usr/local/lib/ruby/1.6/timeout.rb:37: execution expired (TimeoutError)
                from -:6:in `timeout'
                from -:6

  Rubyで書かれたリゾルバを利用するという回避策があります。 `require "resolv-replace"` すると、`resolv.rb` で定義された、 リゾルバが利用されるようになります。

- Windows版 Ruby で timeout できない

  Win32版 ruby (cygwin、mingw、mswin32, bccwin32)では、以下の場合も Thread の切替えが起こらないために timeout できません。

      # Win32ネイティブ版(mingw, mswin32, bccwin32)

      require 'timeout'

      begin
        timeout(5) do
          $stdin.gets
        end
      rescue TimeoutError
        print "timeout\n"
      end

      # Cygwin版

      i = 0
      begin
        timeout(5) do
          while true
              puts (i+=1)
          end
        end
      rescue TimeoutError
        print "timeout\n"
      end

  Win32ネイティブ版では、$stdin.gets が、cygwin版 では、 puts が Thread の切替えを発生させないようです。*1

*1 正確には cygwinで は setitimer(2) にバグがある(らしい)ため Thread が切り替わらないようです。Cygwin版 ruby version 1.6.8 と 1.7.3 の最新では setitimer(2) を使わないようにすることでこの不具合が 修正されました[ruby-list:36058], ruby 1.6 feature

links: [Win32ネイティブ版] [bccwin32] [cygwin] [manual page] [mingw] [mswin32] [ruby 1.6 feature]
reverse links: [Rubyの落とし穴] [timeout.rb] [trap::Thread]