On Tue, Sep 28, 2004 at 07:24:48AM +0900, leon breedt wrote:
> i've seen discussion previously regarding the safety of timeout,
> however, as i understand it, this usage is safe (@client is connected
> socket)
> 
> begin
>   timeout(30) do
>     line = @client.gets
>   end
> rescue Timeout::Error
>    @client.close
> end
> 
> am i wrong? all the real work is done elsewhere once complete requests
> have been received.

That's true. The problem is that the timeout thread raises an exception
asynchronously. In the above case your main work is just @client.gets, but
if it were doing something more important, that work could be interrupted.
In particular, even work within an 'ensure' block is interrupted. It's a
common pattern to use 'ensure' to do cleanup work, but if the timeout occurs
at just the wrong time, the cleanup may not be completed.

This program demonstrates it:

---- 8< ------------------------
require 'timeout'
def bar
  sleep(4)
  raise "wibble"  # optional
end

def foo
  bar
ensure
  puts "Cleanup started..."
  sleep(2)
  puts "Cleanup finished"
end

begin
  timeout(5) do
    foo
  end
rescue Exception => e
  p e
end
---- 8< ------------------------

You can try it both with and without the 'raise "wibble"' line. In both
cases the cleanup code in the 'ensure' block does not complete.

As a more realistic example, imagine some code like this:

timeout(30) do
  File.open("mylog","a") do |f|
    ... do stuff
  end
end

File.open with a block does several things:
  1. open the file
  2. yield it to the block
  3. close the file in an 'ensure' section

If you were really unlucky, and the timeout occurred at exactly the same
time as the 'ensure' section were being executed, then the file could remain
open.

At least, that's what I understand to be the crux of the issue. In many
cases it's not going to be a major concern. You may be able to rewrite the
code to make it safer by pushing the timeouts down to the lowest possible
level. The above example could be rewritten safely as:

File.open("mylog","a") do |f|
  timeout(30) do
    ... do stuff
  end
end

Regards,

Brian.