move AtomicFieldUpdater to 11-enabled "concurrency" and port to VarHandle (IDEA-244473 port Collections/Util classes to VarHandles available in JDK 11)

GitOrigin-RevId: 6d01dd54261459f231dfece52305a97985da58db
This commit is contained in:
Alexey Kudravtsev
2020-12-14 11:40:10 +00:00
committed by intellij-monorepo-bot
parent 858e86bb68
commit a9742f941a
3 changed files with 83 additions and 126 deletions
@@ -44,6 +44,7 @@ import com.intellij.ui.PlaceHolder;
import com.intellij.util.Alarm;
import com.intellij.util.PathUtil;
import com.intellij.util.SystemProperties;
import com.intellij.util.concurrency.AtomicFieldUpdater;
import com.intellij.util.containers.ContainerUtil;
import kotlin.Unit;
import kotlin.reflect.full.NoSuchPropertyException;
@@ -54,7 +55,8 @@ import java.io.File;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@@ -107,7 +109,7 @@ public final class RemoteExternalSystemCommunicationManager implements ExternalS
params.setWorkingDirectory(myWorkingDirectory.isDirectory() ? myWorkingDirectory.getPath() : PathManager.getBinPath());
// IDE jars.
List<String> classPath = new ArrayList<>(PathManager.getUtilClassPath());
Collection<String> classPath = new LinkedHashSet<>(PathManager.getUtilClassPath());
ContainerUtil.addIfNotNull(classPath, PathUtil.getJarPathForClass(Project.class)); //intellij.platform.core
ContainerUtil.addIfNotNull(classPath, PathUtil.getJarPathForClass(PlaceHolder.class)); //intellij.platform.editor
ContainerUtil.addIfNotNull(classPath, PathUtil.getJarPathForClass(DependencyScope.class)); //intellij.platform.projectModel
@@ -116,6 +118,7 @@ public final class RemoteExternalSystemCommunicationManager implements ExternalS
ContainerUtil.addIfNotNull(classPath, PathUtil.getJarPathForClass(ExtensionPointName.class)); //intellij.platform.extensions
ContainerUtil.addIfNotNull(classPath, PathUtil.getJarPathForClass(StorageUtilKt.class)); //intellij.platform.ide.impl
ContainerUtil.addIfNotNull(classPath, PathUtil.getJarPathForClass(ExternalSystemTaskNotificationListener.class)); //intellij.platform.externalSystem
ContainerUtil.addIfNotNull(classPath, PathUtil.getJarPathForClass(AtomicFieldUpdater.class)); //intellij.platform.concurrency
// java plugin jar if it's installed
Class<? extends SdkType> javaSdkClass = ExternalSystemJdkProvider.getInstance().getJavaSdkType().getClass();
@@ -136,7 +139,7 @@ public final class RemoteExternalSystemCommunicationManager implements ExternalS
// com.intellij.openapi.externalSystem.model.FSTSerializer dependencies
ContainerUtil.addIfNotNull(classPath, PathUtil.getJarPathForClass(ObjectSerializer.class));
params.getClassPath().addAll(classPath);
params.getClassPath().addAll(new ArrayList<>(classPath));
params.setMainClass(MAIN_CLASS_NAME);
params.getVMParametersList().addParametersString("-Djava.awt.headless=true");
@@ -0,0 +1,77 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.concurrency;
import com.intellij.util.ReflectionUtil;
import org.jetbrains.annotations.NotNull;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**
* Utility class similar to {@link java.util.concurrent.atomic.AtomicReferenceFieldUpdater} except:
* - removed access check in getAndSet() hot path for performance
* - new methods "forFieldXXX" added that search by field type instead of field name, which is useful in scrambled classes
*/
public final class AtomicFieldUpdater<ContainingClass, FieldType> {
private final VarHandle myHandle;
public static @NotNull <T, V> AtomicFieldUpdater<T, V> forFieldOfType(@NotNull Class<T> ownerClass, @NotNull Class<V> fieldType) {
return new AtomicFieldUpdater<>(ownerClass, fieldType);
}
public static @NotNull <T> AtomicFieldUpdater<T, Long> forLongFieldIn(@NotNull Class<T> ownerClass) {
return new AtomicFieldUpdater<>(ownerClass, long.class);
}
public static @NotNull <T> AtomicFieldUpdater<T, Integer> forIntFieldIn(@NotNull Class<T> ownerClass) {
return new AtomicFieldUpdater<>(ownerClass, int.class);
}
public static @NotNull <O,E> AtomicFieldUpdater<O, E> forField(@NotNull Field field) {
return new AtomicFieldUpdater<>(field);
}
private AtomicFieldUpdater(@NotNull Class<ContainingClass> ownerClass, @NotNull Class<FieldType> fieldType) {
this(ReflectionUtil.getTheOnlyVolatileInstanceFieldOfClass(ownerClass, fieldType));
}
private AtomicFieldUpdater(@NotNull Field field) {
field.setAccessible(true);
if (!Modifier.isVolatile(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) {
throw new IllegalArgumentException(field + " must be volatile instance");
}
try {
myHandle = MethodHandles
.privateLookupIn(field.getDeclaringClass(), MethodHandles.lookup())
.findVarHandle(field.getDeclaringClass(), field.getName(), field.getType());
}
catch (Throwable t) {
throw new RuntimeException(t);
}
}
public boolean compareAndSet(@NotNull ContainingClass owner, FieldType expected, FieldType newValue) {
return (boolean)myHandle.compareAndSet(owner, expected, newValue);
}
public boolean compareAndSetLong(@NotNull ContainingClass owner, long expected, long newValue) {
return (boolean)myHandle.compareAndSet(owner, expected, newValue);
}
public boolean compareAndSetInt(@NotNull ContainingClass owner, int expected, int newValue) {
return (boolean)myHandle.compareAndSet(owner, expected, newValue);
}
public void setVolatile(@NotNull ContainingClass owner, FieldType newValue) {
myHandle.setVolatile(owner, newValue);
}
public FieldType getVolatile(@NotNull ContainingClass owner) {
//noinspection unchecked
return (FieldType)myHandle.getVolatile(owner);
}
}
@@ -1,123 +0,0 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.concurrency;
import com.intellij.util.ReflectionUtil;
import org.jetbrains.annotations.NotNull;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**
* Utility class similar to {@link java.util.concurrent.atomic.AtomicReferenceFieldUpdater} except:
* - removed access check in getAndSet() hot path for performance
* - new methods "forFieldXXX" added that search by field type instead of field name, which is useful in scrambled classes
*/
public final class AtomicFieldUpdater<ContainingClass, FieldType> {
private static final Object unsafe;
private final long offset;
private static final MethodHandle compareAndSwapInt;
private static final MethodHandle compareAndSwapLong;
private static final MethodHandle compareAndSwapObject;
private static final MethodHandle putObjectVolatile;
private static final MethodHandle getObjectVolatile;
static {
unsafe = ReflectionUtil.getUnsafe();
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
try {
compareAndSwapInt = lookup.findVirtual(unsafe.getClass(), "compareAndSwapInt", MethodType.methodType(boolean.class, Object.class, long.class, int.class, int.class)).bindTo(unsafe);
compareAndSwapLong = lookup.findVirtual(unsafe.getClass(), "compareAndSwapLong", MethodType.methodType(boolean.class, Object.class, long.class, long.class, long.class)).bindTo(unsafe);
compareAndSwapObject = lookup.findVirtual(unsafe.getClass(), "compareAndSwapObject", MethodType.methodType(boolean.class, Object.class, long.class, Object.class, Object.class)).bindTo(unsafe);
putObjectVolatile = lookup.findVirtual(unsafe.getClass(), "putObjectVolatile", MethodType.methodType(void.class, Object.class, long.class, Object.class)).bindTo(unsafe);
getObjectVolatile = lookup.findVirtual(unsafe.getClass(), "getObjectVolatile", MethodType.methodType(Object.class, Object.class, long.class)).bindTo(unsafe);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public static @NotNull <T, V> AtomicFieldUpdater<T, V> forFieldOfType(@NotNull Class<T> ownerClass, @NotNull Class<V> fieldType) {
return new AtomicFieldUpdater<>(ownerClass, fieldType);
}
public static @NotNull <T> AtomicFieldUpdater<T, Long> forLongFieldIn(@NotNull Class<T> ownerClass) {
return new AtomicFieldUpdater<>(ownerClass, long.class);
}
public static @NotNull <T> AtomicFieldUpdater<T, Integer> forIntFieldIn(@NotNull Class<T> ownerClass) {
return new AtomicFieldUpdater<>(ownerClass, int.class);
}
public static @NotNull <O,E> AtomicFieldUpdater<O, E> forField(@NotNull Field field) {
return new AtomicFieldUpdater<>(field);
}
private AtomicFieldUpdater(@NotNull Class<ContainingClass> ownerClass, @NotNull Class<FieldType> fieldType) {
this(ReflectionUtil.getTheOnlyVolatileInstanceFieldOfClass(ownerClass, fieldType));
}
private AtomicFieldUpdater(@NotNull Field field) {
field.setAccessible(true);
if (!Modifier.isVolatile(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) throw new IllegalArgumentException(field + " must be volatile instance");
try {
MethodHandle objectFieldOffset =
MethodHandles.publicLookup().findVirtual(unsafe.getClass(), "objectFieldOffset", MethodType.methodType(long.class, Field.class));
offset = (long)objectFieldOffset.invoke(unsafe, field);
}
catch (Throwable t) {
throw new RuntimeException(t);
}
}
public boolean compareAndSet(@NotNull ContainingClass owner, FieldType expected, FieldType newValue) {
try {
return (boolean)compareAndSwapObject.invokeExact(owner, offset, expected, newValue);
}
catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
public boolean compareAndSetLong(@NotNull ContainingClass owner, long expected, long newValue) {
try {
return (boolean)compareAndSwapLong.invokeExact(owner, offset, expected, newValue);
}
catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
public boolean compareAndSetInt(@NotNull ContainingClass owner, int expected, int newValue) {
try {
return (boolean)compareAndSwapInt.invokeExact(owner, offset, expected, newValue);
}
catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
public void setVolatile(@NotNull ContainingClass owner, FieldType newValue) {
try {
putObjectVolatile.invokeExact(owner, offset, newValue);
}
catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
public FieldType getVolatile(@NotNull ContainingClass owner) {
try {
//noinspection unchecked
return (FieldType)getObjectVolatile.invokeExact(owner, offset);
}
catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
}