Skip to content

Commit

Permalink
Prevent thread from being blocked by JDK-8 ConcurrentHashMap#computeI…
Browse files Browse the repository at this point in the history
…fAbsent()

This is a workaround for JDK-8161372

mybatis#1929
https://groups.google.com/g/mybatis-user/c/iBTN98c0dP4/m/H6IJS-LzAQAJ

We should drop this workaround once we drop Java 8 support or the fix is backported.
  • Loading branch information
harawata committed Apr 9, 2021
1 parent f6caf12 commit 2fc8ec0
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 24 deletions.
13 changes: 3 additions & 10 deletions src/main/java/org/apache/ibatis/binding/MapperProxy.java
@@ -1,5 +1,5 @@
/**
* Copyright 2009-2020 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,6 +28,7 @@

import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.util.MapUtil;

/**
* @author Clinton Begin
Expand Down Expand Up @@ -91,15 +92,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl

private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
// A workaround for https://bugs.openjdk.java.net/browse/JDK-8161372
// It should be removed once the fix is backported to Java 8 or
// MyBatis drops Java 8 support. See gh-1929
MapperMethodInvoker invoker = methodCache.get(method);
if (invoker != null) {
return invoker;
}

return methodCache.computeIfAbsent(method, m -> {
return MapUtil.computeIfAbsent(methodCache, method, m -> {
if (m.isDefault()) {
try {
if (privateLookupInMethod == null) {
Expand Down
@@ -1,5 +1,5 @@
/**
* Copyright 2009-2019 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,7 @@
import java.util.Map;

import org.apache.ibatis.cache.decorators.TransactionalCache;
import org.apache.ibatis.util.MapUtil;

/**
* @author Clinton Begin
Expand Down Expand Up @@ -52,7 +53,7 @@ public void rollback() {
}

private TransactionalCache getTransactionalCache(Cache cache) {
return transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);
return MapUtil.computeIfAbsent(transactionalCaches, cache, TransactionalCache::new);
}

}
@@ -1,5 +1,5 @@
/**
* Copyright 2009-2020 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -42,6 +42,7 @@
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.ibatis.util.MapUtil;

/**
* @author Clinton Begin
Expand Down Expand Up @@ -156,7 +157,7 @@ private void assignKeysToParamMap(Configuration configuration, ResultSet rs, Res
for (int i = 0; i < keyProperties.length; i++) {
Entry<String, KeyAssigner> entry = getAssignerForParamMap(configuration, rsmd, i + 1, paramMap, keyProperties[i],
keyProperties, true);
Entry<Iterator<?>, List<KeyAssigner>> iteratorPair = assignerMap.computeIfAbsent(entry.getKey(),
Entry<Iterator<?>, List<KeyAssigner>> iteratorPair = MapUtil.computeIfAbsent(assignerMap, entry.getKey(),
k -> entry(collectionize(paramMap.get(k)).iterator(), new ArrayList<>()));
iteratorPair.getValue().add(entry.getValue());
}
Expand Down
@@ -1,5 +1,5 @@
/**
* Copyright 2009-2020 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -61,6 +61,7 @@
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.ibatis.util.MapUtil;

/**
* @author Clinton Begin
Expand Down Expand Up @@ -589,7 +590,7 @@ private void addPendingChildRelation(ResultSet rs, MetaObject metaResultObject,
PendingRelation deferLoad = new PendingRelation();
deferLoad.metaObject = metaResultObject;
deferLoad.propertyMapping = parentMapping;
List<PendingRelation> relations = pendingRelations.computeIfAbsent(cacheKey, k -> new ArrayList<>());
List<PendingRelation> relations = MapUtil.computeIfAbsent(pendingRelations, cacheKey, k -> new ArrayList<>());
// issue #255
relations.add(deferLoad);
ResultMapping previous = nextResultMaps.get(parentMapping.getResultSet());
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/org/apache/ibatis/plugin/Plugin.java
@@ -1,5 +1,5 @@
/**
* Copyright 2009-2020 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -24,6 +24,7 @@
import java.util.Set;

import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.util.MapUtil;

/**
* @author Clinton Begin
Expand Down Expand Up @@ -75,7 +76,7 @@ private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor intercepto
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
Set<Method> methods = MapUtil.computeIfAbsent(signatureMap, sig.type(), k -> new HashSet<>());
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
Expand Down
@@ -1,5 +1,5 @@
/**
* Copyright 2009-2019 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,6 +18,8 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.ibatis.util.MapUtil;

public class DefaultReflectorFactory implements ReflectorFactory {
private boolean classCacheEnabled = true;
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();
Expand All @@ -39,7 +41,7 @@ public void setClassCacheEnabled(boolean classCacheEnabled) {
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
return reflectorMap.computeIfAbsent(type, Reflector::new);
return MapUtil.computeIfAbsent(reflectorMap, type, Reflector::new);
} else {
return new Reflector(type);
}
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/org/apache/ibatis/reflection/Reflector.java
@@ -1,5 +1,5 @@
/**
* Copyright 2009-2020 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -40,6 +40,7 @@
import org.apache.ibatis.reflection.invoker.MethodInvoker;
import org.apache.ibatis.reflection.invoker.SetFieldInvoker;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.apache.ibatis.util.MapUtil;

/**
* This class represents a cached set of class definition information that
Expand Down Expand Up @@ -143,7 +144,7 @@ private void addSetMethods(Class<?> clazz) {

private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
if (isValidPropertyName(name)) {
List<Method> list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>());
List<Method> list = MapUtil.computeIfAbsent(conflictingMethods, name, k -> new ArrayList<>());
list.add(method);
}
}
Expand Down
@@ -1,5 +1,5 @@
/**
* Copyright 2009-2019 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,6 +18,8 @@
import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.util.MapUtil;

/**
* @author Frank D. Martinez [mnesarco]
*/
Expand All @@ -31,7 +33,7 @@ public void register(Class<? extends LanguageDriver> cls) {
if (cls == null) {
throw new IllegalArgumentException("null is not a valid Language Driver");
}
LANGUAGE_DRIVER_MAP.computeIfAbsent(cls, k -> {
MapUtil.computeIfAbsent(LANGUAGE_DRIVER_MAP, cls, k -> {
try {
return k.getDeclaredConstructor().newInstance();
} catch (Exception ex) {
Expand Down
40 changes: 40 additions & 0 deletions src/main/java/org/apache/ibatis/util/MapUtil.java
@@ -0,0 +1,40 @@
/**
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.ibatis.util;

import java.util.Map;
import java.util.function.Function;

public class MapUtil {
/**
* A temporary workaround for Java 8 specific performance issue JDK-8161372 .<br>
* This class should be removed once we drop Java 8 support.
*
* @see <a href="https://bugs.openjdk.java.net/browse/JDK-8161372">https://bugs.openjdk.java.net/browse/JDK-8161372</a>
*/
public static <K, V> V computeIfAbsent(Map<K, V> map, K key, Function<K, V> mappingFunction) {
V value = map.get(key);
if (value != null) {
return value;
}
return map.computeIfAbsent(key, mappingFunction::apply);
}

private MapUtil() {
super();
}
}

0 comments on commit 2fc8ec0

Please sign in to comment.