Details
-
Type:
Bug
-
Status:
Resolved
-
Priority:
Critical
-
Resolution: Fixed
-
Affects Version/s: JRuby 1.1.6
-
Fix Version/s: JRuby 1.7.0
-
Component/s: Interpreter
-
Labels:None
-
Environment:Hidesomehost:~# uname -a
Linux somehost 2.6.18-6-amd64 #1 SMP Fri Jun 6 05:24:08 UTC 2008 x86_64 GNU/Linux
somehost:~# jruby --version
jruby 1.1.6 (ruby 1.8.6 patchlevel 114) (2008-12-17 rev 8388) [amd64-java]
somehost:~# java -version
java version "1.6.0_10"
Java(TM) SE Runtime Environment (build 1.6.0_10-b33)
Java HotSpot(TM) 64-Bit Server VM (build 11.0-b15, mixed mode)
somehost:~#Showsomehost:~# uname -a Linux somehost 2.6.18-6-amd64 #1 SMP Fri Jun 6 05:24:08 UTC 2008 x86_64 GNU/Linux somehost:~# jruby --version jruby 1.1.6 (ruby 1.8.6 patchlevel 114) (2008-12-17 rev 8388) [amd64-java] somehost:~# java -version java version "1.6.0_10" Java(TM) SE Runtime Environment (build 1.6.0_10-b33) Java HotSpot(TM) 64-Bit Server VM (build 11.0-b15, mixed mode) somehost:~#
-
Testcase included:yes
-
Number of attachments :
Description
Save the following program as "stackoverflowError.rb"
#!/usr/bin/ruby
#
#
def foo
foo
end
def bar
begin
foo
rescue Exception => e
print "#{e} #{e.backtrace.join("\n")}"
rescue Error => e
print "#{e} #{e.backtrace.join("\n")}"
rescue => e
print "#{e} #{e.backtrace.join("\n")}"
end
end
def bar1
if ARGV[0] == "1"
Thread.new do
bar
end.join
else
Thread.new do
foo
end
sleep 2
end
end
def bar2
bar1
end
bar2
Then execute this program repeatedly using the command line
jruby -J-Djruby.compile.mode=OFF stackoverflowError.rb 1
An output like
Exception in thread "Thread-1" java.lang.StackOverflowError
at org.jruby.internal.runtime.methods.JavaMethod.preFrameOnly(JavaMethod.java:800)
at stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.call(stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.gen)
at org.jruby.internal.runtime.methods.CompiledMethod.call(CompiledMethod.java:216)
at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:73)
at stackoverflowError.method__0$RUBY$foo(stackoverflowError.rb:6)
at stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.call(stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.gen)
at org.jruby.internal.runtime.methods.CompiledMethod.call(CompiledMethod.java:216)
at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:73)
at stackoverflowError.method__0$RUBY$foo(stackoverflowError.rb:6)
at stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.call(stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.gen)
at org.jruby.internal.runtime.methods.CompiledMethod.call(CompiledMethod.java:216)
at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:73)
at stackoverflowError.method__0$RUBY$foo(stackoverflowError.rb:6)
[...]
would be expected.
However, you will see the following output, either (1)
stackoverflowError.rb:24:in `bar1': stack level too deep (SystemStackError)
from :1:in `initialize'
or (2)
Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: -1
at org.jruby.runtime.ThreadContext.popRubyClass(ThreadContext.java:519)
at org.jruby.runtime.ThreadContext.postYield(ThreadContext.java:1272)
at org.jruby.runtime.InterpretedBlock.post(InterpretedBlock.java:139)
at org.jruby.runtime.InterpretedBlock.yield(InterpretedBlock.java:194)
at org.jruby.runtime.BlockBody.call(BlockBody.java:64)
at org.jruby.runtime.BlockBody.call(BlockBody.java:70)
at org.jruby.runtime.Block.call(Block.java:116)
at org.jruby.RubyProc.call(RubyProc.java:205)
at org.jruby.RubyProc.call(RubyProc.java:187)
at org.jruby.internal.runtime.RubyRunnable.run(RubyRunnable.java:90)
at java.lang.Thread.run(Thread.java:619)
Both types of output are wrong and unexpected.
The output (1) indicated that there may be a SystemStackError, which is good, but it gives a wrong hint of where it may have happened (it could have happened either in "foo" or in "baz", however, it indicates that it has happened in "bar1", which is clearly wrong and even the wrong Thread.
The output (2) is a clear JRuby bug which is not supposed to happen. Note that, apparently, there is a race condition between output (1) and output (2), YMMV.
Note that the original output showing a java.lang.StackOverflowError is acceptable, but maybe also be incorrect, because the Java error is not converted into a Ruby error, making it unnecessarily hard to find where the infinite recursion is in the Ruby code. Ideally, matching sequences of Java stack frames should be folded into one Ruby stack frame, yielding a mixed language stack trace (which it really is).
Note also that the code in bar tries to catch the SystemStackError, but apparently it cannot catch this error, which is also a bug.
Note that, then executing the program repeatedly using
jruby -J-Djruby.compile.mode=OFF stackoverflowError.rb 2
(e.g. the argument is "2" and not "1"), either output (2) may happen, or it may also happen that there is no output at all. This is absolutely unacceptable, because this type of Error cannot be caught, and the thread affected just vanishes, without any traces of the thread, and without any possibility to debug this type of situation.
I can reproduce this bug in JRuby 1.1.6 as well as in JRuby 1.1.4, both on x86-64 and x86-32.
Because this bug is timing dependent, note that if you can only reproduce one type of output, try adding "-J-server" or "-J-client" to the command line to yield the other type of output.
Note that "-J-Djruby.compile.mode=OFF" is not just a theoretical consideration, this bug happens in production with default compile mode as well (I assume that not everything is immediately compiled).
A workaround is surely to eliminate the infinite recursion, however, pinpointing the exact cause of the infinite recursion in the first place is virtually impossible due to this bug.
Activity
| Field | Original Value | New Value |
|---|---|---|
| Assignee | Thomas E Enebo [ enebo ] | |
| Component/s | Interpreter [ 12272 ] |
| Description |
Save the following program as "stackoverflowError.rb" ---snip--- #!/usr/bin/ruby # # def foo foo end def bar begin foo rescue Exception => e print "#{e} #{e.backtrace.join("\n")}" rescue Error => e print "#{e} #{e.backtrace.join("\n")}" rescue => e print "#{e} #{e.backtrace.join("\n")}" end end def bar1 if ARGV[0] == "1" Thread.new do bar end.join else Thread.new do foo end sleep 2 end end def bar2 bar1 end bar2 ---snap--- Then execute this program repeatedly using the command line ---snip--- jruby -J-Djruby.compile.mode=OFF stackoverflowError.rb 1 ---snap--- An output like ---snip--- Exception in thread "Thread-1" java.lang.StackOverflowError at org.jruby.internal.runtime.methods.JavaMethod.preFrameOnly(JavaMethod.java:800) at stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.call(stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.gen) at org.jruby.internal.runtime.methods.CompiledMethod.call(CompiledMethod.java:216) at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:73) at stackoverflowError.method__0$RUBY$foo(stackoverflowError.rb:6) at stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.call(stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.gen) at org.jruby.internal.runtime.methods.CompiledMethod.call(CompiledMethod.java:216) at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:73) at stackoverflowError.method__0$RUBY$foo(stackoverflowError.rb:6) at stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.call(stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.gen) at org.jruby.internal.runtime.methods.CompiledMethod.call(CompiledMethod.java:216) at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:73) at stackoverflowError.method__0$RUBY$foo(stackoverflowError.rb:6) [...] ---snap--- would be expected. However, you will see the following output, either (1) ---snip--- stackoverflowError.rb:24:in `bar1': stack level too deep (SystemStackError) from :1:in `initialize' ---snap--- or (2) ---snip--- Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: -1 at org.jruby.runtime.ThreadContext.popRubyClass(ThreadContext.java:519) at org.jruby.runtime.ThreadContext.postYield(ThreadContext.java:1272) at org.jruby.runtime.InterpretedBlock.post(InterpretedBlock.java:139) at org.jruby.runtime.InterpretedBlock.yield(InterpretedBlock.java:194) at org.jruby.runtime.BlockBody.call(BlockBody.java:64) at org.jruby.runtime.BlockBody.call(BlockBody.java:70) at org.jruby.runtime.Block.call(Block.java:116) at org.jruby.RubyProc.call(RubyProc.java:205) at org.jruby.RubyProc.call(RubyProc.java:187) at org.jruby.internal.runtime.RubyRunnable.run(RubyRunnable.java:90) at java.lang.Thread.run(Thread.java:619) ---snap--- Both types of output are wrong and unexpected. The output (1) indicated that there may be a SystemStackError, which is good, but it gives a wrong hint of where it may have happened (it could have happened either in "foo" or in "baz", however, it indicates that it has happened in "bar1", which is clearly wrong and even the wrong Thread. The output (2) is a clear JRuby bug which is not supposed to happen. Note that, apparently, there is a race condition between output (1) and output (2), YMMV. Note that the original output showing a java.lang.StackOverflowError is acceptable, but maybe also be incorrect, because the Java error is not converted into a Ruby error, making it unnecessarily hard to find where the infinite recursion is in the Ruby code. Ideally, matching sequences of Java stack frames should be folded into one Ruby stack frame, yielding a mixed language stack trace (which it really is). Note also that the code in bar tries to catch the SystemStackError, but apparently it cannot catch this error, which is also a bug. Note that, then executing the program repeatedly using ---snip--- jruby -J-Djruby.compile.mode=OFF stackoverflowError.rb 2 ---snap--- (e.g. the argument is "2" and not "1"), either output (2) may happen, or it may also happen that there is no output at all. This is absolutely unacceptable, because this type of Error cannot be caught, and the thread affected just vanishes, without any traces of the thread, and without any possibility to debug this type of situation. I can reproduce this bug in JRuby 1.1.6 as well as in JRuby 1.1.4, both on x86-64 and x86-32. Because this bug is timing dependent, note that if you can only reproduce one type of output, try adding "-J-server" or "-J-client" to the command line to yield the other type of output. Note that "-J-Djruby.compile.mode=OFF" is not just a theoretical consideration, this bug happens in production with default compile mode as well (I assume that not everything is immediately compiled). A workaround is surely to eliminate the infinite recursion, however, pinpointing the exact cause of the infinite recursion in the first place is virtually impossible due to this bug. |
Save the following program as "stackoverflowError.rb" {noformat} #!/usr/bin/ruby # # def foo foo end def bar begin foo rescue Exception => e print "#{e} #{e.backtrace.join("\n")}" rescue Error => e print "#{e} #{e.backtrace.join("\n")}" rescue => e print "#{e} #{e.backtrace.join("\n")}" end end def bar1 if ARGV[0] == "1" Thread.new do bar end.join else Thread.new do foo end sleep 2 end end def bar2 bar1 end bar2 {noformat} Then execute this program repeatedly using the command line {noformat} jruby -J-Djruby.compile.mode=OFF stackoverflowError.rb 1 {noformat} An output like {noformat} Exception in thread "Thread-1" java.lang.StackOverflowError at org.jruby.internal.runtime.methods.JavaMethod.preFrameOnly(JavaMethod.java:800) at stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.call(stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.gen) at org.jruby.internal.runtime.methods.CompiledMethod.call(CompiledMethod.java:216) at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:73) at stackoverflowError.method__0$RUBY$foo(stackoverflowError.rb:6) at stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.call(stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.gen) at org.jruby.internal.runtime.methods.CompiledMethod.call(CompiledMethod.java:216) at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:73) at stackoverflowError.method__0$RUBY$foo(stackoverflowError.rb:6) at stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.call(stackoverflowErrorInvokermethod__0$RUBY$fooFixed0.gen) at org.jruby.internal.runtime.methods.CompiledMethod.call(CompiledMethod.java:216) at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:73) at stackoverflowError.method__0$RUBY$foo(stackoverflowError.rb:6) [...] {noformat} would be expected. However, you will see the following output, either (1) {noformat} stackoverflowError.rb:24:in `bar1': stack level too deep (SystemStackError) from :1:in `initialize' {noformat} or (2) {noformat} Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: -1 at org.jruby.runtime.ThreadContext.popRubyClass(ThreadContext.java:519) at org.jruby.runtime.ThreadContext.postYield(ThreadContext.java:1272) at org.jruby.runtime.InterpretedBlock.post(InterpretedBlock.java:139) at org.jruby.runtime.InterpretedBlock.yield(InterpretedBlock.java:194) at org.jruby.runtime.BlockBody.call(BlockBody.java:64) at org.jruby.runtime.BlockBody.call(BlockBody.java:70) at org.jruby.runtime.Block.call(Block.java:116) at org.jruby.RubyProc.call(RubyProc.java:205) at org.jruby.RubyProc.call(RubyProc.java:187) at org.jruby.internal.runtime.RubyRunnable.run(RubyRunnable.java:90) at java.lang.Thread.run(Thread.java:619) {noformat} Both types of output are wrong and unexpected. The output (1) indicated that there may be a SystemStackError, which is good, but it gives a wrong hint of where it may have happened (it could have happened either in "foo" or in "baz", however, it indicates that it has happened in "bar1", which is clearly wrong and even the wrong Thread. The output (2) is a clear JRuby bug which is not supposed to happen. Note that, apparently, there is a race condition between output (1) and output (2), YMMV. Note that the original output showing a java.lang.StackOverflowError is acceptable, but maybe also be incorrect, because the Java error is not converted into a Ruby error, making it unnecessarily hard to find where the infinite recursion is in the Ruby code. Ideally, matching sequences of Java stack frames should be folded into one Ruby stack frame, yielding a mixed language stack trace (which it really is). Note also that the code in bar tries to catch the SystemStackError, but apparently it cannot catch this error, which is also a bug. Note that, then executing the program repeatedly using {noformat} jruby -J-Djruby.compile.mode=OFF stackoverflowError.rb 2 {noformat} (e.g. the argument is "2" and not "1"), either output (2) may happen, or it may also happen that there is no output at all. This is absolutely unacceptable, because this type of Error cannot be caught, and the thread affected just vanishes, without any traces of the thread, and without any possibility to debug this type of situation. I can reproduce this bug in JRuby 1.1.6 as well as in JRuby 1.1.4, both on x86-64 and x86-32. Because this bug is timing dependent, note that if you can only reproduce one type of output, try adding "-J-server" or "-J-client" to the command line to yield the other type of output. Note that "-J-Djruby.compile.mode=OFF" is not just a theoretical consideration, this bug happens in production with default compile mode as well (I assume that not everything is immediately compiled). A workaround is surely to eliminate the infinite recursion, however, pinpointing the exact cause of the infinite recursion in the first place is virtually impossible due to this bug. |
| Status | Open [ 1 ] | Resolved [ 5 ] |
| Assignee | Charles Oliver Nutter [ headius ] | |
| Fix Version/s | JRuby 1.7.0 [ 18624 ] | |
| Resolution | Fixed [ 1 ] |