diff --git a/src/org/jruby/RubyModule.java b/src/org/jruby/RubyModule.java index 10789b6..4da1b6c 100644 --- a/src/org/jruby/RubyModule.java +++ b/src/org/jruby/RubyModule.java @@ -2465,14 +2465,24 @@ public class RubyModule extends RubyObject { public IRubyObject getConstantAt(String name) { IRubyObject value = fetchConstant(name); - return value == UNDEF ? resolveUndefConstant(getRuntime(), name) : value; + if (value == UNDEF) { + resolveUndefConstant(getRuntime(), name); + value = fetchConstant(name); + } + + return value; } public IRubyObject fastGetConstantAt(String internedName) { assert internedName == internedName.intern() : internedName + " is not interned"; IRubyObject value = fastFetchConstant(internedName); - return value == UNDEF ? resolveUndefConstant(getRuntime(), internedName) : value; + if (value == UNDEF) { + resolveUndefConstant(getRuntime(), internedName); + value = fastFetchConstant(internedName); + } + + return value; } /** @@ -2516,8 +2526,10 @@ public class RubyModule extends RubyObject { protected IRubyObject getConstantInner(String name) { IRubyObject value = constantTableFetch(name); - for (; value == UNDEF; value = constantTableFetch(name)) { - if (resolveUndefConstant(getRuntime(), name) == null) return UNDEF; + if (value == UNDEF) { + resolveUndefConstant(getRuntime(), name); + value = constantTableFetch(name); + if (value == null) return UNDEF; } return value; @@ -2540,7 +2552,7 @@ public class RubyModule extends RubyObject { while (p != null) { if ((value = p.constantTableFastFetch(internedName)) != null) { if (value == UNDEF) { - if (p.resolveUndefConstant(runtime, internedName) == null) break; + p.resolveUndefConstant(runtime, internedName); continue; // Not that is loaded loop around to resolve it next pass } @@ -2559,10 +2571,18 @@ public class RubyModule extends RubyObject { "const_missing", getRuntime().fastNewSymbol(internedName)); } - public IRubyObject resolveUndefConstant(Ruby runtime, String name) { - deleteConstant(name); - - return runtime.getLoadService().autoload(getName() + "::" + name); + public void resolveUndefConstant(Ruby runtime, String name) { + synchronized (runtime.getLoadService()) { + // We synchronize globally here to ensure only one thread can attempt + // autoload at a time; subsequent threads will see that the constant + // is no longer there or no longer UNDEF and not do the autoload, + // but all will at least wait until the first autoload has completed. + // See JRUBY-3194. + if (fetchConstant(name) == UNDEF) { + deleteConstant(name); + runtime.getLoadService().autoload(getName() + "::" + name); + } + } } /** diff --git a/src/org/jruby/parser/StaticScope.java b/src/org/jruby/parser/StaticScope.java index ab59023..5e8ebf7 100644 --- a/src/org/jruby/parser/StaticScope.java +++ b/src/org/jruby/parser/StaticScope.java @@ -154,7 +154,7 @@ public abstract class StaticScope implements Serializable { /* Try and unload the autoload specified from internedName */ private IRubyObject getUndefConstant(Ruby runtime, String internedName, RubyModule object) { - if (cref.resolveUndefConstant(runtime, internedName) == null) return null; + cref.resolveUndefConstant(runtime, internedName); return getConstantInner(runtime, internedName, object); }