8079205: CallSite dependency tracking is broken after sun.misc.Cleaner became automatically cleared

Reviewed-by: roland, psandoz, plevart, kbarrett, jrose
This commit is contained in:
Vladimir Ivanov 2015-05-15 19:23:27 +03:00
parent 937fb712ee
commit 6652d6ac33
2 changed files with 24 additions and 45 deletions

View File

@ -27,8 +27,6 @@ package java.lang.invoke;
import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
import java.lang.reflect.Field;
import sun.misc.Cleaner;
/** /**
* A {@code CallSite} is a holder for a variable {@link MethodHandle}, * A {@code CallSite} is a holder for a variable {@link MethodHandle},
@ -138,47 +136,9 @@ public class CallSite {
/** /**
* {@code CallSite} dependency context. * {@code CallSite} dependency context.
* VM uses context class to store nmethod dependencies on the call site target. * JVM uses CallSite.context to store nmethod dependencies on the call site target.
* Can be in 2 states: (a) null; or (b) {@code Cleaner} instance pointing to some Class instance.
* Lazily initialized when CallSite instance is linked to some indy call site or VM needs
* it to store dependencies. As a corollary, "null" context means there are no dependencies
* registered yet. {@code Cleaner} is used in 2 roles:
* (a) context class access for VM;
* (b) stale context class cleanup.
* {@code Cleaner} holds the context class until cleanup action is finished (see {@code PhantomReference}).
* Though it's impossible to get the context class using {@code Reference.get()}, VM extracts it directly
* from {@code Reference.referent} field.
*/ */
private volatile Cleaner context = null; private final MethodHandleNatives.CallSiteContext context = MethodHandleNatives.CallSiteContext.make(this);
/**
* Default context.
* VM uses it to initialize non-linked CallSite context.
*/
private static class DefaultContext {}
private static final Cleaner DEFAULT_CONTEXT = makeContext(DefaultContext.class, null);
private static Cleaner makeContext(Class<?> referent, final CallSite holder) {
return Cleaner.create(referent,
new Runnable() {
@Override public void run() {
MethodHandleNatives.invalidateDependentNMethods(holder);
}
});
}
/** Initialize context class used for nmethod dependency tracking */
/*package-private*/
void initContext(Class<?> newContext) {
// If there are concurrent actions, exactly one succeeds.
if (context == null) {
UNSAFE.compareAndSwapObject(this, CONTEXT_OFFSET, /*expected=*/null, makeContext(newContext, this));
// No need to care about failed CAS attempt.
// Since initContext is called from indy call site linkage in newContext class, there's no risk
// that the context class becomes dead while corresponding context cleaner is alive (causing cleanup
// action in the wrong context).
}
}
/** /**
* Returns the type of this call site's target. * Returns the type of this call site's target.

View File

@ -30,6 +30,7 @@ import java.lang.reflect.Field;
import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
import sun.misc.Cleaner;
/** /**
* The JVM interface for the method handles package is all here. * The JVM interface for the method handles package is all here.
@ -61,8 +62,27 @@ class MethodHandleNatives {
static native void setCallSiteTargetNormal(CallSite site, MethodHandle target); static native void setCallSiteTargetNormal(CallSite site, MethodHandle target);
static native void setCallSiteTargetVolatile(CallSite site, MethodHandle target); static native void setCallSiteTargetVolatile(CallSite site, MethodHandle target);
/** Invalidate CallSite context: clean up dependent nmethods and reset call site context to initial state (null). */ /** Represents a context to track nmethod dependencies on CallSite instance target. */
static native void invalidateDependentNMethods(CallSite site); static class CallSiteContext implements Runnable {
//@Injected JVM_nmethodBucket* vmdependencies;
static CallSiteContext make(CallSite cs) {
final CallSiteContext newContext = new CallSiteContext();
// Cleaner is attached to CallSite instance and it clears native structures allocated for CallSite context.
// Though the CallSite can become unreachable, its Context is retained by the Cleaner instance (which is
// referenced from Cleaner class) until cleanup is performed.
Cleaner.create(cs, newContext);
return newContext;
}
@Override
public void run() {
MethodHandleNatives.clearCallSiteContext(this);
}
}
/** Invalidate all recorded nmethods. */
private static native void clearCallSiteContext(CallSiteContext context);
private static native void registerNatives(); private static native void registerNatives();
static { static {
@ -235,7 +255,6 @@ class MethodHandleNatives {
return Invokers.linkToTargetMethod(type); return Invokers.linkToTargetMethod(type);
} else { } else {
appendixResult[0] = callSite; appendixResult[0] = callSite;
callSite.initContext(caller);
return Invokers.linkToCallSiteMethod(type); return Invokers.linkToCallSiteMethod(type);
} }
} }