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