-
-
Notifications
You must be signed in to change notification settings - Fork 925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Infinite loop when using mutually-referential constants #2314
Comments
Weird. |
This one is a little tricky...there's some differences in constant-definition logic in JRuby. The problem is basically that when doing const_set, we do an additional step that sets the parent of a module/class that doesn't currently have a parent. MRI does not appear to have this logic, but if I remove it from setConstantCommon, a number of specs that do want to see parent updated properly no longer pass. |
Here's my diff. This patch isn't right and this fix isn't going to make 1.7.18 because it's too risky to change this day before release. diff --git a/core/src/main/java/org/jruby/RubyModule.java b/core/src/main/java/org/jruby/RubyModule.java
index c17f30e..19b2d20 100644
--- a/core/src/main/java/org/jruby/RubyModule.java
+++ b/core/src/main/java/org/jruby/RubyModule.java
@@ -3354,7 +3354,6 @@ public class RubyModule extends RubyObject {
RubyModule module = (RubyModule)value;
if (module != this && module.getBaseName() == null) {
module.setBaseName(name);
- module.setParent(this);
}
}
return value;
|
For anyone that attempts this bug... more facts.
|
I'm looking at this casually for 1.7.20 and wondering how synthetic this is. @jeremyevans Did you run into this with real code? |
FWIW, you have to ^Z and kill because IRB traps SIGINT. If you run this at command line it can be interrupted with ^C like a normal JVM program. |
I did run into this with real code. When building Roda, I originally wanted to use mutually referential constants. When you subclass
When that didn't work on JRuby, I switched it to use an instance variable/accessor to store a link from the request subclass to the roda subclass:
I would have preferred to use the mutually referential constant, as constant access is faster than a method call and the value really is constant (it won't ever change after being set). |
@jeremyevans Ok thanks, good to know this wasn't a purely synthetic case. I'm researching how MRI does this, having never actually looked into their code. Apparently they do the name discover when you request the module's name, rather than at assignment time like in JRuby and Rubinius. That could explain why they don't have the same issues. |
With @evanphx's help, I've discovered that .name on detached namespaces is orders of magnitude slower than namespaces reachable from Object, due to the fact that they have to keep re-searching for the module every time. require 'benchmark'
module B
module X
end
end
def a
c = Class.new
c2 = Class.new
c.class_eval 'A = c2'
c2.class_eval 'A = c'
c
end
c = a
x = B::X
loop do
puts 'named'
puts Benchmark.measure { 1_000_000.times { x.name } }
puts 'anon'
puts Benchmark.measure { 1_000_000.times { c.name } }
end And results:
We are going to fix this in some way, but if you have any namespaces unreachable from Object, you really shouldn't be calling |
@headius can you do the benchmark and call inspect on an instance to see if that is also orders of magnitude slower? |
What fast path use-cases could be affected by this? Exceptions inspect. Logging/IO will call inspect. Is this really not on top of something much slower in general? |
I wonder if JRuby+Truffle is doing something wrong here, then. Even without Graal the two run roughly the same speed. But clearly it's much slower in MRI and JRuby. |
And indeed, it looks like we get it wrong. I'll have to see about adding a RubySpec for that. |
@nirvdrum I think everyone except MRI is going to get this wrong unless we intend to do full-system search to build the namespace lazily. |
I filed https://bugs.ruby-lang.org/issues/11119 for the perf issue in MRI. |
This will get into 1.7.20, and 9k.rc1 (or whatever follows pre2). |
The following code appears to cause an infinite loop in JRuby 1.7.17 (OpenBSD) and 1.7.6 (Windows):
Calling that
a
method causes the CPU to go to 100%, and requires akill -9
to kill the process.The text was updated successfully, but these errors were encountered: