mathew murphy <meta / pobox.com> 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