Skip to content
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

Named based constant cache invalidation #798

Merged
merged 8 commits into from Jun 11, 2013
34 changes: 22 additions & 12 deletions src/org/jruby/Ruby.java
Expand Up @@ -135,6 +135,7 @@
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.*; import java.util.*;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue; import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
Expand Down Expand Up @@ -219,7 +220,6 @@ private Ruby(RubyInstanceConfig config) {
this.runtimeCache = new RuntimeCache(); this.runtimeCache = new RuntimeCache();
runtimeCache.initMethodCache(ClassIndex.MAX_CLASSES * MethodNames.values().length - 1); runtimeCache.initMethodCache(ClassIndex.MAX_CLASSES * MethodNames.values().length - 1);


constantInvalidator = OptoFactory.newConstantInvalidator();
checkpointInvalidator = OptoFactory.newConstantInvalidator(); checkpointInvalidator = OptoFactory.newConstantInvalidator();


if (config.isObjectSpaceEnabled()) { if (config.isObjectSpaceEnabled()) {
Expand Down Expand Up @@ -4181,23 +4181,28 @@ public int getConstantGeneration() {
return -1; return -1;
} }


@Deprecated public Invalidator getConstantInvalidator(String constantName) {
public synchronized void incrementConstantGeneration() { Invalidator invalidator = constantNameInvalidators.get(constantName);
constantInvalidator.invalidate(); if (invalidator != null) {
return invalidator;
} else {
return addConstantInvalidator(constantName);
}
} }


public Invalidator getConstantInvalidator() { private Invalidator addConstantInvalidator(String constantName) {
return constantInvalidator; 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() { public Invalidator getCheckpointInvalidator() {
return checkpointInvalidator; return checkpointInvalidator;
} }


public void invalidateConstants() {

}

public <E extends Enum<E>> void loadConstantSet(RubyModule module, Class<E> enumClass) { public <E extends Enum<E>> void loadConstantSet(RubyModule module, Class<E> enumClass) {
for (E e : EnumSet.allOf(enumClass)) { for (E e : EnumSet.allOf(enumClass)) {
Constant c = (Constant) e; Constant c = (Constant) e;
Expand Down Expand Up @@ -4467,7 +4472,12 @@ public CallbackFactory callbackFactory(Class<?> type) {
throw new RuntimeException("callback-style handles are no longer supported in JRuby"); 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 Invalidator checkpointInvalidator;
private final ThreadService threadService; private final ThreadService threadService;


Expand Down
45 changes: 24 additions & 21 deletions src/org/jruby/RubyModule.java
Expand Up @@ -986,9 +986,8 @@ private CacheEntry cacheHit(String name) {
private void invalidateConstantCacheForModuleInclusion(RubyModule module) private void invalidateConstantCacheForModuleInclusion(RubyModule module)
{ {
for (RubyModule mod : gatherModules(module)) { for (RubyModule mod : gatherModules(module)) {
if (!mod.getConstantMap().isEmpty()) { for (String key : mod.getConstantMap().keySet()) {
invalidateConstantCache(); invalidateConstantCache(key);
break;
} }
} }
} }
Expand Down Expand Up @@ -1144,8 +1143,8 @@ protected void invalidateCacheDescendantsInner() {
methodInvalidator.invalidate(); methodInvalidator.invalidate();
} }


protected void invalidateConstantCache() { protected void invalidateConstantCache(String constantName) {
getRuntime().getConstantInvalidator().invalidate(); getRuntime().getConstantInvalidator(constantName).invalidate();
} }


/** /**
Expand Down Expand Up @@ -2619,7 +2618,7 @@ public IRubyObject remove_const(ThreadContext context, IRubyObject rubyName) {
String name = validateConstant(rubyName.asJavaString()); String name = validateConstant(rubyName.asJavaString());
IRubyObject value; IRubyObject value;
if ((value = deleteConstant(name)) != null) { if ((value = deleteConstant(name)) != null) {
invalidateConstantCache(); invalidateConstantCache(name);
if (value != UNDEF) { if (value != UNDEF) {
return value; return value;
} }
Expand Down Expand Up @@ -2731,34 +2730,38 @@ public Collection<String> constantsCommon(Ruby runtime, boolean replaceModule, b
} }


@JRubyMethod(compat = RUBY1_9) @JRubyMethod(compat = RUBY1_9)
public IRubyObject private_constant(ThreadContext context, IRubyObject name) { public IRubyObject private_constant(ThreadContext context, IRubyObject rubyName) {
setConstantVisibility(context, validateConstant(name.asJavaString()), true); String name = rubyName.asJavaString();
invalidateConstantCache(); setConstantVisibility(context, validateConstant(name), true);
invalidateConstantCache(name);
return this; return this;
} }


@JRubyMethod(compat = RUBY1_9, required = 1, rest = true) @JRubyMethod(compat = RUBY1_9, required = 1, rest = true)
public IRubyObject private_constant(ThreadContext context, IRubyObject[] names) { public IRubyObject private_constant(ThreadContext context, IRubyObject[] rubyNames) {
for (IRubyObject name : names) { for (IRubyObject rubyName : rubyNames) {
setConstantVisibility(context, validateConstant(name.asJavaString()), true); String name = rubyName.asJavaString();
setConstantVisibility(context, validateConstant(name), true);
invalidateConstantCache(name);
} }
invalidateConstantCache();
return this; return this;
} }


@JRubyMethod(compat = RUBY1_9) @JRubyMethod(compat = RUBY1_9)
public IRubyObject public_constant(ThreadContext context, IRubyObject name) { public IRubyObject public_constant(ThreadContext context, IRubyObject rubyName) {
setConstantVisibility(context, validateConstant(name.asJavaString()), false); String name = rubyName.asJavaString();
invalidateConstantCache(); setConstantVisibility(context, validateConstant(name), false);
invalidateConstantCache(name);
return this; return this;
} }


@JRubyMethod(compat = RUBY1_9, required = 1, rest = true) @JRubyMethod(compat = RUBY1_9, required = 1, rest = true)
public IRubyObject public_constant(ThreadContext context, IRubyObject[] names) { public IRubyObject public_constant(ThreadContext context, IRubyObject[] rubyNames) {
for (IRubyObject name : names) { for (IRubyObject rubyName : rubyNames) {
setConstantVisibility(context, validateConstant(name.asJavaString()), false); String name = rubyName.asJavaString();
setConstantVisibility(context, validateConstant(name), false);
invalidateConstantCache(name);
} }
invalidateConstantCache();
return this; return this;
} }


Expand Down Expand Up @@ -3082,7 +3085,7 @@ private IRubyObject setConstantCommon(String name, IRubyObject value, boolean wa
storeConstant(name, value); storeConstant(name, value);
} }


invalidateConstantCache(); invalidateConstantCache(name);


// if adding a module under a constant name, set that module's basename to the constant name // if adding a module under a constant name, set that module's basename to the constant name
if (value instanceof RubyModule) { if (value instanceof RubyModule) {
Expand Down
4 changes: 2 additions & 2 deletions src/org/jruby/ast/Colon2ConstNode.java
Expand Up @@ -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 // We could probably also detect if LHS value came out of cache and avoid some of this
return return
value != null && value != null &&
generation == context.runtime.getConstantInvalidator().getData() && generation == invalidator(context).getData() &&
hash == target.hashCode(); hash == target.hashCode();
} }


public IRubyObject reCache(ThreadContext context, RubyModule target) { public IRubyObject reCache(ThreadContext context, RubyModule target) {
Object newGeneration = context.runtime.getConstantInvalidator().getData(); Object newGeneration = invalidator(context).getData();
IRubyObject value = target.getConstantFromNoConstMissing(name, false); IRubyObject value = target.getConstantFromNoConstMissing(name, false);


cachedValue = value; cachedValue = value;
Expand Down
14 changes: 12 additions & 2 deletions src/org/jruby/ast/Colon3Node.java
Expand Up @@ -43,6 +43,7 @@
import org.jruby.runtime.Block; import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext; import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.opto.Invalidator;
import org.jruby.util.ByteList; import org.jruby.util.ByteList;
import org.jruby.util.DefinedMessage; import org.jruby.util.DefinedMessage;


Expand All @@ -54,6 +55,7 @@ public class Colon3Node extends Node implements INameNode {
protected String name; protected String name;
private volatile transient IRubyObject cachedValue; private volatile transient IRubyObject cachedValue;
private volatile Object generation; private volatile Object generation;
private Invalidator invalidator;


public Colon3Node(ISourcePosition position, String name) { public Colon3Node(ISourcePosition position, String name) {
super(position); super(position);
Expand Down Expand Up @@ -86,6 +88,7 @@ public List<Node> childNodes() {


public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
this.invalidator = null;
} }


/** Get parent module/class that this module represents */ /** Get parent module/class that this module represents */
Expand Down Expand Up @@ -132,12 +135,12 @@ public IRubyObject getValue(ThreadContext context) {
} }


private boolean isCached(ThreadContext context, IRubyObject value) { 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) { public IRubyObject reCache(ThreadContext context, String name) {
Ruby runtime = context.runtime; Ruby runtime = context.runtime;
Object newGeneration = runtime.getConstantInvalidator().getData(); Object newGeneration = invalidator(context).getData();
IRubyObject value = runtime.getObject().getConstantFromNoConstMissing(name, false); IRubyObject value = runtime.getObject().getConstantFromNoConstMissing(name, false);


cachedValue = value; cachedValue = value;
Expand All @@ -146,4 +149,11 @@ public IRubyObject reCache(ThreadContext context, String name) {


return value; return value;
} }

protected Invalidator invalidator(ThreadContext context) {
if (invalidator == null) {
invalidator = context.runtime.getConstantInvalidator(name);
}
return invalidator;
}
} }
15 changes: 13 additions & 2 deletions src/org/jruby/ast/ConstNode.java
Expand Up @@ -41,6 +41,7 @@
import org.jruby.runtime.Block; import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext; import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.opto.Invalidator;
import org.jruby.util.ByteList; import org.jruby.util.ByteList;
import org.jruby.util.DefinedMessage; import org.jruby.util.DefinedMessage;


Expand All @@ -51,6 +52,7 @@ public class ConstNode extends Node implements INameNode {
private String name; private String name;
private volatile transient IRubyObject cachedValue = null; private volatile transient IRubyObject cachedValue = null;
private Object generation = -1; private Object generation = -1;
private Invalidator invalidator;


public ConstNode(ISourcePosition position, String name) { public ConstNode(ISourcePosition position, String name) {
super(position); super(position);
Expand Down Expand Up @@ -83,6 +85,7 @@ public List<Node> childNodes() {


public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
this.invalidator = null;
} }


@Override @Override
Expand All @@ -106,17 +109,25 @@ public IRubyObject getValue(ThreadContext context) {
} }


private boolean isCached(ThreadContext context, IRubyObject value) { 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) { public IRubyObject reCache(ThreadContext context, String name) {
Object newGeneration = context.runtime.getConstantInvalidator().getData(); Object newGeneration = invalidator(context).getData();
IRubyObject value = context.getCurrentStaticScope().getConstant(name); IRubyObject value = context.getCurrentStaticScope().getConstant(name);
this.name = name;


cachedValue = value; cachedValue = value;


if (value != null) generation = newGeneration; if (value != null) generation = newGeneration;


return value; return value;
} }

private Invalidator invalidator(ThreadContext context) {
if (invalidator == null) {
invalidator = context.runtime.getConstantInvalidator(name);
}
return invalidator;
}
} }
14 changes: 10 additions & 4 deletions src/org/jruby/ast/executable/RuntimeCache.java
Expand Up @@ -25,6 +25,7 @@
import org.jruby.runtime.ThreadContext; import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CacheEntry; import org.jruby.runtime.callsite.CacheEntry;
import org.jruby.runtime.opto.Invalidator;
import org.jruby.util.ByteList; import org.jruby.util.ByteList;
import org.jruby.util.DefinedMessage; import org.jruby.util.DefinedMessage;
import org.jruby.util.RegexpOptions; import org.jruby.util.RegexpOptions;
Expand Down Expand Up @@ -375,6 +376,7 @@ public final void initConstants(int size) {
constants = new IRubyObject[size]; constants = new IRubyObject[size];
constantTargetHashes = new int[size]; constantTargetHashes = new int[size];
constantGenerations = new Object[size]; constantGenerations = new Object[size];
constantInvalidators = new Invalidator[size];
Arrays.fill(constantGenerations, -1); Arrays.fill(constantGenerations, -1);
Arrays.fill(constantTargetHashes, -1); Arrays.fill(constantTargetHashes, -1);
} }
Expand Down Expand Up @@ -411,13 +413,15 @@ public IRubyObject getValue(ThreadContext context, StaticScope scope, String nam
} }


private boolean isCached(ThreadContext context, IRubyObject value, int index) { 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) { 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); IRubyObject value = scope.getConstant(name);
constants[index] = value; constants[index] = value;
constantInvalidators[index] = invalidator;
if (value != null) { if (value != null) {
constantGenerations[index] = newGeneration; constantGenerations[index] = newGeneration;
} }
Expand All @@ -436,16 +440,17 @@ public IRubyObject getValueFrom(RubyModule target, ThreadContext context, String
} }


private boolean isCachedFrom(RubyModule target, ThreadContext context, IRubyObject value, int index) { 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) { 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); IRubyObject value = target.getConstantFromNoConstMissing(name, false);
constants[index] = value; constants[index] = value;
if (value != null) { if (value != null) {
constantGenerations[index] = newGeneration; constantGenerations[index] = newGeneration;
constantTargetHashes[index] = target.hashCode(); constantTargetHashes[index] = target.hashCode();
constantInvalidators[index] = context.runtime.getConstantInvalidator(name);
} }
return value; return value;
} }
Expand Down Expand Up @@ -673,4 +678,5 @@ private CacheEntry getCacheEntry(int index) {
private static final Object[] EMPTY_OBJS = {}; private static final Object[] EMPTY_OBJS = {};
public Object[] constantGenerations = EMPTY_OBJS; public Object[] constantGenerations = EMPTY_OBJS;
public int[] constantTargetHashes = EMPTY_INTS; public int[] constantTargetHashes = EMPTY_INTS;
public Invalidator[] constantInvalidators = {};
} }
2 changes: 1 addition & 1 deletion src/org/jruby/embed/variable/Argv.java
Expand Up @@ -132,7 +132,7 @@ public void inject() {
if (rubyModule == null) return; if (rubyModule == null) return;


rubyModule.storeConstant(name, irubyObject); rubyModule.storeConstant(name, irubyObject);
receiver.getRuntime().getConstantInvalidator().invalidate(); receiver.getRuntime().getConstantInvalidator(name).invalidate();
fromRuby = true; fromRuby = true;
} }


Expand Down
2 changes: 1 addition & 1 deletion src/org/jruby/embed/variable/Constant.java
Expand Up @@ -225,7 +225,7 @@ public void inject() {
} else { } else {
receiver.getMetaClass().storeConstant(name, irubyObject); receiver.getMetaClass().storeConstant(name, irubyObject);
} }
receiver.getRuntime().getConstantInvalidator().invalidate(); receiver.getRuntime().getConstantInvalidator(name).invalidate();
initialized = true; initialized = true;
} }


Expand Down