Named based constant cache invalidation #798

Merged
merged 8 commits into from Jun 11, 2013
View
@@ -135,6 +135,7 @@
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
@@ -219,7 +220,6 @@ private Ruby(RubyInstanceConfig config) {
this.runtimeCache = new RuntimeCache();
runtimeCache.initMethodCache(ClassIndex.MAX_CLASSES * MethodNames.values().length - 1);
- constantInvalidator = OptoFactory.newConstantInvalidator();
checkpointInvalidator = OptoFactory.newConstantInvalidator();
if (config.isObjectSpaceEnabled()) {
@@ -4181,23 +4181,28 @@ public int getConstantGeneration() {
return -1;
}
- @Deprecated
- public synchronized void incrementConstantGeneration() {
- constantInvalidator.invalidate();
+ public Invalidator getConstantInvalidator(String constantName) {
+ Invalidator invalidator = constantNameInvalidators.get(constantName);
+ if (invalidator != null) {
+ return invalidator;
+ } else {
+ return addConstantInvalidator(constantName);
+ }
}
-
- public Invalidator getConstantInvalidator() {
- return constantInvalidator;
+
+ private Invalidator addConstantInvalidator(String constantName) {
+ Invalidator invalidator = OptoFactory.newConstantInvalidator();
+ constantNameInvalidators.putIfAbsent(constantName, invalidator);
+
+ // fetch the invalidator back from the ConcurrentHashMap to ensure that
+ // only one invalidator for a given constant name is ever used:
+ return constantNameInvalidators.get(constantName);
}
public Invalidator getCheckpointInvalidator() {
return checkpointInvalidator;
}
- public void invalidateConstants() {
-
- }
-
public <E extends Enum<E>> void loadConstantSet(RubyModule module, Class<E> enumClass) {
for (E e : EnumSet.allOf(enumClass)) {
Constant c = (Constant) e;
@@ -4467,7 +4472,12 @@ public CallbackFactory callbackFactory(Class<?> type) {
throw new RuntimeException("callback-style handles are no longer supported in JRuby");
}
- private final Invalidator constantInvalidator;
+ private final ConcurrentHashMap<String, Invalidator> constantNameInvalidators =
+ new ConcurrentHashMap<String, Invalidator>(
+ 16 /* default initial capacity */,
+ 0.75f /* default load factory */,
+ 1 /* concurrency level - mostly reads here so this can be 1 */);
+
private final Invalidator checkpointInvalidator;
private final ThreadService threadService;
@@ -986,9 +986,8 @@ private CacheEntry cacheHit(String name) {
private void invalidateConstantCacheForModuleInclusion(RubyModule module)
{
for (RubyModule mod : gatherModules(module)) {
- if (!mod.getConstantMap().isEmpty()) {
- invalidateConstantCache();
- break;
+ for (String key : mod.getConstantMap().keySet()) {
+ invalidateConstantCache(key);
}
}
}
@@ -1144,8 +1143,8 @@ protected void invalidateCacheDescendantsInner() {
methodInvalidator.invalidate();
}
- protected void invalidateConstantCache() {
- getRuntime().getConstantInvalidator().invalidate();
+ protected void invalidateConstantCache(String constantName) {
+ getRuntime().getConstantInvalidator(constantName).invalidate();
}
/**
@@ -2619,7 +2618,7 @@ public IRubyObject remove_const(ThreadContext context, IRubyObject rubyName) {
String name = validateConstant(rubyName.asJavaString());
IRubyObject value;
if ((value = deleteConstant(name)) != null) {
- invalidateConstantCache();
+ invalidateConstantCache(name);
if (value != UNDEF) {
return value;
}
@@ -2731,34 +2730,38 @@ public RubyArray constantsCommon19(ThreadContext context, boolean replaceModule,
}
@JRubyMethod(compat = RUBY1_9)
- public IRubyObject private_constant(ThreadContext context, IRubyObject name) {
- setConstantVisibility(context, validateConstant(name.asJavaString()), true);
- invalidateConstantCache();
+ public IRubyObject private_constant(ThreadContext context, IRubyObject rubyName) {
+ String name = rubyName.asJavaString();
+ setConstantVisibility(context, validateConstant(name), true);
+ invalidateConstantCache(name);
return this;
}
@JRubyMethod(compat = RUBY1_9, required = 1, rest = true)
- public IRubyObject private_constant(ThreadContext context, IRubyObject[] names) {
- for (IRubyObject name : names) {
- setConstantVisibility(context, validateConstant(name.asJavaString()), true);
+ public IRubyObject private_constant(ThreadContext context, IRubyObject[] rubyNames) {
+ for (IRubyObject rubyName : rubyNames) {
+ String name = rubyName.asJavaString();
+ setConstantVisibility(context, validateConstant(name), true);
+ invalidateConstantCache(name);
}
- invalidateConstantCache();
return this;
}
@JRubyMethod(compat = RUBY1_9)
- public IRubyObject public_constant(ThreadContext context, IRubyObject name) {
- setConstantVisibility(context, validateConstant(name.asJavaString()), false);
- invalidateConstantCache();
+ public IRubyObject public_constant(ThreadContext context, IRubyObject rubyName) {
+ String name = rubyName.asJavaString();
+ setConstantVisibility(context, validateConstant(name), false);
+ invalidateConstantCache(name);
return this;
}
@JRubyMethod(compat = RUBY1_9, required = 1, rest = true)
- public IRubyObject public_constant(ThreadContext context, IRubyObject[] names) {
- for (IRubyObject name : names) {
- setConstantVisibility(context, validateConstant(name.asJavaString()), false);
+ public IRubyObject public_constant(ThreadContext context, IRubyObject[] rubyNames) {
+ for (IRubyObject rubyName : rubyNames) {
+ String name = rubyName.asJavaString();
+ setConstantVisibility(context, validateConstant(name), false);
+ invalidateConstantCache(name);
}
- invalidateConstantCache();
return this;
}
@@ -3082,7 +3085,7 @@ private IRubyObject setConstantCommon(String name, IRubyObject value, boolean wa
storeConstant(name, value);
}
- invalidateConstantCache();
+ invalidateConstantCache(name);
// if adding a module under a constant name, set that module's basename to the constant name
if (value instanceof RubyModule) {
@@ -65,12 +65,12 @@ private boolean isCached(ThreadContext context, RubyModule target, IRubyObject v
// We could probably also detect if LHS value came out of cache and avoid some of this
return
value != null &&
- generation == context.runtime.getConstantInvalidator().getData() &&
+ generation == invalidator(context).getData() &&
hash == target.hashCode();
}
public IRubyObject reCache(ThreadContext context, RubyModule target) {
- Object newGeneration = context.runtime.getConstantInvalidator().getData();
+ Object newGeneration = invalidator(context).getData();
IRubyObject value = target.getConstantFromNoConstMissing(name, false);
cachedValue = value;
@@ -43,6 +43,7 @@
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.runtime.opto.Invalidator;
import org.jruby.util.ByteList;
import org.jruby.util.DefinedMessage;
@@ -54,6 +55,7 @@
protected String name;
private volatile transient IRubyObject cachedValue;
private volatile Object generation;
+ private Invalidator invalidator;
public Colon3Node(ISourcePosition position, String name) {
super(position);
@@ -86,6 +88,7 @@ public String getName() {
public void setName(String name) {
this.name = name;
+ this.invalidator = null;
}
/** Get parent module/class that this module represents */
@@ -132,12 +135,12 @@ public IRubyObject getValue(ThreadContext context) {
}
private boolean isCached(ThreadContext context, IRubyObject value) {
- return value != null && generation == context.runtime.getConstantInvalidator().getData();
+ return value != null && generation == invalidator(context).getData();
}
public IRubyObject reCache(ThreadContext context, String name) {
Ruby runtime = context.runtime;
- Object newGeneration = runtime.getConstantInvalidator().getData();
+ Object newGeneration = invalidator(context).getData();
IRubyObject value = runtime.getObject().getConstantFromNoConstMissing(name, false);
cachedValue = value;
@@ -146,4 +149,11 @@ public IRubyObject reCache(ThreadContext context, String name) {
return value;
}
+
+ protected Invalidator invalidator(ThreadContext context) {
+ if (invalidator == null) {
+ invalidator = context.runtime.getConstantInvalidator(name);
+ }
+ return invalidator;
+ }
}
@@ -41,6 +41,7 @@
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.runtime.opto.Invalidator;
import org.jruby.util.ByteList;
import org.jruby.util.DefinedMessage;
@@ -51,6 +52,7 @@
private String name;
private volatile transient IRubyObject cachedValue = null;
private Object generation = -1;
+ private Invalidator invalidator;
public ConstNode(ISourcePosition position, String name) {
super(position);
@@ -83,6 +85,7 @@ public String getName() {
public void setName(String name) {
this.name = name;
+ this.invalidator = null;
}
@Override
@@ -106,17 +109,25 @@ public IRubyObject getValue(ThreadContext context) {
}
private boolean isCached(ThreadContext context, IRubyObject value) {
- return value != null && generation == context.runtime.getConstantInvalidator().getData();
+ return value != null && generation == invalidator(context).getData();
}
public IRubyObject reCache(ThreadContext context, String name) {
- Object newGeneration = context.runtime.getConstantInvalidator().getData();
+ Object newGeneration = invalidator(context).getData();
IRubyObject value = context.getCurrentStaticScope().getConstant(name);
+ this.name = name;
cachedValue = value;
if (value != null) generation = newGeneration;
return value;
}
+
+ private Invalidator invalidator(ThreadContext context) {
+ if (invalidator == null) {
+ invalidator = context.runtime.getConstantInvalidator(name);
+ }
+ return invalidator;
+ }
}
@@ -25,6 +25,7 @@
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CacheEntry;
+import org.jruby.runtime.opto.Invalidator;
import org.jruby.util.ByteList;
import org.jruby.util.DefinedMessage;
import org.jruby.util.RegexpOptions;
@@ -375,6 +376,7 @@ public final void initConstants(int size) {
constants = new IRubyObject[size];
constantTargetHashes = new int[size];
constantGenerations = new Object[size];
+ constantInvalidators = new Invalidator[size];
Arrays.fill(constantGenerations, -1);
Arrays.fill(constantTargetHashes, -1);
}
@@ -411,13 +413,15 @@ public IRubyObject getValue(ThreadContext context, StaticScope scope, String nam
}
private boolean isCached(ThreadContext context, IRubyObject value, int index) {
- return value != null && constantGenerations[index] == context.runtime.getConstantInvalidator().getData();
+ return value != null && constantGenerations[index] == constantInvalidators[index].getData();
}
public IRubyObject reCache(ThreadContext context, StaticScope scope, String name, int index) {
- Object newGeneration = context.runtime.getConstantInvalidator().getData();
+ Invalidator invalidator = context.runtime.getConstantInvalidator(name);
+ Object newGeneration = invalidator.getData();
IRubyObject value = scope.getConstant(name);
constants[index] = value;
+ constantInvalidators[index] = invalidator;
if (value != null) {
constantGenerations[index] = newGeneration;
}
@@ -436,16 +440,17 @@ public IRubyObject getValueFrom(RubyModule target, ThreadContext context, String
}
private boolean isCachedFrom(RubyModule target, ThreadContext context, IRubyObject value, int index) {
- return value != null && constantGenerations[index] == context.runtime.getConstantInvalidator().getData() && constantTargetHashes[index] == target.hashCode();
+ return value != null && constantGenerations[index] == constantInvalidators[index].getData() && constantTargetHashes[index] == target.hashCode();
}
public IRubyObject reCacheFrom(RubyModule target, ThreadContext context, String name, int index) {
- Object newGeneration = context.runtime.getConstantInvalidator().getData();
+ Object newGeneration = context.runtime.getConstantInvalidator(name).getData();
IRubyObject value = target.getConstantFromNoConstMissing(name, false);
constants[index] = value;
if (value != null) {
constantGenerations[index] = newGeneration;
constantTargetHashes[index] = target.hashCode();
+ constantInvalidators[index] = context.runtime.getConstantInvalidator(name);
}
return value;
}
@@ -673,4 +678,5 @@ private CacheEntry getCacheEntry(int index) {
private static final Object[] EMPTY_OBJS = {};
public Object[] constantGenerations = EMPTY_OBJS;
public int[] constantTargetHashes = EMPTY_INTS;
+ public Invalidator[] constantInvalidators = {};
}
@@ -132,7 +132,7 @@ public void inject() {
if (rubyModule == null) return;
rubyModule.storeConstant(name, irubyObject);
- receiver.getRuntime().getConstantInvalidator().invalidate();
+ receiver.getRuntime().getConstantInvalidator(name).invalidate();
fromRuby = true;
}
@@ -225,7 +225,7 @@ public void inject() {
} else {
receiver.getMetaClass().storeConstant(name, irubyObject);
}
- receiver.getRuntime().getConstantInvalidator().invalidate();
+ receiver.getRuntime().getConstantInvalidator(name).invalidate();
initialized = true;
}
Oops, something went wrong.