IJPL-191435 Unsafe::arrayBaseOffset called in Kotlin under JDK 24

- unify duplicate ConcurrentIntObjectHashMap implementations to work via VarHandles if available

GitOrigin-RevId: 084232c35927181a80917c12f6e0bca9b7380483
This commit is contained in:
Alexey Kudravtsev
2025-07-30 17:16:24 +02:00
committed by intellij-monorepo-bot
parent 5126eaa8bd
commit d2a42ea691
13 changed files with 638 additions and 3779 deletions

View File

@@ -61,10 +61,10 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
private static final Map<ProgressIndicator, Set<Thread>> threadsUnderIndicator = new HashMap<>(); // guarded by threadsUnderIndicator
// the active indicator for the thread id
private static final ConcurrentLongObjectMap<ProgressIndicator> currentIndicators =
Java11Shim.INSTANCE.createConcurrentLongObjectMap();
Java11Shim.Companion.createConcurrentLongObjectMap();
// top-level indicators for the thread id
private static final ConcurrentLongObjectMap<ProgressIndicator> threadTopLevelIndicators =
Java11Shim.INSTANCE.createConcurrentLongObjectMap();
Java11Shim.Companion.createConcurrentLongObjectMap();
// threads which are running under canceled indicator
private static final Set<Thread> threadsUnderCanceledIndicator = new HashSet<>(); // guarded by threadsUnderIndicator

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.ide.bootstrap
import com.intellij.concurrency.VarHandleWrapperImpl
@@ -67,4 +67,4 @@ class Java11ShimImpl : Java11Shim() {
stream.skip(stackFrameIndex.toLong()).map { it.declaringClass }.findFirst().orElse(null)
}
}
}
}

View File

@@ -527,13 +527,6 @@ public class ContainerUtilCollectionsTest extends Assert {
assertTrue(map.isEmpty());
}
@Test(timeout = TIMEOUT)
public void testOldConcurrentLongObjectHashMap() {
ConcurrentLongObjectMap<Object> map = Java11Shim.INSTANCE.createConcurrentLongObjectMap();
check(map);
}
@Test(timeout = TIMEOUT)
public void testConcurrentIntObjectHashMap() {
IntObjectMap<Object> map = ConcurrentCollectionFactory.createConcurrentIntObjectMap();
@@ -555,27 +548,6 @@ public class ContainerUtilCollectionsTest extends Assert {
assertEquals(0, map.size());
}
@Test
public void testOldConcurrentIntObjectHashMap() {
IntObjectMap<Object> map = ContainerUtil.createConcurrentIntObjectMap();
for (int i = 0; i < 1000; i++) {
Object prev = map.put(i, i);
assertNull(prev);
Object ret = map.get(i);
assertTrue(ret instanceof Integer);
assertEquals(i, ret);
if (i != 0) {
Object remove = map.remove(i - 1);
assertTrue(remove instanceof Integer);
assertEquals(i - 1, remove);
}
assertEquals(1, map.size());
}
map.clear();
assertEquals(0, map.size());
}
@Test(timeout = TIMEOUT)
public void testConcurrentWeakKeyWeakValueMapTossed() {
ConcurrentMap<Object, Object> map = CollectionFactory.createConcurrentWeakKeyWeakValueMap();
@@ -851,7 +823,7 @@ public class ContainerUtilCollectionsTest extends Assert {
checkEntrySetIterator(ContainerUtil.createIntKeyWeakValueMap());
checkEntrySetIterator(ConcurrentCollectionFactory.createConcurrentLongObjectMap());
checkEntrySetIterator(Java11Shim.INSTANCE.createConcurrentLongObjectMap());
checkEntrySetIterator(Java11Shim.Companion.createConcurrentLongObjectMap());
}
@Test

View File

@@ -74,24 +74,24 @@ public final class ConcurrentCollectionFactory {
@Contract(value = " -> new", pure = true)
public static @NotNull <V> ConcurrentLongObjectMap<@NotNull V> createConcurrentLongObjectMap() {
return Java11Shim.INSTANCE.createConcurrentLongObjectMap();
return Java11Shim.Companion.createConcurrentLongObjectMap();
}
@Contract(value = " -> new", pure = true)
public static @NotNull <V> ConcurrentIntObjectMap<@NotNull V> createConcurrentIntObjectMap() {
return new ConcurrentIntObjectHashMap<>();
return Java11Shim.Companion.createConcurrentIntObjectMap();
}
@Contract(value = "_,_,_ -> new", pure = true)
public static @NotNull <V> ConcurrentIntObjectMap<@NotNull V> createConcurrentIntObjectMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
return new ConcurrentIntObjectHashMap<>(initialCapacity, loadFactor, concurrencyLevel);
return Java11Shim.Companion.createConcurrentIntObjectMap(initialCapacity, loadFactor, concurrencyLevel);
}
@Contract(value = " -> new", pure = true)
public static @NotNull <V> ConcurrentIntObjectMap<@NotNull V> createConcurrentIntObjectSoftValueMap() {
return new ConcurrentIntKeySoftValueHashMap<>();
return Java11Shim.Companion.createConcurrentIntObjectSoftValueMap();
}
@Contract(value = " -> new", pure = true)
public static @NotNull <V> ConcurrentIntObjectMap<@NotNull V> createConcurrentIntObjectWeakValueMap() {
return new ConcurrentIntKeyWeakValueHashMap<>();
return Java11Shim.Companion.createConcurrentIntObjectWeakValueMap();
}
}

View File

@@ -1,11 +1,8 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.concurrency;
package com.intellij.util.containers;
import com.intellij.reference.SoftReference;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ReferenceQueueable;
import com.intellij.util.containers.SimpleEntry;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.ReferenceQueue;
@@ -162,7 +159,7 @@ abstract class ConcurrentIntKeyRefValueHashMap<V> implements ConcurrentIntObject
private @NotNull Iterator<Entry<V>> entriesIterator() {
final Iterator<Entry<IntReference<V>>> entryIterator = myMap.entrySet().iterator();
return new Iterator<>() {
return new Iterator<Entry<V>>() {
private Entry<V> nextVEntry;
private Entry<IntReference<V>> nextReferenceEntry;
private Entry<IntReference<V>> lastReturned;
@@ -224,7 +221,7 @@ abstract class ConcurrentIntKeyRefValueHashMap<V> implements ConcurrentIntObject
@Override
public @NotNull Enumeration<V> elements() {
final Enumeration<IntReference<V>> elementRefs = myMap.elements();
return new Enumeration<>() {
return new Enumeration<V>() {
private V findNextRef() {
while (elementRefs.hasMoreElements()) {
IntReference<V> result = elementRefs.nextElement();

View File

@@ -1,5 +1,5 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.concurrency;
package com.intellij.util.containers;
import com.intellij.openapi.util.Comparing;
import org.jetbrains.annotations.NotNull;
@@ -10,7 +10,7 @@ import java.lang.ref.SoftReference;
/**
* Concurrent key:int -> soft value:V map
* Null values are NOT allowed
* Use {@link ConcurrentCollectionFactory#createConcurrentIntObjectSoftValueMap()} to create this
* Use {@link com.intellij.concurrency.ConcurrentCollectionFactory#createConcurrentIntObjectSoftValueMap()} to create this
*/
final class ConcurrentIntKeySoftValueHashMap<V> extends ConcurrentIntKeyRefValueHashMap<V> {
private static final class MyRef<V> extends SoftReference<V> implements IntReference<V> {

View File

@@ -1,5 +1,5 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.concurrency;
package com.intellij.util.containers;
import com.intellij.openapi.util.Comparing;
import org.jetbrains.annotations.NotNull;
@@ -10,7 +10,7 @@ import java.lang.ref.WeakReference;
/**
* Concurrent key:int -> weak value:V map
* Null values are NOT allowed
* Use {@link ConcurrentCollectionFactory#createConcurrentIntObjectWeakValueMap()} to create
* Use {@link com.intellij.concurrency.ConcurrentCollectionFactory#createConcurrentIntObjectWeakValueMap()} to create
*/
final class ConcurrentIntKeyWeakValueHashMap<V> extends ConcurrentIntKeyRefValueHashMap<V> {
private static final class MyRef<V> extends WeakReference<V> implements IntReference<V> {

View File

@@ -2672,7 +2672,7 @@ public final class ContainerUtil {
@Deprecated
@Contract(value = " -> new", pure = true)
public static @NotNull <V> ConcurrentIntObjectMap<@NotNull V> createConcurrentIntObjectMap() {
return new ConcurrentIntObjectHashMap<>();
return Java11Shim.Companion.createConcurrentIntObjectMap();
}
@Contract(value = " -> new", pure = true)

View File

@@ -12,6 +12,11 @@ abstract class Java11Shim {
companion object {
@JvmField
var INSTANCE: Java11Shim = DefaultJava11Shim()
fun <V> createConcurrentLongObjectMap(): ConcurrentLongObjectMap<V> = ConcurrentLongObjectHashMap()
fun <V> createConcurrentIntObjectMap(): ConcurrentIntObjectMap<V> = ConcurrentIntObjectHashMap()
fun <V> createConcurrentIntObjectMap(initialCapacity:Int, loadFactor:Float, concurrencyLevel:Int): ConcurrentIntObjectMap<V> = ConcurrentIntObjectHashMap(initialCapacity, loadFactor, concurrencyLevel)
fun <V> createConcurrentIntObjectSoftValueMap(): ConcurrentIntObjectMap<V> = ConcurrentIntKeySoftValueHashMap()
fun <V> createConcurrentIntObjectWeakValueMap(): ConcurrentIntObjectMap<V> = ConcurrentIntKeyWeakValueHashMap()
}
/**
@@ -37,9 +42,5 @@ abstract class Java11Shim {
abstract fun <E> listOf(array: Array<E>, size: Int): List<E>
fun <V : Any> createConcurrentLongObjectMap(): ConcurrentLongObjectMap<V> {
return ConcurrentLongObjectHashMap()
}
abstract fun getCallerClass(stackFrameIndex: Int): Class<*>?
}

View File

@@ -17,7 +17,6 @@ public final class Unsafe {
private static final MethodHandle compareAndSwapObject;
private static final MethodHandle compareAndSwapInt;
private static final MethodHandle compareAndSwapLong;
private static final MethodHandle getAndAddInt;
private static final MethodHandle objectFieldOffset;
private static final MethodHandle arrayIndexScale;
private static final MethodHandle arrayBaseOffset;
@@ -30,7 +29,6 @@ public final class Unsafe {
compareAndSwapObject = find("compareAndSwapObject", boolean.class, Object.class, long.class, Object.class, Object.class);
compareAndSwapInt = find("compareAndSwapInt", boolean.class, Object.class, long.class, int.class, int.class);
compareAndSwapLong = find("compareAndSwapLong", boolean.class, Object.class, long.class, long.class, long.class);
getAndAddInt = find("getAndAddInt", int.class, Object.class, long.class, int.class);
objectFieldOffset = find("objectFieldOffset", long.class, Field.class);
arrayBaseOffset = find("arrayBaseOffset", int.class, Class.class);
arrayIndexScale = find("arrayIndexScale", int.class, Class.class);
@@ -49,7 +47,7 @@ public final class Unsafe {
.bindTo(unsafe);
}
public static boolean compareAndSwapInt(Object object, long offset, int expected, int value) {
static boolean compareAndSwapInt(Object object, long offset, int expected, int value) {
try {
return (boolean)compareAndSwapInt.invokeExact(object, offset, expected, value);
}
@@ -58,7 +56,7 @@ public final class Unsafe {
}
}
public static boolean compareAndSwapLong(@NotNull Object object, long offset, long expected, long value) {
static boolean compareAndSwapLong(@NotNull Object object, long offset, long expected, long value) {
try {
return (boolean)compareAndSwapLong.invokeExact(object, offset, expected, value);
}
@@ -66,15 +64,8 @@ public final class Unsafe {
throw new RuntimeException(throwable);
}
}
public static int getAndAddInt(Object object, long offset, int v) {
try {
return (int)getAndAddInt.invokeExact(object, offset, v);
}
catch (Throwable t) {
throw new RuntimeException(t);
}
}
public static Object getObjectVolatile(Object object, long offset) {
static Object getObjectVolatile(Object object, long offset) {
try {
return getObjectVolatile.invokeExact(object, offset);
}
@@ -83,9 +74,9 @@ public final class Unsafe {
}
}
public static boolean compareAndSwapObject(Object o, long offset,
Object expected,
Object x) {
static boolean compareAndSwapObject(Object o, long offset,
Object expected,
Object x) {
try {
return (boolean)compareAndSwapObject.invokeExact(o, offset, expected, x);
}
@@ -94,7 +85,7 @@ public final class Unsafe {
}
}
public static void putObjectVolatile(Object o, long offset, Object x) {
static void putObjectVolatile(Object o, long offset, Object x) {
try {
putObjectVolatile.invokeExact(o, offset, x);
}
@@ -102,7 +93,7 @@ public final class Unsafe {
throw new RuntimeException(throwable);
}
}
public static long objectFieldOffset(Field f) {
static long objectFieldOffset(Field f) {
try {
return (long)objectFieldOffset.invokeExact(f);
}
@@ -111,7 +102,7 @@ public final class Unsafe {
}
}
public static int arrayIndexScale(Class<?> arrayClass) {
static int arrayIndexScale(Class<?> arrayClass) {
try {
return (int)arrayIndexScale.invokeExact(arrayClass);
}

View File

@@ -74,7 +74,7 @@ internal open class ImmutableEntityStorageImpl(
// I suppose that we can use some kind of array of arrays to get a quicker access (just two accesses by-index)
// However, it's not implemented currently because I'm not sure about threading.
private val entityCache: ConcurrentLongObjectMap<WorkspaceEntity> = Java11Shim.INSTANCE.createConcurrentLongObjectMap()
private val entityCache: ConcurrentLongObjectMap<WorkspaceEntity> = Java11Shim.createConcurrentLongObjectMap()
override fun <T> cached(query: StorageQuery<T>): T {
return snapshotCache.cached(query, this, null).value