Skip to content
This repository has been archived by the owner before Nov 9, 2022. It is now read-only.
Permalink
Browse files
8257189: Handle concurrent updates of MH.form better
Reviewed-by: redestad, psandoz
  • Loading branch information
Vladimir Ivanov committed Dec 2, 2020
1 parent 6704266 commit 692b273ec53f54a879a4bbaad6c2f5f1d5358a71
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 53 deletions.
@@ -36,6 +36,7 @@
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 @@ protected WeakReference<Thread> computeValue(Class<?> type) {
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 @@ static MethodHandle getCallSiteTarget(CallSite site) {
@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 @@ private static boolean namesOK(int arity, Name[] names) {

/** 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 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 @@ Object internalProperties() {
*/
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 @@ Object internalProperties() {
* 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 @@ public MethodHandle asTypeUncached(MethodType newType) {
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 @@ boolean countDown() {

@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;
}});
}
}

0 comments on commit 692b273

Please sign in to comment.