What should TCPSocket#close do to a blocked #readline?

Take the following code:

require ‘socket’
require ‘thread’

server = TCPServer.new(“localhost”, 0)
serv_thread = Thread.new{ server.accept }
sleep(0.1)
sock = TCPSocket.new(“localhost”, server.addr[1])

q = Queue.new
client_thread = Thread.new{
begin
sock.readline
rescue StandardError => e
q.push(e)
end
}

sleep(0.1) while client_thread.status == “run”
sock.close

client_thread.wakeup rescue nil

err = q.pop
client_thread.join
fail “Failure!” unless err.is_a?(IOError)
puts “Success!”

So, we’ve got one thread blocked on a #readline, and another thread
closes the socket out from under it. On 1.8.7-p302, this completes
successfully. On 1.9.2-p136, it hangs at q.pop.

If I uncomment the client_thread.wakeup call, both complete (although
the rescue is necessary to prevent a ThreadError on 1.8.7).

Is this expected? Which is “correct”?

Thanks,

Alex

On Thu, Feb 10, 2011 at 12:59 PM, Alex Y. [email protected]
wrote:

sleep(0.1) while client_thread.status == “run”
successfully. On 1.9.2-p136, it hangs at q.pop.
I cannot confirm this:

15:40:10 Temp$ ruby19 -v
ruby 1.9.2p136 (2010-12-25 revision 30365) [i386-cygwin]
15:40:50 Temp$ ruby19 s.rb
#<IOError: closed stream>
Success!
15:41:31 Temp$ cat s.rb
require ‘socket’
require ‘thread’

server = TCPServer.new(“localhost”, 0)
serv_thread = Thread.new{ server.accept }
sleep(0.1)
sock = TCPSocket.new(“localhost”, server.addr[1])

q = Queue.new
client_thread = Thread.new{
begin
sock.readline
q.push “OK”
rescue StandardError => e
p e
q.push(e)
end
}

sleep(0.1) while client_thread.status == “run”
sock.close

client_thread.wakeup rescue nil

err = q.pop

client_thread.join

fail “Failure!” unless err.is_a?(IOError)
puts “Success!”

15:41:36 Temp$

If I uncomment the client_thread.wakeup call, both complete (although
the rescue is necessary to prevent a ThreadError on 1.8.7).

Is this expected? Which is “correct”?

Hm… I would expect your 1.9.2 to also throw.

Btw, one thing is odd about your test case: you have two mechanisms to
synchronize threads - you read from the queue and you join
client_thread. If you really only want to track status of the thread
you could as well use Thread#value:

client_thread = Thread.new{
begin
sock.readline
nil
rescue StandardError => e
e
end
}

err = client_thread.value

Kind regards

robert

Robert K. wrote in post #980828:

On Thu, Feb 10, 2011 at 12:59 PM, Alex Y. [email protected]
wrote:

sleep(0.1) while client_thread.status == “run”
successfully. On 1.9.2-p136, it hangs at q.pop.
I cannot confirm this:

15:40:10 Temp$ ruby19 -v
ruby 1.9.2p136 (2010-12-25 revision 30365) [i386-cygwin]

$ ruby -v
ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-linux]

is mine. Might it be an architecture issue? I don’t have a 32bit machine
handy to compare.

15:40:50 Temp$ ruby19 s.rb
#<IOError: closed stream>
Success!
15:41:31 Temp$ cat s.rb
require ‘socket’
require ‘thread’

server = TCPServer.new(“localhost”, 0)
serv_thread = Thread.new{ server.accept }
sleep(0.1)
sock = TCPSocket.new(“localhost”, server.addr[1])

q = Queue.new
client_thread = Thread.new{
begin
sock.readline
q.push “OK”
rescue StandardError => e
p e
q.push(e)
end
}

sleep(0.1) while client_thread.status == “run”
sock.close

client_thread.wakeup rescue nil

err = q.pop

client_thread.join

fail “Failure!” unless err.is_a?(IOError)
puts “Success!”

15:41:36 Temp$

If I uncomment the client_thread.wakeup call, both complete (although
the rescue is necessary to prevent a ThreadError on 1.8.7).

Is this expected? Which is “correct”?

Hm… I would expect your 1.9.2 to also throw.

That’s what I thought, but rubinius doesn’t throw either, so I wasn’t
sure.

Btw, one thing is odd about your test case: you have two mechanisms to
synchronize threads - you read from the queue and you join
client_thread.

Yes, the #join was a bit of leftover that I forgot to trim.

If you really only want to track status of the thread
you could as well use Thread#value:

client_thread = Thread.new{
begin
sock.readline
nil
rescue StandardError => e
e
end
}

err = client_thread.value

That’s a bit neater, though. I like that.

Thanks,

Alex

Alex Y. wrote in post #980861:

Robert K. wrote in post #980828:

On Thu, Feb 10, 2011 at 12:59 PM, Alex Y. [email protected]
wrote:

sleep(0.1) while client_thread.status == “run”
successfully. On 1.9.2-p136, it hangs at q.pop.
I cannot confirm this:

15:40:10 Temp$ ruby19 -v
ruby 1.9.2p136 (2010-12-25 revision 30365) [i386-cygwin]

$ ruby -v
ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-linux]

Ok, so this also fails on i686-linux. Now, where’s the bug? :slight_smile:


Alex

Robert K. wrote in post #981080:

On Fri, Feb 11, 2011 at 1:01 PM, Alex Y. [email protected]
wrote:

ruby 1.9.2p136 (2010-12-25 revision 30365) [i386-cygwin]

$ ruby -v
ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-linux]

Ok, so this also fails on i686-linux. Now, where’s the bug? :slight_smile:

Maybe the network stack behaves different on these different platforms.

That doesn’t explain why 1.8.7 on Linux behaves the same as cygwin
1.9.2, though.


Alex

On Fri, Feb 11, 2011 at 1:01 PM, Alex Y. [email protected]
wrote:

ruby 1.9.2p136 (2010-12-25 revision 30365) [i386-cygwin]

$ ruby -v
ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-linux]

Ok, so this also fails on i686-linux. Now, where’s the bug? :slight_smile:

Maybe the network stack behaves different on these different platforms.

Kind regards

robert

Alex Y. wrote in post #981138:

Robert K. wrote in post #981080:

On Fri, Feb 11, 2011 at 1:01 PM, Alex Y. [email protected]
wrote:

ruby 1.9.2p136 (2010-12-25 revision 30365) [i386-cygwin]

$ ruby -v
ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-linux]

Ok, so this also fails on i686-linux. Now, where’s the bug? :slight_smile:

Maybe the network stack behaves different on these different platforms.

That doesn’t explain why 1.8.7 on Linux behaves the same as cygwin
1.9.2, though.

Well, that was quick :slight_smile:

http://redmine.ruby-lang.org/issues/show/4390


Alex

On Sat, Feb 12, 2011 at 5:06 PM, Alex Y. [email protected]
wrote:

Ok, so this also fails on i686-linux. Now, where’s the bug? :slight_smile:
http://redmine.ruby-lang.org/issues/show/4390
Yep, Nobu is great! Thanks for filing the bug, Alex.

Cheers

robert