8257189: Handle concurrent updates of MH.form better
Reviewed-by: redestad, psandoz
This commit is contained in:
parent
670426646d
commit
692b273ec5
@ -36,6 +36,7 @@ import sun.invoke.util.Wrapper;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.LambdaForm.Kind.*;
|
||||
@ -387,10 +388,12 @@ class DirectMethodHandle extends MethodHandle {
|
||||
private void ensureInitialized() {
|
||||
if (checkInitialized(member)) {
|
||||
// The coast is clear. Delete the <clinit> barrier.
|
||||
if (member.isField())
|
||||
updateForm(preparedFieldLambdaForm(member));
|
||||
else
|
||||
updateForm(preparedLambdaForm(member));
|
||||
updateForm(new Function<>() {
|
||||
public LambdaForm apply(LambdaForm oldForm) {
|
||||
return (member.isField() ? preparedFieldLambdaForm(member)
|
||||
: preparedLambdaForm(member));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
private static boolean checkInitialized(MemberName member) {
|
||||
|
@ -596,21 +596,17 @@ class Invokers {
|
||||
@ForceInline
|
||||
/*non-public*/
|
||||
static void checkCustomized(MethodHandle mh) {
|
||||
if (MethodHandleImpl.isCompileConstant(mh)) return;
|
||||
if (mh.form.customized == null) {
|
||||
maybeCustomize(mh);
|
||||
if (MethodHandleImpl.isCompileConstant(mh)) {
|
||||
return; // no need to customize a MH when the instance is known to JIT
|
||||
}
|
||||
if (mh.form.customized == null) { // fast approximate check that the underlying form is already customized
|
||||
maybeCustomize(mh); // marked w/ @DontInline
|
||||
}
|
||||
}
|
||||
|
||||
@DontInline
|
||||
/*non-public*/
|
||||
static void maybeCustomize(MethodHandle mh) {
|
||||
byte count = mh.customizationCount;
|
||||
if (count >= CUSTOMIZE_THRESHOLD) {
|
||||
mh.customize();
|
||||
} else {
|
||||
mh.customizationCount = (byte)(count+1);
|
||||
}
|
||||
mh.maybeCustomize();
|
||||
}
|
||||
|
||||
// Local constant functions:
|
||||
|
@ -501,6 +501,9 @@ class LambdaForm {
|
||||
|
||||
/** Customize LambdaForm for a particular MethodHandle */
|
||||
LambdaForm customize(MethodHandle mh) {
|
||||
if (customized == mh) {
|
||||
return this;
|
||||
}
|
||||
LambdaForm customForm = new LambdaForm(arity, names, result, forceInline, mh, kind);
|
||||
if (COMPILE_THRESHOLD >= 0 && isCompiled) {
|
||||
// If shared LambdaForm has been compiled, compile customized version as well.
|
||||
|
@ -30,13 +30,13 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.Constable;
|
||||
import java.lang.constant.ConstantDesc;
|
||||
import java.lang.constant.DirectMethodHandleDesc;
|
||||
import java.lang.constant.MethodHandleDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.lang.invoke.MethodHandleInfo.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
@ -455,9 +455,8 @@ public abstract class MethodHandle implements Constable {
|
||||
/*private*/
|
||||
MethodHandle asTypeCache;
|
||||
// asTypeCache is not private so that invokers can easily fetch it
|
||||
/*non-public*/
|
||||
byte customizationCount;
|
||||
// customizationCount should be accessible from invokers
|
||||
|
||||
private byte customizationCount;
|
||||
|
||||
/**
|
||||
* Reports the type of this method handle.
|
||||
@ -1733,6 +1732,30 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
*/
|
||||
abstract BoundMethodHandle rebind();
|
||||
|
||||
/* non-public */
|
||||
void maybeCustomize() {
|
||||
if (form.customized == null) {
|
||||
byte count = customizationCount;
|
||||
if (count >= CUSTOMIZE_THRESHOLD) {
|
||||
customize();
|
||||
} else {
|
||||
customizationCount = (byte) (count + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Craft a LambdaForm customized for this particular MethodHandle. */
|
||||
/*non-public*/
|
||||
void customize() {
|
||||
updateForm(new Function<>() {
|
||||
public LambdaForm apply(LambdaForm oldForm) {
|
||||
return oldForm.customize(MethodHandle.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private volatile boolean updateInProgress; // = false;
|
||||
|
||||
/**
|
||||
* Replace the old lambda form of this method handle with a new one.
|
||||
* The new one must be functionally equivalent to the old one.
|
||||
@ -1741,26 +1764,26 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
* Use with discretion.
|
||||
*/
|
||||
/*non-public*/
|
||||
void updateForm(LambdaForm newForm) {
|
||||
assert(newForm.customized == null || newForm.customized == this);
|
||||
if (form == newForm) return;
|
||||
newForm.prepare(); // as in MethodHandle.<init>
|
||||
UNSAFE.putReference(this, FORM_OFFSET, newForm);
|
||||
UNSAFE.fullFence();
|
||||
}
|
||||
|
||||
/** Craft a LambdaForm customized for this particular MethodHandle */
|
||||
/*non-public*/
|
||||
void customize() {
|
||||
final LambdaForm form = this.form;
|
||||
if (form.customized == null) {
|
||||
LambdaForm newForm = form.customize(this);
|
||||
updateForm(newForm);
|
||||
void updateForm(Function<LambdaForm, LambdaForm> updater) {
|
||||
if (UNSAFE.compareAndSetBoolean(this, UPDATE_OFFSET, false, true)) { // updateInProgress = true
|
||||
// Only 1 thread wins the race and updates MH.form field.
|
||||
try {
|
||||
LambdaForm oldForm = form;
|
||||
LambdaForm newForm = updater.apply(oldForm);
|
||||
if (oldForm != newForm) {
|
||||
assert (newForm.customized == null || newForm.customized == this);
|
||||
newForm.prepare(); // as in MethodHandle.<init>
|
||||
UNSAFE.putReference(this, FORM_OFFSET, newForm);
|
||||
UNSAFE.fullFence();
|
||||
}
|
||||
} finally {
|
||||
updateInProgress = false;
|
||||
}
|
||||
} else {
|
||||
assert(form.customized == this);
|
||||
// Update got lost due to concurrent update. But callers don't care.
|
||||
}
|
||||
}
|
||||
|
||||
private static final long FORM_OFFSET
|
||||
= UNSAFE.objectFieldOffset(MethodHandle.class, "form");
|
||||
private static final long FORM_OFFSET = UNSAFE.objectFieldOffset(MethodHandle.class, "form");
|
||||
private static final long UPDATE_OFFSET = UNSAFE.objectFieldOffset(MethodHandle.class, "updateInProgress");
|
||||
}
|
||||
|
@ -841,21 +841,9 @@ abstract class MethodHandleImpl {
|
||||
return (asTypeCache = wrapper);
|
||||
}
|
||||
|
||||
// Customize target if counting happens for too long.
|
||||
private int invocations = CUSTOMIZE_THRESHOLD;
|
||||
private void maybeCustomizeTarget() {
|
||||
int c = invocations;
|
||||
if (c >= 0) {
|
||||
if (c == 1) {
|
||||
target.customize();
|
||||
}
|
||||
invocations = c - 1;
|
||||
}
|
||||
}
|
||||
|
||||
boolean countDown() {
|
||||
int c = count;
|
||||
maybeCustomizeTarget();
|
||||
target.maybeCustomize(); // customize if counting happens for too long
|
||||
if (c <= 1) {
|
||||
// Try to limit number of updates. MethodHandle.updateForm() doesn't guarantee LF update visibility.
|
||||
if (isCounting) {
|
||||
@ -872,12 +860,15 @@ abstract class MethodHandleImpl {
|
||||
|
||||
@Hidden
|
||||
static void maybeStopCounting(Object o1) {
|
||||
CountingWrapper wrapper = (CountingWrapper) o1;
|
||||
final CountingWrapper wrapper = (CountingWrapper) o1;
|
||||
if (wrapper.countDown()) {
|
||||
// Reached invocation threshold. Replace counting behavior with a non-counting one.
|
||||
LambdaForm lform = wrapper.nonCountingFormProducer.apply(wrapper.target);
|
||||
lform.compileToBytecode(); // speed up warmup by avoiding LF interpretation again after transition
|
||||
wrapper.updateForm(lform);
|
||||
wrapper.updateForm(new Function<>() {
|
||||
public LambdaForm apply(LambdaForm oldForm) {
|
||||
LambdaForm lform = wrapper.nonCountingFormProducer.apply(wrapper.target);
|
||||
lform.compileToBytecode(); // speed up warmup by avoiding LF interpretation again after transition
|
||||
return lform;
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user