[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

timeout when listening with TCPServer

Shea Martin

3/28/2006 5:47:00 PM

I want to listen for connections for 2 seconds, then timeout. Do I have
to use the Timeout module?

~S
13 Answers

Bill Kelly

3/28/2006 6:47:00 PM

0

From: "Shea Martin" <null@void.0>
>
> I want to listen for connections for 2 seconds, then timeout. Do I have
> to use the Timeout module?

You could use select:

timeout_sec = 2.0
ios = select([@server], nil, nil, timeout_sec)
if ios
client = @server.accept
end

Note that you'll probably want to put the socket into
nonblocking mode. It's possible for select to return
"ready to read" and have accept still block, if the
client happens to disconnect in the small window
between the select and the accept.


Regards,

Bill




Eric Hodel

3/28/2006 7:20:00 PM

0

On Mar 28, 2006, at 9:48 AM, Shea Martin wrote:

> I want to listen for connections for 2 seconds, then timeout. Do I
> have to use the Timeout module?

That will be the easiest way.

Timeout.timeout 2 do
Thread.start server.accept do |s| new_connection s end
end

--
Eric Hodel - drbrain@segment7.net - http://blog.se...
This implementation is HODEL-HASH-9600 compliant

http://trackmap.rob...




Eric Hodel

3/28/2006 7:23:00 PM

0

On Mar 28, 2006, at 10:47 AM, Bill Kelly wrote:

> From: "Shea Martin" <null@void.0>
>>
>> I want to listen for connections for 2 seconds, then timeout. Do
>> I have to use the Timeout module?
>
> You could use select:
>
> timeout_sec = 2.0
> ios = select([@server], nil, nil, timeout_sec)
> if ios
> client = @server.accept
> end

Unfortunately this won't accept connections for two seconds. If a
client connects at .5 seconds then immediately disconnects you'll
need extra code to loop until you've consumed the full 2 seconds.

> Note that you'll probably want to put the socket into nonblocking
> mode. It's possible for select to return
> "ready to read" and have accept still block, if the
> client happens to disconnect in the small window between the select
> and the accept.

Yuck, timeout will give a cleaner solution with less work.

--
Eric Hodel - drbrain@segment7.net - http://blog.se...
This implementation is HODEL-HASH-9600 compliant

http://trackmap.rob...




Bill Kelly

3/28/2006 8:23:00 PM

0

From: "Eric Hodel" <drbrain@segment7.net>
>
> On Mar 28, 2006, at 9:48 AM, Shea Martin wrote:
>
>> I want to listen for connections for 2 seconds, then timeout. Do I
>> have to use the Timeout module?
>
> That will be the easiest way.
>
> Timeout.timeout 2 do
> Thread.start server.accept do |s| new_connection s end
> end

Oh, hey, cool. Using the thread there looks like it
ought to avoid the issue with Timeout firing in the
middle of ensure blocks and circumventing them?

I've been avoiding Timeout like "the plague" since
running into that behavior.


Regards,

Bill




Joel VanderWerf

3/28/2006 8:33:00 PM

0

Bill Kelly wrote:
> From: "Eric Hodel" <drbrain@segment7.net>
>>
>> On Mar 28, 2006, at 9:48 AM, Shea Martin wrote:
>>
>>> I want to listen for connections for 2 seconds, then timeout. Do I
>>> have to use the Timeout module?
>>
>> That will be the easiest way.
>>
>> Timeout.timeout 2 do
>> Thread.start server.accept do |s| new_connection s end
>> end
>
> Oh, hey, cool. Using the thread there looks like it
> ought to avoid the issue with Timeout firing in the
> middle of ensure blocks and circumventing them?
>
> I've been avoiding Timeout like "the plague" since
> running into that behavior.

But the timeout won't stop the thread.

$ cat timeout.rb
require 'timeout'

th = nil
begin
Timeout.timeout 2 do
th = Thread.new do
loop {puts "in thread"; sleep 1}
end
end
rescue TimeoutError
puts "timeout!"
end

sleep 5
p th.alive?

$ ruby timeout.rb
in thread
in thread
in thread
in thread
in thread
true

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407


Joel VanderWerf

3/28/2006 8:43:00 PM

0

Joel VanderWerf wrote:
> Bill Kelly wrote:
>> From: "Eric Hodel" <drbrain@segment7.net>
>>> On Mar 28, 2006, at 9:48 AM, Shea Martin wrote:
>>>
>>>> I want to listen for connections for 2 seconds, then timeout. Do I
>>>> have to use the Timeout module?
>>> That will be the easiest way.
>>>
>>> Timeout.timeout 2 do
>>> Thread.start server.accept do |s| new_connection s end
>>> end
>> Oh, hey, cool. Using the thread there looks like it
>> ought to avoid the issue with Timeout firing in the
>> middle of ensure blocks and circumventing them?
>>
>> I've been avoiding Timeout like "the plague" since
>> running into that behavior.
>
> But the timeout won't stop the thread.

Ignore my last post. Not stopping the thread is probably exactly the
behavior that you want. The timeout affects only the server.accept call
(and the Thread.start). So this code is allowing 2 sec for a connection,
but no placing a limit on what happens after that.

I think it might be clearer to write it as

session = Timeout.timeout 2 do
server.accept
end

Thread.start(session) do |s| new_connection s end

but that is only my unconsidered opinion.

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407


Bill Kelly

3/28/2006 8:48:00 PM

0

From: "Joel VanderWerf" <vjoel@path.berkeley.edu>
> Bill Kelly wrote:
>> From: "Eric Hodel" <drbrain@segment7.net>
>>>
>>> On Mar 28, 2006, at 9:48 AM, Shea Martin wrote:
>>>
>>>> I want to listen for connections for 2 seconds, then timeout. Do I
>>>> have to use the Timeout module?
>>>
>>> That will be the easiest way.
>>>
>>> Timeout.timeout 2 do
>>> Thread.start server.accept do |s| new_connection s end
>>> end
>>
>> Oh, hey, cool. Using the thread there looks like it
>> ought to avoid the issue with Timeout firing in the
>> middle of ensure blocks and circumventing them?
>>
>> I've been avoiding Timeout like "the plague" since
>> running into that behavior.
>
> But the timeout won't stop the thread.

I don't think it was supposed to. The #accept is
happening as an argument to Thread.start, so the thread
won't be started until the connection is accepted.

The only teeny race condition I can see is if the
timeout fires after server.accept but before
Thread.start is invoked. That would seem to leak
the socket - but the GC would normally reclaim it
eventually (except in cases where it wouldn't. :)


Regards,

Bill




Eric Hodel

3/28/2006 9:03:00 PM

0

On Mar 28, 2006, at 12:23 PM, Bill Kelly wrote:

> From: "Eric Hodel" <drbrain@segment7.net>
>>
>> On Mar 28, 2006, at 9:48 AM, Shea Martin wrote:
>>> I want to listen for connections for 2 seconds, then timeout. Do
>>> I have to use the Timeout module?
>> That will be the easiest way.
>> Timeout.timeout 2 do
>> Thread.start server.accept do |s| new_connection s end
>> end

Ooh, I forgot the loop around Thread.start :(

> Oh, hey, cool. Using the thread there looks like it
> ought to avoid the issue with Timeout firing in the
> middle of ensure blocks and circumventing them?
>
> I've been avoiding Timeout like "the plague" since
> running into that behavior.

You've timed out, so that's what's supposed to happen :)

Its easy to know you've timed out though:

require 'timeout'

Timeout.timeout 2 do
begin
ensure
begin
sleep
rescue Timeout::Error
puts 'caught!'
end
end
end

But, don't nest Timeout blocks without changing the raised
exception. Your code will eventually catch the wrong error and do
something bad.

$ ri Timeout.timeout
-------------------------------------------------------- Timeout#timeout
timeout(sec, exception=Error) {|if sec == nil or sec.zero?| ...}
------------------------------------------------------------------------

class MyTimeout < Timeout::Error; end

Timeout.timeout 2, MyTimeout do end

--
Eric Hodel - drbrain@segment7.net - http://blog.se...
This implementation is HODEL-HASH-9600 compliant

http://trackmap.rob...




Bill Kelly

3/28/2006 9:12:00 PM

0

From: "Joel VanderWerf" <vjoel@path.berkeley.edu>
>
> I think it might be clearer to write it as
>
> session = Timeout.timeout 2 do
> server.accept
> end
>
> Thread.start(session) do |s| new_connection s end

I think I'd do it that way too. (Probably adding a
rescue for Timeout::Error.)

I wonder if, there's still any possible race condition
in this version.

session = Timeout.timeout 2 do
server.accept
# If timeout fires "here" would the socket be leaked?
# (eventual probable reaping by GC notwithstanding)
end

Not that I would lose much sleep over it. But I try
to write code without any possibility of race conditions
whenever possible.

I'm guessing the race condition does exist there. Anyone
know whether assignments are atomic with regard to
exceptions? E.g.

begin
Timeout.timeout 2 do
session = server.accept
# ^^^ atomic?
end
rescue Timeout::Error
end

Any possibility of the timeout interrupting the assignment
to "session" there? If assignment is atomic then we
should be able to avoid leaking the socket.

(Just wondering... not too worried about it in practical
terms with this particular example.)


Regards,

Bill




Bill Kelly

3/28/2006 9:27:00 PM

0

From: "Eric Hodel" <drbrain@segment7.net>
>
>> Oh, hey, cool. Using the thread there looks like it
>> ought to avoid the issue with Timeout firing in the
>> middle of ensure blocks and circumventing them?
>>
>> I've been avoiding Timeout like "the plague" since
>> running into that behavior.
>
> You've timed out, so that's what's supposed to happen :)

Haha, yes... But it can be tricky...

Timeout.timeout 2 do
sleep 1.9
mutex.synchronize { @shared << "foo" }
end

...the timeout can fire in such a way that the
Thread#unlock in the ensure block of Thread#synchronize
is skipped - leaving the mutex locked.

I've been bitten by this... and it was pretty tough to
track down. (Of course, I was calling some harmless
looking method in the timeout block that just happened
to be using a mutex as part of its implementation...)


Regards,

Bill