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

A

Alex Young

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,
 
R

Robert Klemme

Take the following code:

=A0require 'socket'
=A0require 'thread'

=A0server =3D TCPServer.new("localhost", 0)
=A0serv_thread =3D Thread.new{ server.accept }
=A0sleep(0.1)
=A0sock =3D TCPSocket.new("localhost", server.addr[1])

=A0q =3D Queue.new
=A0client_thread =3D Thread.new{
=A0 =A0begin
=A0 =A0 =A0sock.readline
=A0 =A0rescue StandardError =3D> e
=A0 =A0 =A0q.push(e)
=A0 =A0end
=A0}


=A0sleep(0.1) while client_thread.status =3D=3D "run"
=A0sock.close
=A0# client_thread.wakeup rescue nil
=A0err =3D q.pop
=A0client_thread.join
=A0fail "Failure!" unless err.is_a?(IOError)
=A0puts "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.

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 =3D TCPServer.new("localhost", 0)
serv_thread =3D Thread.new{ server.accept }
sleep(0.1)
sock =3D TCPSocket.new("localhost", server.addr[1])

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


sleep(0.1) while client_thread.status =3D=3D "run"
sock.close
# client_thread.wakeup rescue nil
err =3D 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 =3D Thread.new{
begin
sock.readline
nil
rescue StandardError =3D> e
e
end
}

err =3D client_thread.value

Kind regards

robert
 
A

Alex Young

Robert Klemme wrote in post #980828:
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,
 
A

Alex Young

Alex Young wrote in post #980861:
Robert Klemme wrote in post #980828:
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? :)
 
R

Robert Klemme

Alex Young wrote in post #980861:
Robert Klemme wrote in post #980828:
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? :)

Maybe the network stack behaves different on these different platforms.

Kind regards

robert
 
A

Alex Young

Robert Klemme wrote in post #981080:
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? :)

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.
 
A

Alex Young

Alex Young wrote in post #981138:
Robert Klemme wrote in post #981080:
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? :)

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 :)

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

Robert Klemme

Alex Young wrote in post #981138:
Robert Klemme wrote in post #981080:
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? :)

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 :)

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

Yep, Nobu is great! Thanks for filing the bug, Alex.

Cheers

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,565
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top