JRuby

Major divergence in threading behaviour between Ruby 1.8.6 and JRuby 1.1.6

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: JRuby 1.1.6
  • Fix Version/s: JRuby 1.2
  • Component/s: Core Classes/Modules
  • Labels:
    None
  • Environment:
    Ubuntu 8.04, Java 1.6.0_10, x86 32-bit, JRuby 1.1.6
  • Testcase included:
    yes
  • Number of attachments :
    0

Description

The following very simple test case works in Ruby 1.8.6pl111 and fails in JRuby 1.1.6:

require 'net/telnet'
require 'thread'

puts host=(ARGV[0] || 'london.ginkosoft.com')
puts port=Integer((ARGV[1] || 2007))

server = Net::Telnet.new('Host' => host, 'Port' => port, 'Waittime' => 0.1)

outputthread = Thread.new do
loop do
server.waitfor('Match' => /.*/) {|output| print output}
end
end

loop do
input = STDIN.gets
server.write input
end

outputthread.join

The error when running this with jruby happens after the STDIN.gets call:

java.nio.channels.spi.AbstractSelectableChannel:257:in `configureBlocking': java.nio.channels.IllegalBlockingModeException
from org.jruby.RubyIO:2765:in `select_static'
from org.jruby.RubyIO:2646:in `select'
from org.jruby.RubyIO$s_method_0_3$RUBYINVOKER$select:-1:in `call'
from org.jruby.internal.runtime.methods.JavaMethod$JavaMethodNoBlock:62:in `call'
from org.jruby.internal.runtime.methods.DynamicMethod:261:in `call'
from org.jruby.internal.runtime.methods.DynamicMethod:169:in `call'
from org.jruby.runtime.callsite.CachingCallSite:155:in `call'
from org.jruby.ast.CallTwoArgNode:59:in `interpret'
from org.jruby.ast.NewlineNode:101:in `interpret'
from org.jruby.ast.BlockNode:68:in `interpret'
from org.jruby.ast.WhileNode:128:in `interpret'
from org.jruby.ast.NewlineNode:101:in `interpret'
from org.jruby.ast.BlockNode:68:in `interpret'
from org.jruby.internal.runtime.methods.DefaultMethod:156:in `interpretedCall'
from org.jruby.internal.runtime.methods.DefaultMethod:133:in `call'
from org.jruby.internal.runtime.methods.DefaultMethod:246:in `call'
from org.jruby.runtime.callsite.CachingCallSite:280:in `cacheAndCall'
from org.jruby.runtime.callsite.CachingCallSite:116:in `call'
from junk:17:in `block_3$RUBY$_block_'
from junkBlockCallback$block_3$RUBY$_block_xx1:-1:in `call'
from org.jruby.runtime.CompiledBlock:100:in `yield'
from org.jruby.runtime.Block:120:in `yield'
from org.jruby.RubyKernel:1058:in `loop'
from org.jruby.RubyKernel$s_method_0_0$RUBYFRAMEDINVOKER$loop:-1:in `call'
from org.jruby.runtime.callsite.CachingCallSite:270:in `cacheAndCall'
from org.jruby.runtime.callsite.CachingCallSite:100:in `callIter'
from junk:15:in `_file_'
from junk:-1:in `_file_'
from junk:-1:in `load'
from org.jruby.Ruby:564:in `runScript'
from org.jruby.Ruby:467:in `runNormally'
from org.jruby.Ruby:340:in `runFromMain'
from org.jruby.Main:214:in `run'
from org.jruby.Main:100:in `run'
from org.jruby.Main:84:in `main'

Activity

Hide
Charles Oliver Nutter added a comment -

Fixed in 8893... but I really don't have a good test for this. I suppose you'd have to start up a server socket and use this stuff to hit it. If you feel like contributing a simple test, we'd appreciate it (and it would help ensure it doesn't break again).

Show
Charles Oliver Nutter added a comment - Fixed in 8893... but I really don't have a good test for this. I suppose you'd have to start up a server socket and use this stuff to hit it. If you feel like contributing a simple test, we'd appreciate it (and it would help ensure it doesn't break again).
Hide
Michael T. Richter added a comment -

Here is a self-contained script that doesn't rely on any outside servers to illustrate the bug. It is, however, stochastic: multiple executions are required to make it illustrate the problem case. I will continue hacking at this to have it more reliably show the issue and will also convert it to a proper unit test case sometime soon.

  1. This script is a test case for the JRuby bug reported at
  2. http://jira.codehaus.org/browse/JRUBY-3329 concerning threading and I/O
  3. clashes.

require 'net/telnet'
require 'socket'
require 'thread'

  1. Test data.
    ls = [
    'THIS IS TEST LINE 1',
    'THIS IS TEST LINE 2',
    'THIS IS TEST LINE 3',
    'THIS IS TEST LINE 4'
    ]

port = 6870

  1. The echo server.
    es = TCPServer.open(port)
    et = Thread.new() do
    s = es.accept
    ls.length.times do
    l = s.gets
    print "ECHOING: #{l}"
    s.puts "ECHOED: #{l}"
    end
    sleep 1
    end
  1. Telnet link to the echo server.
    s = Net::Telnet.new('Port' => port, 'Waittime' => 0.1)
  1. The reader thread.
    rt = Thread.new do
    ls.length.times do
    s.waitfor('Match' => /.*/) {|output| print output}
    end
    end
  1. The writer logic.
    ls.each do |l|
    s.puts l
    end

sleep 1

  1. Cleanup
    rt.join
    s.close

et.join
es.close

Show
Michael T. Richter added a comment - Here is a self-contained script that doesn't rely on any outside servers to illustrate the bug. It is, however, stochastic: multiple executions are required to make it illustrate the problem case. I will continue hacking at this to have it more reliably show the issue and will also convert it to a proper unit test case sometime soon.
  1. This script is a test case for the JRuby bug reported at
  2. http://jira.codehaus.org/browse/JRUBY-3329 concerning threading and I/O
  3. clashes.
require 'net/telnet' require 'socket' require 'thread'
  1. Test data. ls = [ 'THIS IS TEST LINE 1', 'THIS IS TEST LINE 2', 'THIS IS TEST LINE 3', 'THIS IS TEST LINE 4' ]
port = 6870
  1. The echo server. es = TCPServer.open(port) et = Thread.new() do s = es.accept ls.length.times do l = s.gets print "ECHOING: #{l}" s.puts "ECHOED: #{l}" end sleep 1 end
  1. Telnet link to the echo server. s = Net::Telnet.new('Port' => port, 'Waittime' => 0.1)
  1. The reader thread. rt = Thread.new do ls.length.times do s.waitfor('Match' => /.*/) {|output| print output} end end
  1. The writer logic. ls.each do |l| s.puts l end
sleep 1
  1. Cleanup rt.join s.close
et.join es.close
Hide
Wayne Meissner added a comment -

Here is a slightly simplified version that replicates the bug on 1.1.6

require 'net/telnet'
require 'socket'
require 'thread'

host, port = "localhost", 2007

tcp = TCPServer.new(port)
Thread.new do    
  begin
    client = tcp.accept
    loop do
      if !client.read(1)
        break
      end
      client.write_nonblock("a")
    end
  rescue Exception => ex
  end
end

server = Net::Telnet.new('Host' => host, 'Port' => port, 'Waittime' => 0.1)

outputthread = Thread.new do
  loop do
    server.waitfor('Match' => /.*/) {|output|}
  end
end

loop do
  server.write "b"
end

outputthread.join
Show
Wayne Meissner added a comment - Here is a slightly simplified version that replicates the bug on 1.1.6
require 'net/telnet'
require 'socket'
require 'thread'

host, port = "localhost", 2007

tcp = TCPServer.new(port)
Thread.new do    
  begin
    client = tcp.accept
    loop do
      if !client.read(1)
        break
      end
      client.write_nonblock("a")
    end
  rescue Exception => ex
  end
end

server = Net::Telnet.new('Host' => host, 'Port' => port, 'Waittime' => 0.1)

outputthread = Thread.new do
  loop do
    server.waitfor('Match' => /.*/) {|output|}
  end
end

loop do
  server.write "b"
end

outputthread.join
Hide
Charles Oliver Nutter added a comment -

I was unable to get any scripts to fail on 1.1.6, so this seems to be very hard to reproduce.

Show
Charles Oliver Nutter added a comment - I was unable to get any scripts to fail on 1.1.6, so this seems to be very hard to reproduce.

People

Vote (0)
Watch (2)

Dates

  • Created:
    Updated:
    Resolved: