Details
-
Type:
Bug
-
Status:
Closed
-
Priority:
Major
-
Resolution: Won't Fix
-
Affects Version/s: JRuby 1.6.7
-
Fix Version/s: None
-
Component/s: None
-
Labels:None
-
Environment:JRuby 1.6.7 as well as head on Mac OSX.
-
Testcase included:yes
-
Number of attachments :
Description
I recently wrote an example that uses multiple acceptor threads that all listen on the same listener socket. This works fine on MRI 1.9.3, however repeatedly fails in JRuby. The sample app can be found here: https://github.com/ryanlecompte/timeserver
One server is running, simply telnet localhost 3000 and you will see the error.
Server launched: 10 acceptors, 10 workers.
Exception in thread "RubyThread-34: /Users/ryan/fun/timeserver/lib/timeserver/server.rb:37" java.nio.channels.IllegalBlockingModeException
at java.nio.channels.spi.AbstractSelectableChannel.configureBlocking(AbstractSelectableChannel.java:257)
at org.jruby.util.io.SelectBlob.tidyUp(SelectBlob.java:352)
at org.jruby.util.io.SelectBlob.goForIt(SelectBlob.java:94)
at org.jruby.RubyIO.select_static(RubyIO.java:3398)
at org.jruby.RubyIO.select(RubyIO.java:3394)
at org.jruby.RubyIO$INVOKER$s$0$3$select.call(RubyIO$INVOKER$s$0$3$select.gen:65535)
at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:69)
at org.jruby.ast.CallManyArgsNode.interpret(CallManyArgsNode.java:59)
at org.jruby.ast.IfNode.interpret(IfNode.java:111)
at org.jruby.ast.NewlineNode.interpret(NewlineNode.java:104)
at org.jruby.ast.WhileNode.interpret(WhileNode.java:131)
at org.jruby.ast.NewlineNode.interpret(NewlineNode.java:104)
at org.jruby.evaluator.ASTInterpreter.INTERPRET_BLOCK(ASTInterpreter.java:112)
at org.jruby.runtime.Interpreted19Block.evalBlockBody(Interpreted19Block.java:209)
at org.jruby.runtime.Interpreted19Block.yield(Interpreted19Block.java:197)
at org.jruby.runtime.Interpreted19Block.call(Interpreted19Block.java:128)
at org.jruby.runtime.Block.call(Block.java:89)
at org.jruby.RubyProc.call(RubyProc.java:269)
at org.jruby.RubyProc.call(RubyProc.java:223)
at org.jruby.internal.runtime.RubyRunnable.run(RubyRunnable.java:101)
at java.lang.Thread.run(Thread.java:680)
Ok, finally circled around to this one.
I think your design may be flawed. Your code has N threads all listening for connect events on the same server socket. In MRI, this might work ok because only one thread can be woken up at a time. In JRuby, when the select event fires, potentially all of the threads wake up and try to accept the connection. It's not surprising that you get a cascade of errors as a result.
The error itself is caused because one thread "wins" at selecting and then tries to set the socket back into blocking mode. But other threads are still using the socket in their selectors, and you can't set a socket to blocking when it's still part of a selection event.
I think what you really want to do here is to have a single thread that's doing the select + accept_nonblock, and it parcels out the resulting client socket to a pool of threads waiting to handle requests. That will guarantee you only have one thread that's selecting on the server socket, and you still have N threads handling the actual requests.
I will clean up the error here, but I don't think it's possible to fix JRuby to work the way your code wants it to work.