From a9742f941a708750f0d0525bb082c77aaefc76ae Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 11 Dec 2020 20:26:11 +0100 Subject: [PATCH] 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 --- ...oteExternalSystemCommunicationManager.java | 9 +- .../util/concurrency/AtomicFieldUpdater.java | 77 +++++++++++ .../util/concurrency/AtomicFieldUpdater.java | 123 ------------------ 3 files changed, 83 insertions(+), 126 deletions(-) create mode 100644 platform/util/concurrency/com/intellij/util/concurrency/AtomicFieldUpdater.java delete mode 100644 platform/util/src/com/intellij/util/concurrency/AtomicFieldUpdater.java diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/RemoteExternalSystemCommunicationManager.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/RemoteExternalSystemCommunicationManager.java index 8118618e77c1..74e2975dd7ad 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/RemoteExternalSystemCommunicationManager.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/RemoteExternalSystemCommunicationManager.java @@ -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 classPath = new ArrayList<>(PathManager.getUtilClassPath()); + Collection 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 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"); diff --git a/platform/util/concurrency/com/intellij/util/concurrency/AtomicFieldUpdater.java b/platform/util/concurrency/com/intellij/util/concurrency/AtomicFieldUpdater.java new file mode 100644 index 000000000000..ff6158d994a0 --- /dev/null +++ b/platform/util/concurrency/com/intellij/util/concurrency/AtomicFieldUpdater.java @@ -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 { + private final VarHandle myHandle; + + public static @NotNull AtomicFieldUpdater forFieldOfType(@NotNull Class ownerClass, @NotNull Class fieldType) { + return new AtomicFieldUpdater<>(ownerClass, fieldType); + } + + public static @NotNull AtomicFieldUpdater forLongFieldIn(@NotNull Class ownerClass) { + return new AtomicFieldUpdater<>(ownerClass, long.class); + } + + public static @NotNull AtomicFieldUpdater forIntFieldIn(@NotNull Class ownerClass) { + return new AtomicFieldUpdater<>(ownerClass, int.class); + } + + public static @NotNull AtomicFieldUpdater forField(@NotNull Field field) { + return new AtomicFieldUpdater<>(field); + } + + private AtomicFieldUpdater(@NotNull Class ownerClass, @NotNull Class 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); + } +} diff --git a/platform/util/src/com/intellij/util/concurrency/AtomicFieldUpdater.java b/platform/util/src/com/intellij/util/concurrency/AtomicFieldUpdater.java deleted file mode 100644 index 3b627559a4eb..000000000000 --- a/platform/util/src/com/intellij/util/concurrency/AtomicFieldUpdater.java +++ /dev/null @@ -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 { - 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 AtomicFieldUpdater forFieldOfType(@NotNull Class ownerClass, @NotNull Class fieldType) { - return new AtomicFieldUpdater<>(ownerClass, fieldType); - } - - public static @NotNull AtomicFieldUpdater forLongFieldIn(@NotNull Class ownerClass) { - return new AtomicFieldUpdater<>(ownerClass, long.class); - } - - public static @NotNull AtomicFieldUpdater forIntFieldIn(@NotNull Class ownerClass) { - return new AtomicFieldUpdater<>(ownerClass, int.class); - } - - public static @NotNull AtomicFieldUpdater forField(@NotNull Field field) { - return new AtomicFieldUpdater<>(field); - } - - private AtomicFieldUpdater(@NotNull Class ownerClass, @NotNull Class 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); - } - } -}