Project

General

Profile

Actions

Bug #4681

closed

Timeout.timeout doesn't actually time out?

Added by meta (mathew murphy) over 13 years ago. Updated almost 13 years ago.

Status:
Rejected
Target version:
ruby -v:
1.9.2p180
Backport:
[ruby-core:36153]

Description

Documentation for Timeout says "A way of performing a potentially long-running operation in a thread, and terminating its execution if it hasn‘t finished by a fixed amount of time."

This doesn't actually seem to be what it does.

Example code:

#!/usr/bin/ruby

encoding: UTF-8

require 'open3'
require 'timeout'

puts "Time out after 2 seconds. Count them..."
result = Timeout.timeout(2) do
stdin, stdout, stderr = Open3.capture3("sleep 30")
output = stdout.read + "\n" + stderr.read
end

On my systems, this doesn't time out after 2 seconds. Instead, it runs for 30 seconds, and then throws an error saying it took longer than 2 seconds.

Updated by normalperson (Eric Wong) over 13 years ago

mathew murphy wrote:

This doesn't actually seem to be what it does.

Example code:

#!/usr/bin/ruby

encoding: UTF-8

require 'open3'
require 'timeout'

puts "Time out after 2 seconds. Count them..."
result = Timeout.timeout(2) do
stdin, stdout, stderr = Open3.capture3("sleep 30")
output = stdout.read + "\n" + stderr.read
end

On my systems, this doesn't time out after 2 seconds. Instead, it runs
for 30 seconds, and then throws an error saying it took longer than 2
seconds.

It's because open3 has an ensure block where it does Thread#join and
that waits forever, so the timeout thread raised to unblock the
main thread, and then it got stuck again inside the ensure block.

diff --git a/lib/open3.rb b/lib/open3.rb
index b65cb19..d335f9f 100644
--- a/lib/open3.rb
+++ b/lib/open3.rb
@@ -207,6 +207,7 @@ module Open3
begin
return yield(*result)
ensure

  •    p [ :ensure, __FILE__, __LINE__ ]
    
    parent_io.each{|io| io.close unless io.closed?}
    wait_thr.join
    end
    @@ -702,6 +703,7 @@ module Open3
    begin
    return yield(*result)
    ensure
  •    p [ :ensure, __FILE__, __LINE__ ]
    
    parent_io.each{|io| io.close unless io.closed?}
    wait_thrs.each {|t| t.join }
    end

You can work around it by having an extra timeout block (ugly):

puts "Time out after 2 seconds. Count them..."
Timeout.timeout(2) do
result = Timeout.timeout(2) do
stdin, stdout, stderr = Open3.capture3("sleep 30")
output = stdout.read + "\n" + stderr.read
end
end

But IMHO, timeout is a very fragile module and shouldn't be relied on.

--
Eric Wong

Updated by naruse (Yui NARUSE) over 13 years ago

  • Status changed from Open to Assigned
  • Assignee set to akr (Akira Tanaka)

Updated by nahi (Hiroshi Nakamura) over 13 years ago

  • Target version set to 1.9.3

Updated by kosaki (Motohiro KOSAKI) over 13 years ago

  • Status changed from Assigned to Rejected

I think Eric described the reason and workaround at commetn#1. IOW, it's spec unfortunately.

Updated by meta (mathew murphy) almost 13 years ago

Just noting for the benefit of anyone else passing by that I posted a way to do popen3 with timeout at
https://gist.github.com/1032297

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0