Permalink
Browse files

Reduce cost of repeatedly invalidating global variables.

* Mark all variables as GLOBAL, THREAD, or FRAME scoped
* Only perform indy caching for GLOBAL
* Failover to direct access every time after 100 failures
* Add a failover invalidator that eventually stays invalid

In order to avoid the impact of invalidating global variables that
are modified continually during the run of an application, I've
added a config parameter (invokedynamic.global.maxfail) to fail
those variables and not continue caching them using invokedynamic
and a SwitchPoint. On the update side, I added a new Invalidator
called FailoverSwitchPointInvalidator that eventually gives up and
stays invalid, since even invalidating an unused SwitchPoint is
rather expensive.

With these changes, highly-volatile global variables perform as
well as without invokedynamic, rather than much slower.
  • Loading branch information...
1 parent cc90601 commit 3b8b934e79bf1001b2ec4d767ac3672e9ee4d079 @headius headius committed Jan 5, 2013
View
14 src/org/jruby/Ruby.java
@@ -156,6 +156,8 @@
import java.util.regex.Pattern;
import org.jruby.javasupport.proxy.JavaProxyClassFactory;
+import static org.jruby.internal.runtime.GlobalVariable.Scope.*;
+
/**
* The Ruby object represents the top-level of a JRuby "instance" in a given VM.
* JRuby supports spawning multiple instances in the same JVM. Generally, objects
@@ -478,8 +480,8 @@ public IRubyObject executeScript(String script, String filename) {
*/
public void runFromMain(InputStream inputStream, String filename) {
IAccessor d = new ValueAccessor(newString(filename));
- getGlobalVariables().define("$PROGRAM_NAME", d);
- getGlobalVariables().define("$0", d);
+ getGlobalVariables().define("$PROGRAM_NAME", d, GLOBAL);
+ getGlobalVariables().define("$0", d, GLOBAL);
for (Iterator i = config.getOptionGlobals().entrySet().iterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
@@ -2434,7 +2436,7 @@ public synchronized JRubyClassLoader getJRubyClassLoader() {
/** Defines a global variable
*/
- public void defineVariable(final GlobalVariable variable) {
+ public void defineVariable(final GlobalVariable variable, org.jruby.internal.runtime.GlobalVariable.Scope scope) {
globalVariables.define(variable.name(), new IAccessor() {
public IRubyObject getValue() {
return variable.get();
@@ -2443,14 +2445,14 @@ public IRubyObject getValue() {
public IRubyObject setValue(IRubyObject newValue) {
return variable.set(newValue);
}
- });
+ }, scope);
}
/** defines a readonly global variable
*
*/
- public void defineReadonlyVariable(String name, IRubyObject value) {
- globalVariables.defineReadonly(name, new ValueAccessor(value));
+ public void defineReadonlyVariable(String name, IRubyObject value, org.jruby.internal.runtime.GlobalVariable.Scope scope) {
+ globalVariables.defineReadonly(name, new ValueAccessor(value), scope);
}
public Node parseFile(InputStream in, String file, DynamicScope scope, int lineNumber) {
View
9 src/org/jruby/RubyArgsFile.java
@@ -46,6 +46,7 @@
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import static org.jruby.CompatVersion.*;
+import org.jruby.internal.runtime.GlobalVariable;
public class RubyArgsFile {
private static final class ArgsFileData {
@@ -80,7 +81,7 @@ public boolean next_argv(ThreadContext context) {
RubyString filename = (RubyString)((RubyObject)arg).to_s();
ByteList filenameBytes = filename.getByteList();
if (!filename.op_equal(context, (RubyString) runtime.getGlobalVariables().get("$FILENAME")).isTrue()) {
- runtime.defineReadonlyVariable("$FILENAME", filename);
+ runtime.defineReadonlyVariable("$FILENAME", filename, GlobalVariable.Scope.GLOBAL);
}
if (filenameBytes.length() == 1 && filenameBytes.get(0) == '-') {
@@ -105,7 +106,7 @@ public boolean next_argv(ThreadContext context) {
} else if (next_p == -1) {
currentFile = runtime.getGlobalVariables().get("$stdin");
if(!runtime.getGlobalVariables().get("$FILENAME").asJavaString().equals("-")) {
- runtime.defineReadonlyVariable("$FILENAME", runtime.newString("-"));
+ runtime.defineReadonlyVariable("$FILENAME", runtime.newString("-"), GlobalVariable.Scope.GLOBAL);
}
}
@@ -194,12 +195,12 @@ public IRubyObject getValue() {
public IRubyObject setValue(IRubyObject newValue) {
throw new UnsupportedOperationException("Not supported yet.");
}
- });
+ }, GlobalVariable.Scope.GLOBAL);
runtime.defineGlobalConstant("ARGF", argsFile);
RubyClass argfClass = argsFile.getMetaClass();
argfClass.defineAnnotatedMethods(RubyArgsFile.class);
- runtime.defineReadonlyVariable("$FILENAME", runtime.newString("-"));
+ runtime.defineReadonlyVariable("$FILENAME", runtime.newString("-"), GlobalVariable.Scope.GLOBAL);
}
@JRubyMethod(name = {"fileno", "to_i"})
View
3 src/org/jruby/RubyClassPathVariable.java
@@ -31,6 +31,7 @@
import java.net.MalformedURLException;
import java.net.URL;
import org.jruby.anno.JRubyMethod;
+import org.jruby.internal.runtime.GlobalVariable;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
@@ -43,7 +44,7 @@
public static void createClassPathVariable(Ruby runtime) {
RubyClassPathVariable self = new RubyClassPathVariable(runtime);
runtime.getEnumerable().extend_object(self);
- runtime.defineReadonlyVariable("$CLASSPATH", self);
+ runtime.defineReadonlyVariable("$CLASSPATH", self, GlobalVariable.Scope.GLOBAL);
self.getMetaClass().defineAnnotatedMethods(RubyClassPathVariable.class);
}
View
87 src/org/jruby/RubyGlobal.java
@@ -59,6 +59,8 @@
import org.jruby.util.io.BadDescriptorException;
import org.jruby.util.io.STDIO;
+import static org.jruby.internal.runtime.GlobalVariable.Scope.*;
+
import java.util.HashMap;
import java.util.Map;
@@ -80,7 +82,7 @@ public static void initARGV(Ruby runtime) {
((RubyArray)runtime.getObject().getConstant("ARGV")).replace(argvArray);
} else {
runtime.getObject().setConstantQuiet("ARGV", argvArray);
- runtime.getGlobalVariables().define("$*", new ValueAccessor(argvArray));
+ runtime.getGlobalVariables().define("$*", new ValueAccessor(argvArray), GLOBAL);
}
}
@@ -97,8 +99,8 @@ public static void createGlobals(ThreadContext context, Ruby runtime) {
IAccessor d = new ValueAccessor(runtime.newString(
runtime.getInstanceConfig().displayedFileName()));
- globals.define("$PROGRAM_NAME", d);
- globals.define("$0", d);
+ globals.define("$PROGRAM_NAME", d, GLOBAL);
+ globals.define("$0", d, GLOBAL);
// Version information:
IRubyObject version = null;
@@ -147,7 +149,7 @@ public static void createGlobals(ThreadContext context, Ruby runtime) {
runtime.defineGlobalConstant("RUBY_REVISION", runtime.newFixnum(Constants.RUBY1_9_REVISION));
RubyInstanceConfig.Verbosity verbosity = runtime.getInstanceConfig().getVerbosity();
- runtime.defineVariable(new WarningGlobalVariable(runtime, "$-W", verbosity));
+ runtime.defineVariable(new WarningGlobalVariable(runtime, "$-W", verbosity), GLOBAL);
}
final GlobalVariable kcodeGV;
@@ -157,27 +159,27 @@ public static void createGlobals(ThreadContext context, Ruby runtime) {
kcodeGV = new KCodeGlobalVariable(runtime, "$KCODE", runtime.newString("NONE"));
}
- runtime.defineVariable(kcodeGV);
- runtime.defineVariable(new GlobalVariable.Copy(runtime, "$-K", kcodeGV));
+ runtime.defineVariable(kcodeGV, GLOBAL);
+ runtime.defineVariable(new GlobalVariable.Copy(runtime, "$-K", kcodeGV), GLOBAL);
IRubyObject defaultRS = runtime.newString(runtime.getInstanceConfig().getRecordSeparator()).freeze(context);
GlobalVariable rs = new StringGlobalVariable(runtime, "$/", defaultRS);
- runtime.defineVariable(rs);
+ runtime.defineVariable(rs, GLOBAL);
runtime.setRecordSeparatorVar(rs);
globals.setDefaultSeparator(defaultRS);
- runtime.defineVariable(new StringGlobalVariable(runtime, "$\\", runtime.getNil()));
- runtime.defineVariable(new StringGlobalVariable(runtime, "$,", runtime.getNil()));
+ runtime.defineVariable(new StringGlobalVariable(runtime, "$\\", runtime.getNil()), GLOBAL);
+ runtime.defineVariable(new StringGlobalVariable(runtime, "$,", runtime.getNil()), GLOBAL);
- runtime.defineVariable(new LineNumberGlobalVariable(runtime, "$."));
- runtime.defineVariable(new LastlineGlobalVariable(runtime, "$_"));
- runtime.defineVariable(new LastExitStatusVariable(runtime, "$?"));
+ runtime.defineVariable(new LineNumberGlobalVariable(runtime, "$."), GLOBAL);
+ runtime.defineVariable(new LastlineGlobalVariable(runtime, "$_"), FRAME);
+ runtime.defineVariable(new LastExitStatusVariable(runtime, "$?"), THREAD);
- runtime.defineVariable(new ErrorInfoGlobalVariable(runtime, "$!", runtime.getNil()));
- runtime.defineVariable(new NonEffectiveGlobalVariable(runtime, "$=", runtime.getFalse()));
+ runtime.defineVariable(new ErrorInfoGlobalVariable(runtime, "$!", runtime.getNil()), THREAD);
+ runtime.defineVariable(new NonEffectiveGlobalVariable(runtime, "$=", runtime.getFalse()), GLOBAL);
if(runtime.getInstanceConfig().getInputFieldSeparator() == null) {
- runtime.defineVariable(new GlobalVariable(runtime, "$;", runtime.getNil()));
+ runtime.defineVariable(new GlobalVariable(runtime, "$;", runtime.getNil()), GLOBAL);
} else {
- runtime.defineVariable(new GlobalVariable(runtime, "$;", RubyRegexp.newRegexp(runtime, runtime.getInstanceConfig().getInputFieldSeparator(), new RegexpOptions())));
+ runtime.defineVariable(new GlobalVariable(runtime, "$;", RubyRegexp.newRegexp(runtime, runtime.getInstanceConfig().getInputFieldSeparator(), new RegexpOptions())), GLOBAL);
}
RubyInstanceConfig.Verbosity verbose = runtime.getInstanceConfig().getVerbosity();
@@ -189,67 +191,70 @@ public static void createGlobals(ThreadContext context, Ruby runtime) {
} else {
verboseValue = runtime.getFalse();
}
- runtime.defineVariable(new VerboseGlobalVariable(runtime, "$VERBOSE", verboseValue));
- runtime.defineVariable(new VerboseGlobalVariable(runtime, "$-v", verboseValue));
- runtime.defineVariable(new VerboseGlobalVariable(runtime, "$-w", verboseValue));
+ runtime.defineVariable(new VerboseGlobalVariable(runtime, "$VERBOSE", verboseValue), GLOBAL);
+ runtime.defineVariable(new VerboseGlobalVariable(runtime, "$-v", verboseValue), GLOBAL);
+ runtime.defineVariable(new VerboseGlobalVariable(runtime, "$-w", verboseValue), GLOBAL);
IRubyObject debug = runtime.newBoolean(runtime.getInstanceConfig().isDebug());
- runtime.defineVariable(new DebugGlobalVariable(runtime, "$DEBUG", debug));
- runtime.defineVariable(new DebugGlobalVariable(runtime, "$-d", debug));
+ runtime.defineVariable(new DebugGlobalVariable(runtime, "$DEBUG", debug), GLOBAL);
+ runtime.defineVariable(new DebugGlobalVariable(runtime, "$-d", debug), GLOBAL);
- runtime.defineVariable(new SafeGlobalVariable(runtime, "$SAFE"));
+ runtime.defineVariable(new SafeGlobalVariable(runtime, "$SAFE"), THREAD);
- runtime.defineVariable(new BacktraceGlobalVariable(runtime, "$@"));
+ runtime.defineVariable(new BacktraceGlobalVariable(runtime, "$@"), THREAD);
IRubyObject stdin = new RubyIO(runtime, STDIO.IN);
IRubyObject stdout = new RubyIO(runtime, STDIO.OUT);
IRubyObject stderr = new RubyIO(runtime, STDIO.ERR);
- runtime.defineVariable(new InputGlobalVariable(runtime, "$stdin", stdin));
+ runtime.defineVariable(new InputGlobalVariable(runtime, "$stdin", stdin), GLOBAL);
- runtime.defineVariable(new OutputGlobalVariable(runtime, "$stdout", stdout));
+ runtime.defineVariable(new OutputGlobalVariable(runtime, "$stdout", stdout), GLOBAL);
globals.alias("$>", "$stdout");
if (!runtime.is1_9()) globals.alias("$defout", "$stdout");
- runtime.defineVariable(new OutputGlobalVariable(runtime, "$stderr", stderr));
+ runtime.defineVariable(new OutputGlobalVariable(runtime, "$stderr", stderr), GLOBAL);
if (!runtime.is1_9()) globals.alias("$deferr", "$stderr");
runtime.defineGlobalConstant("STDIN", stdin);
runtime.defineGlobalConstant("STDOUT", stdout);
runtime.defineGlobalConstant("STDERR", stderr);
- runtime.defineVariable(new LoadedFeatures(runtime, "$\""));
- runtime.defineVariable(new LoadedFeatures(runtime, "$LOADED_FEATURES"));
+ runtime.defineVariable(new LoadedFeatures(runtime, "$\""), GLOBAL);
+ runtime.defineVariable(new LoadedFeatures(runtime, "$LOADED_FEATURES"), GLOBAL);
- runtime.defineVariable(new LoadPath(runtime, "$:"));
- runtime.defineVariable(new LoadPath(runtime, "$-I"));
- runtime.defineVariable(new LoadPath(runtime, "$LOAD_PATH"));
+ runtime.defineVariable(new LoadPath(runtime, "$:"), GLOBAL);
+ runtime.defineVariable(new LoadPath(runtime, "$-I"), GLOBAL);
+ runtime.defineVariable(new LoadPath(runtime, "$LOAD_PATH"), GLOBAL);
- runtime.defineVariable(new MatchMatchGlobalVariable(runtime, "$&"));
- runtime.defineVariable(new PreMatchGlobalVariable(runtime, "$`"));
- runtime.defineVariable(new PostMatchGlobalVariable(runtime, "$'"));
- runtime.defineVariable(new LastMatchGlobalVariable(runtime, "$+"));
- runtime.defineVariable(new BackRefGlobalVariable(runtime, "$~"));
+ runtime.defineVariable(new MatchMatchGlobalVariable(runtime, "$&"), FRAME);
+ runtime.defineVariable(new PreMatchGlobalVariable(runtime, "$`"), FRAME);
+ runtime.defineVariable(new PostMatchGlobalVariable(runtime, "$'"), FRAME);
+ runtime.defineVariable(new LastMatchGlobalVariable(runtime, "$+"), FRAME);
+ runtime.defineVariable(new BackRefGlobalVariable(runtime, "$~"), FRAME);
// On platforms without a c-library accessable through JNA, getpid will return hashCode
// as $$ used to. Using $$ to kill processes could take down many runtimes, but by basing
// $$ on getpid() where available, we have the same semantics as MRI.
- globals.defineReadonly("$$", new PidAccessor(runtime));
+ globals.defineReadonly("$$", new PidAccessor(runtime), GLOBAL);
// after defn of $stderr as the call may produce warnings
defineGlobalEnvConstants(runtime);
// Fixme: Do we need the check or does Main.java not call this...they should consolidate
if (globals.get("$*").isNil()) {
- globals.defineReadonly("$*", new ValueAccessor(runtime.newArray()));
+ globals.defineReadonly("$*", new ValueAccessor(runtime.newArray()), GLOBAL);
}
globals.defineReadonly("$-p",
- new ValueAccessor(runtime.newBoolean(runtime.getInstanceConfig().isAssumePrinting())));
+ new ValueAccessor(runtime.newBoolean(runtime.getInstanceConfig().isAssumePrinting())),
+ GLOBAL);
globals.defineReadonly("$-a",
- new ValueAccessor(runtime.newBoolean(runtime.getInstanceConfig().isSplit())));
+ new ValueAccessor(runtime.newBoolean(runtime.getInstanceConfig().isSplit())),
+ GLOBAL);
globals.defineReadonly("$-l",
- new ValueAccessor(runtime.newBoolean(runtime.getInstanceConfig().isProcessLineEnds())));
+ new ValueAccessor(runtime.newBoolean(runtime.getInstanceConfig().isProcessLineEnds())),
+ GLOBAL);
// ARGF, $< object
RubyArgsFile.initArgsFile(runtime);
View
3 src/org/jruby/demo/IRBConsole.java
@@ -20,6 +20,7 @@
import org.jruby.CompatVersion;
import org.jruby.Ruby;
import org.jruby.RubyInstanceConfig;
+import org.jruby.internal.runtime.GlobalVariable;
import org.jruby.internal.runtime.ValueAccessor;
public class IRBConsole extends JFrame {
@@ -75,7 +76,7 @@ public void windowClosing(WindowEvent e) {
}};
final Ruby runtime = Ruby.newInstance(config);
- runtime.getGlobalVariables().defineReadonly("$$", new ValueAccessor(runtime.newFixnum(System.identityHashCode(runtime))));
+ runtime.getGlobalVariables().defineReadonly("$$", new ValueAccessor(runtime.newFixnum(System.identityHashCode(runtime))), GlobalVariable.Scope.GLOBAL);
tar.hookIntoRuntime(runtime);
View
7 src/org/jruby/embed/ScriptingContainer.java
@@ -60,6 +60,7 @@
import org.jruby.embed.io.ReaderInputStream;
import org.jruby.embed.io.WriterOutputStream;
import org.jruby.embed.util.SystemPropertyCatcher;
+import org.jruby.internal.runtime.GlobalVariable;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.runtime.Block;
import org.jruby.runtime.builtin.IRubyObject;
@@ -1641,7 +1642,7 @@ public void setReader(Reader reader) {
Ruby runtime = provider.getRuntime();
RubyIO io = new RubyIO(runtime, istream);
io.getOpenFile().getMainStream().setSync(true);
- runtime.defineVariable(new InputGlobalVariable(runtime, "$stdin", io));
+ runtime.defineVariable(new InputGlobalVariable(runtime, "$stdin", io), GlobalVariable.Scope.GLOBAL);
runtime.getObject().storeConstant("STDIN", io);
}
@@ -1699,7 +1700,7 @@ private void setOutputStream(PrintStream pstream) {
Ruby runtime = provider.getRuntime();
RubyIO io = new RubyIO(runtime, pstream);
io.getOpenFile().getMainStream().setSync(true);
- runtime.defineVariable(new OutputGlobalVariable(runtime, "$stdout", io));
+ runtime.defineVariable(new OutputGlobalVariable(runtime, "$stdout", io), GlobalVariable.Scope.GLOBAL);
runtime.getObject().storeConstant("STDOUT", io);
runtime.getGlobalVariables().alias("$>", "$stdout");
runtime.getGlobalVariables().alias("$defout", "$stdout");
@@ -1764,7 +1765,7 @@ private void setErrorStream(PrintStream error) {
Ruby runtime = provider.getRuntime();
RubyIO io = new RubyIO(runtime, error);
io.getOpenFile().getMainStream().setSync(true);
- runtime.defineVariable(new OutputGlobalVariable(runtime, "$stderr", io));
+ runtime.defineVariable(new OutputGlobalVariable(runtime, "$stderr", io), GlobalVariable.Scope.GLOBAL);
runtime.getObject().storeConstant("STDERR", io);
runtime.getGlobalVariables().alias("$deferr", "$stderr");
}
View
3 src/org/jruby/embed/bsf/JRubyEngine.java
@@ -59,6 +59,7 @@
import org.jruby.embed.variable.VariableInterceptor;
import org.jruby.exceptions.JumpException;
import org.jruby.exceptions.RaiseException;
+import org.jruby.internal.runtime.GlobalVariable;
import org.jruby.javasupport.Java;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.javasupport.JavaEmbedUtils.EvalUnit;
@@ -162,7 +163,7 @@ public void initialize(BSFManager manager, String language, Vector someDeclaredB
setVariable(bean);
}
}
- runtime.getGlobalVariables().defineReadonly("$bsf", new FunctionsGlobalVariable(runtime, new BSFFunctions(manager, this)));
+ runtime.getGlobalVariables().defineReadonly("$bsf", new FunctionsGlobalVariable(runtime, new BSFFunctions(manager, this)), GlobalVariable.Scope.GLOBAL);
}
private void setVariable(BSFDeclaredBean bean) {
View
5 src/org/jruby/embed/internal/EmbedRubyRuntimeAdapterImpl.java
@@ -54,6 +54,7 @@
import org.jruby.embed.io.ReaderInputStream;
import org.jruby.embed.util.SystemPropertyCatcher;
import org.jruby.exceptions.RaiseException;
+import org.jruby.internal.runtime.GlobalVariable;
import org.jruby.internal.runtime.ValueAccessor;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.javasupport.JavaEmbedUtils.EvalUnit;
@@ -154,8 +155,8 @@ private EmbedEvalUnit runParser(Object input, String filename, int... lines) {
}
Ruby runtime = container.getProvider().getRuntime();
IAccessor d = new ValueAccessor(RubyString.newString(runtime, filename));
- runtime.getGlobalVariables().define("$PROGRAM_NAME", d);
- runtime.getGlobalVariables().define("$0", d);
+ runtime.getGlobalVariables().define("$PROGRAM_NAME", d, GlobalVariable.Scope.GLOBAL);
+ runtime.getGlobalVariables().define("$0", d, GlobalVariable.Scope.GLOBAL);
int line = 0;
if (lines != null && lines.length > 0) {
View
5 src/org/jruby/embed/jsr223/Utils.java
@@ -47,6 +47,7 @@
import org.jruby.embed.io.WriterOutputStream;
import org.jruby.embed.variable.TransientLocalVariable;
import org.jruby.embed.variable.VariableInterceptor;
+import org.jruby.internal.runtime.GlobalVariable;
import org.jruby.util.io.BadDescriptorException;
/**
@@ -147,7 +148,7 @@ private static void setWriter(ScriptingContainer container, Writer writer) throw
Ruby runtime = container.getProvider().getRuntime();
RubyIO io = getRubyIO(runtime, writer);
- runtime.defineVariable(new OutputGlobalVariable(runtime, "$stdout", io));
+ runtime.defineVariable(new OutputGlobalVariable(runtime, "$stdout", io), GlobalVariable.Scope.GLOBAL);
runtime.getObject().storeConstant("STDOUT", io);
runtime.getGlobalVariables().alias("$>", "$stdout");
runtime.getGlobalVariables().alias("$defout", "$stdout");
@@ -168,7 +169,7 @@ private static void setErrorWriter(ScriptingContainer container, Writer writer)
Ruby runtime = container.getProvider().getRuntime();
RubyIO io = getRubyIO(runtime, writer);
- runtime.defineVariable(new OutputGlobalVariable(runtime, "$stderr", io));
+ runtime.defineVariable(new OutputGlobalVariable(runtime, "$stderr", io), GlobalVariable.Scope. GLOBAL);
runtime.getObject().storeConstant("STDERR", io);
runtime.getGlobalVariables().alias("$deferr", "$stderr");
}
View
14 src/org/jruby/internal/runtime/GlobalVariable.java
@@ -38,30 +38,38 @@
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.opto.Invalidator;
import org.jruby.runtime.opto.OptoFactory;
+import org.jruby.util.cli.Options;
/**
*
* @author jpetersen
*/
public final class GlobalVariable {
+ public enum Scope { GLOBAL, THREAD, FRAME }
private IAccessor accessor;
private ArrayList<IRubyObject> traces = null;
private boolean tracing;
- private Invalidator invalidator = OptoFactory.newConstantInvalidator();
+ private Invalidator invalidator = OptoFactory.newGlobalInvalidator(Options.INVOKEDYNAMIC_GLOBAL_MAXFAIL.load());
+ private final Scope scope;
- public GlobalVariable(IAccessor accessor) {
+ public GlobalVariable(IAccessor accessor, Scope scope) {
this.accessor = accessor;
+ this.scope = scope;
}
public static GlobalVariable newUndefined(Ruby runtime, String name) {
- GlobalVariable variable = new GlobalVariable(null);
+ GlobalVariable variable = new GlobalVariable(null, Scope.GLOBAL);
variable.setAccessor(new UndefinedAccessor(runtime, variable, name));
return variable;
}
public IAccessor getAccessor() {
return accessor;
}
+
+ public Scope getScope() {
+ return scope;
+ }
public ArrayList getTraces() {
return traces;
View
8 src/org/jruby/internal/runtime/GlobalVariables.java
@@ -54,20 +54,20 @@ public GlobalVariables(Ruby runtime) {
this.runtime = runtime;
}
- public void define(String name, IAccessor accessor) {
+ public void define(String name, IAccessor accessor, GlobalVariable.Scope scope) {
assert name != null;
assert accessor != null;
assert name.startsWith("$");
- globalVariables.put(name, new GlobalVariable(accessor));
+ globalVariables.put(name, new GlobalVariable(accessor, scope));
}
- public void defineReadonly(String name, IAccessor accessor) {
+ public void defineReadonly(String name, IAccessor accessor, GlobalVariable.Scope scope) {
assert name != null;
assert accessor != null;
assert name.startsWith("$");
- globalVariables.put(name, new GlobalVariable(new ReadonlyAccessor(name, accessor)));
+ globalVariables.put(name, new GlobalVariable(new ReadonlyAccessor(name, accessor), scope));
}
public boolean isDefined(String name) {
View
12 src/org/jruby/javasupport/bsf/JRubyEngine.java
@@ -119,11 +119,12 @@ public void initialize(BSFManager manager, String language, Vector someDeclaredB
for (int i = 0, size = someDeclaredBeans.size(); i < size; i++) {
BSFDeclaredBean bean = (BSFDeclaredBean) someDeclaredBeans.elementAt(i);
runtime.getGlobalVariables().define(
- GlobalVariable.variableName(bean.name),
- new BeanGlobalVariable(runtime, bean));
+ GlobalVariable.variableName(bean.name),
+ new BeanGlobalVariable(runtime, bean),
+ org.jruby.internal.runtime.GlobalVariable.Scope.GLOBAL);
}
- runtime.getGlobalVariables().defineReadonly("$bsf", new FunctionsGlobalVariable(runtime, new BSFFunctions(manager, this)));
+ runtime.getGlobalVariables().defineReadonly("$bsf", new FunctionsGlobalVariable(runtime, new BSFFunctions(manager, this)), org.jruby.internal.runtime.GlobalVariable.Scope.GLOBAL);
}
private List getClassPath(BSFManager manager) {
@@ -133,8 +134,9 @@ private List getClassPath(BSFManager manager) {
@Override
public void declareBean(BSFDeclaredBean bean) throws BSFException {
runtime.getGlobalVariables().define(
- GlobalVariable.variableName(bean.name),
- new BeanGlobalVariable(runtime, bean));
+ GlobalVariable.variableName(bean.name),
+ new BeanGlobalVariable(runtime, bean),
+ org.jruby.internal.runtime.GlobalVariable.Scope.GLOBAL);
}
@Override
View
15 src/org/jruby/runtime/invokedynamic/GlobalSite.java
@@ -1,5 +1,6 @@
package org.jruby.runtime.invokedynamic;
+import java.lang.invoke.MethodHandle;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.common.IRubyWarnings;
@@ -13,6 +14,7 @@
public final String name;
private final String file;
private final int line;
+ private volatile int failures;
public GlobalSite(MethodType type, String name, String file, int line) {
super(type);
@@ -28,4 +30,17 @@ public String file() {
public int line() {
return line;
}
+
+ public void setTarget(MethodHandle target) {
+ super.setTarget(target);
+ incrementFailures();
+ }
+
+ public int failures() {
+ return failures;
+ }
+
+ public void incrementFailures() {
+ failures += 1;
+ }
}
View
31 src/org/jruby/runtime/invokedynamic/InvokeDynamicSupport.java
@@ -64,6 +64,7 @@
import org.jruby.internal.runtime.GlobalVariable;
import org.jruby.runtime.opto.Invalidator;
import org.jruby.util.JavaNameMangler;
+import org.jruby.util.cli.Options;
@SuppressWarnings("deprecation")
public class InvokeDynamicSupport {
@@ -592,6 +593,17 @@ public static CallSite globalBooleanBootstrap(Lookup lookup, String name, Method
public static IRubyObject getGlobalFallback(GlobalSite site, ThreadContext context) throws Throwable {
Ruby runtime = context.runtime;
GlobalVariable variable = runtime.getGlobalVariables().getVariable(site.name);
+
+ if (site.failures() > Options.INVOKEDYNAMIC_GLOBAL_MAXFAIL.load() ||
+ variable.getScope() != GlobalVariable.Scope.GLOBAL) {
+ // use uncached logic forever
+ MethodHandle uncached = lookup().findStatic(InvokeDynamicSupport.class, "getGlobalUncached", methodType(IRubyObject.class, GlobalVariable.class));
+ uncached = uncached.bindTo(variable);
+ uncached = dropArguments(uncached, 0, ThreadContext.class);
+ site.setTarget(uncached);
+ return (IRubyObject)uncached.invokeWithArguments(context);
+ }
+
Invalidator invalidator = variable.getInvalidator();
IRubyObject value = variable.getAccessor().getValue();
@@ -607,9 +619,24 @@ public static IRubyObject getGlobalFallback(GlobalSite site, ThreadContext conte
return value;
}
+ public static IRubyObject getGlobalUncached(GlobalVariable variable) throws Throwable {
+ return variable.getAccessor().getValue();
+ }
+
public static boolean getGlobalBooleanFallback(GlobalSite site, ThreadContext context) throws Throwable {
Ruby runtime = context.runtime;
GlobalVariable variable = runtime.getGlobalVariables().getVariable(site.name);
+
+ if (site.failures() > Options.INVOKEDYNAMIC_GLOBAL_MAXFAIL.load() ||
+ variable.getScope() != GlobalVariable.Scope.GLOBAL) {
+ // use uncached logic forever
+ MethodHandle uncached = lookup().findStatic(InvokeDynamicSupport.class, "getGlobalBooleanUncached", methodType(boolean.class, GlobalVariable.class));
+ uncached = uncached.bindTo(variable);
+ uncached = dropArguments(uncached, 0, ThreadContext.class);
+ site.setTarget(uncached);
+ return (Boolean)uncached.invokeWithArguments(context);
+ }
+
Invalidator invalidator = variable.getInvalidator();
boolean value = variable.getAccessor().getValue().isTrue();
@@ -624,6 +651,10 @@ public static boolean getGlobalBooleanFallback(GlobalSite site, ThreadContext co
return value;
}
+
+ public static boolean getGlobalBooleanUncached(GlobalVariable variable) throws Throwable {
+ return variable.getAccessor().getValue().isTrue();
+ }
public static CallSite checkpointBootstrap(Lookup lookup, String name, MethodType type) throws Throwable {
MutableCallSite site = new MutableCallSite(type);
View
84 src/org/jruby/runtime/opto/FailoverSwitchPointInvalidator.java
@@ -0,0 +1,84 @@
+/*
+ ***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.runtime.opto;
+
+import java.lang.invoke.SwitchPoint;
+import java.util.List;
+
+public class FailoverSwitchPointInvalidator implements Invalidator {
+ // a dummy switchpoint to use until we actually need a real one
+ private static final SwitchPoint DUMMY = new SwitchPoint();
+ static {SwitchPoint.invalidateAll(new SwitchPoint[]{DUMMY});}
+
+ private volatile SwitchPoint switchPoint = DUMMY;
+
+ private volatile int failures;
+ private final int maxFailures;
+
+ public FailoverSwitchPointInvalidator(int maxFailures) {
+ this.maxFailures = maxFailures;
+ }
+
+ public synchronized void invalidate() {
+ if (switchPoint == DUMMY) return;
+
+ failures += 1;
+
+ // if we have failed too many times, set it to DUMMY and leave it
+ if (failures > maxFailures) {
+ switchPoint = DUMMY;
+ return;
+ }
+
+ SwitchPoint.invalidateAll(new SwitchPoint[]{switchPoint});
+ switchPoint = new SwitchPoint();
+ }
+
+ public void invalidateAll(List<Invalidator> invalidators) {
+ SwitchPoint[] switchPoints = new SwitchPoint[invalidators.size()];
+
+ for (int i = 0; i < invalidators.size(); i++) {
+ Invalidator invalidator = invalidators.get(i);
+ assert invalidator instanceof FailoverSwitchPointInvalidator;
+ switchPoints[i] = ((FailoverSwitchPointInvalidator)invalidator).replaceSwitchPoint();
+ }
+
+ SwitchPoint.invalidateAll(switchPoints);
+ }
+
+ public synchronized Object getData() {
+ return switchPoint == DUMMY && failures <= maxFailures ? switchPoint = new SwitchPoint() : switchPoint;
+ }
+
+ public synchronized SwitchPoint replaceSwitchPoint() {
+ if (switchPoint == DUMMY || failures > maxFailures) return DUMMY;
+
+ SwitchPoint oldSwitchPoint = switchPoint;
+ switchPoint = new SwitchPoint();
+ return oldSwitchPoint;
+ }
+}
View
13 src/org/jruby/runtime/opto/OptoFactory.java
@@ -85,6 +85,19 @@ public static Invalidator newConstantInvalidator() {
return new ObjectIdentityInvalidator();
}
+ public static Invalidator newGlobalInvalidator(int maxFailures) {
+ if (RubyInstanceConfig.JAVA_VERSION == Opcodes.V1_7 && RubyInstanceConfig.INVOKEDYNAMIC_CONSTANTS) {
+ try {
+ Class failoverInvalidator = Class.forName("org.jruby.runtime.opto.FailoverSwitchPointInvalidator");
+ Constructor constructor = failoverInvalidator.getConstructor(int.class);
+ return (Invalidator)constructor.newInstance(maxFailures);
+ } catch (Throwable t) {
+ // ignore
+ }
+ }
+ return new ObjectIdentityInvalidator();
+ }
+
public static Invalidator newMethodInvalidator(RubyModule module) {
if (RubyInstanceConfig.JAVA_VERSION == Opcodes.V1_7 && RubyInstanceConfig.INVOKEDYNAMIC_INVOCATION_SWITCHPOINT) {
try {
View
1 src/org/jruby/util/cli/Options.java
@@ -127,6 +127,7 @@ public static String dump() {
public static final Option<Boolean> INVOKEDYNAMIC_CACHE_LITERALS = bool(INVOKEDYNAMIC, "invokedynamic.cache.literals", true, "Use invokedynamic to load literals.");
public static final Option<Boolean> INVOKEDYNAMIC_CACHE_IVARS = bool(INVOKEDYNAMIC, "invokedynamic.cache.ivars", true, "Use invokedynamic to get/set instance variables.");
public static final Option<Boolean> INVOKEDYNAMIC_CLASS_VALUES = bool(INVOKEDYNAMIC, "invokedynamic.class.values", false, "Use ClassValue to store class-specific data.");
+ public static final Option<Integer> INVOKEDYNAMIC_GLOBAL_MAXFAIL = integer(INVOKEDYNAMIC, "invokedynamic.global.maxfail", 100, "Maximum global cache failures after which to use slow path.");
public static final Option<Integer> JIT_THRESHOLD = integer(JIT, "jit.threshold", Constants.JIT_THRESHOLD, "Set the JIT threshold to the specified method invocation count.");
public static final Option<Integer> JIT_MAX = integer(JIT, "jit.max", Constants.JIT_MAX_METHODS_LIMIT, "Set the max count of active methods eligible for JIT-compilation.");
View
15 test/org/jruby/javasupport/test/RubyTestCase.java
@@ -40,6 +40,7 @@
import org.jruby.Ruby;
import org.jruby.RubyKernel;
+import org.jruby.internal.runtime.GlobalVariable;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.NormalizedFile;
@@ -83,13 +84,13 @@ protected void initRuby(Ruby runtime) {
EMPTY_ARRAY,
EMPTY_ARRAY.getClass());
- runtime.defineReadonlyVariable("$-p", runtime.getNil());
- runtime.defineReadonlyVariable("$-n", runtime.getNil());
- runtime.defineReadonlyVariable("$-a", runtime.getNil());
- runtime.defineReadonlyVariable("$-l", runtime.getNil());
- runtime.defineReadonlyVariable("$\"", empty);
- runtime.defineReadonlyVariable("$*", empty);
- runtime.defineReadonlyVariable("$:", empty);
+ runtime.defineReadonlyVariable("$-p", runtime.getNil(), GlobalVariable.Scope.GLOBAL);
+ runtime.defineReadonlyVariable("$-n", runtime.getNil(), GlobalVariable.Scope.GLOBAL);
+ runtime.defineReadonlyVariable("$-a", runtime.getNil(), GlobalVariable.Scope.GLOBAL);
+ runtime.defineReadonlyVariable("$-l", runtime.getNil(), GlobalVariable.Scope.GLOBAL);
+ runtime.defineReadonlyVariable("$\"", empty, GlobalVariable.Scope.GLOBAL);
+ runtime.defineReadonlyVariable("$*", empty, GlobalVariable.Scope.GLOBAL);
+ runtime.defineReadonlyVariable("$:", empty, GlobalVariable.Scope.GLOBAL);
runtime.defineGlobalConstant("ARGV", empty);
}
}

0 comments on commit 3b8b934

Please sign in to comment.