Avoid bottlenecks by using ConcurrentHashMap instead of a synchronized IdentityHashMap.
Repost from Rietveld issue 1582804
Thanks to Jason Terk for the original patch!
Review at http://gwt-code-reviews.appspot.com/1609803
Review by: unnurg@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10784 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java b/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java
index fff5875..3b81cc9 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java
@@ -38,9 +38,9 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.CRC32;
/**
@@ -63,58 +63,41 @@
/**
* A permanent cache of all computed CRCs on classes. This is safe to do
* because a Class is guaranteed not to change within the lifetime of a
- * ClassLoader (and thus, this Map). Access must be synchronized.
- *
- * NOTE: after synchronizing on this field, it's possible to additionally
- * synchronize on {@link #classCustomSerializerCache},
- * {@link #classSerializableFieldsCache} or
- * {@link #classServerCustomSerializerCache}, so be aware deadlock potential
- * when changing this code.
+ * ClassLoader (and thus, this Map).
*/
private static final Map<Class<?>, String> classCRC32Cache =
- new IdentityHashMap<Class<?>, String>();
+ new ConcurrentHashMap<Class<?>, String>();
/**
* A permanent cache of all serializable fields on classes. This is safe to do
* because a Class is guaranteed not to change within the lifetime of a
- * ClassLoader (and thus, this Map). Access must be synchronized.
- *
- * NOTE: to prevent deadlock, you may NOT synchronize {@link #classCRC32Cache}
- * after synchronizing on this field.
+ * ClassLoader (and thus, this Map).
*/
private static final Map<Class<?>, Field[]> classSerializableFieldsCache =
- new IdentityHashMap<Class<?>, Field[]>();
+ new ConcurrentHashMap<Class<?>, Field[]>();
/**
* A permanent cache of all which classes onto custom field serializers. This
* is safe to do because a Class is guaranteed not to change within the
- * lifetime of a ClassLoader (and thus, this Map). Access must be
- * synchronized.
- *
- * NOTE: to prevent deadlock, you may NOT synchronize {@link #classCRC32Cache}
- * after synchronizing on this field.
+ * lifetime of a ClassLoader (and thus, this Map).
*/
private static final Map<Class<?>, Class<?>> classCustomSerializerCache =
- new IdentityHashMap<Class<?>, Class<?>>();
+ new ConcurrentHashMap<Class<?>, Class<?>>();
/**
* A permanent cache of all which classes onto server-side custom field
* serializers. This is safe to do because a Class is guaranteed not to change
- * within the lifetime of a ClassLoader (and thus, this Map). Access must be
- * synchronized.
- *
- * NOTE: to prevent deadlock, you may NOT synchronize {@link #classCRC32Cache}
- * after synchronizing on this field.
+ * within the lifetime of a ClassLoader (and thus, this Map).
*/
private static final Map<Class<?>, Class<?>> classServerCustomSerializerCache =
- new IdentityHashMap<Class<?>, Class<?>>();
+ new ConcurrentHashMap<Class<?>, Class<?>>();
/**
* Map of {@link Class} objects to singleton instances of that
* {@link CustomFieldSerializer}.
*/
private static final Map<Class<?>, CustomFieldSerializer<?>> CLASS_TO_SERIALIZER_INSTANCE =
- new IdentityHashMap<Class<?>, CustomFieldSerializer<?>>();
+ new ConcurrentHashMap<Class<?>, CustomFieldSerializer<?>>();
private static final String JRE_SERVER_SERIALIZER_PACKAGE = "com.google.gwt.user.server.rpc.core";
private static final String JRE_SERIALIZER_PACKAGE = "com.google.gwt.user.client.rpc.core";
@@ -194,23 +177,21 @@
*/
public static Field[] applyFieldSerializationPolicy(Class<?> clazz) {
Field[] serializableFields;
- synchronized (classSerializableFieldsCache) {
- serializableFields = classSerializableFieldsCache.get(clazz);
- if (serializableFields == null) {
- ArrayList<Field> fieldList = new ArrayList<Field>();
- Field[] fields = clazz.getDeclaredFields();
- for (Field field : fields) {
- if (fieldQualifiesForSerialization(field)) {
- fieldList.add(field);
- }
+ serializableFields = classSerializableFieldsCache.get(clazz);
+ if (serializableFields == null) {
+ ArrayList<Field> fieldList = new ArrayList<Field>();
+ Field[] fields = clazz.getDeclaredFields();
+ for (Field field : fields) {
+ if (fieldQualifiesForSerialization(field)) {
+ fieldList.add(field);
}
- serializableFields = fieldList.toArray(new Field[fieldList.size()]);
-
- // sort the fields by name
- Arrays.sort(serializableFields, 0, serializableFields.length, FIELD_COMPARATOR);
-
- classSerializableFieldsCache.put(clazz, serializableFields);
}
+ serializableFields = fieldList.toArray(new Field[fieldList.size()]);
+
+ // sort the fields by name
+ Arrays.sort(serializableFields, 0, serializableFields.length, FIELD_COMPARATOR);
+
+ classSerializableFieldsCache.put(clazz, serializableFields);
}
return serializableFields;
}
@@ -347,18 +328,16 @@
public static String getSerializationSignature(Class<?> instanceType,
SerializationPolicy policy) {
String result;
- synchronized (classCRC32Cache) {
- result = classCRC32Cache.get(instanceType);
- if (result == null) {
- CRC32 crc = new CRC32();
- try {
- generateSerializationSignature(instanceType, crc, policy);
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("Could not compute the serialization signature", e);
- }
- result = Long.toString(crc.getValue());
- classCRC32Cache.put(instanceType, result);
+ result = classCRC32Cache.get(instanceType);
+ if (result == null) {
+ CRC32 crc = new CRC32();
+ try {
+ generateSerializationSignature(instanceType, crc, policy);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("Could not compute the serialization signature", e);
}
+ result = Long.toString(crc.getValue());
+ classCRC32Cache.put(instanceType, result);
}
return result;
}
@@ -384,21 +363,19 @@
}
Class<?> result;
- synchronized (classCustomSerializerCache) {
- result = classCustomSerializerCache.get(instanceType);
+ result = classCustomSerializerCache.get(instanceType);
+ if (result == null) {
+ result = computeHasCustomFieldSerializer(instanceType, false);
if (result == null) {
- result = computeHasCustomFieldSerializer(instanceType, false);
- if (result == null) {
- /*
- * Use (result == instanceType) as a sentinel value when the class has
- * no custom field serializer. We avoid using null as the sentinel
- * value, because that would necessitate an additional containsKey()
- * check in the most common case, inside the synchronized block.
- */
- result = instanceType;
- }
- classCustomSerializerCache.put(instanceType, result);
+ /*
+ * Use (result == instanceType) as a sentinel value when the class has
+ * no custom field serializer. We avoid using null as the sentinel
+ * value, because that would necessitate an additional containsKey()
+ * check in the most common case.
+ */
+ result = instanceType;
}
+ classCustomSerializerCache.put(instanceType, result);
}
return (result == instanceType) ? null : result;
}
@@ -417,21 +394,19 @@
}
Class<?> result;
- synchronized (classServerCustomSerializerCache) {
- result = classServerCustomSerializerCache.get(instanceType);
+ result = classServerCustomSerializerCache.get(instanceType);
+ if (result == null) {
+ result = computeHasCustomFieldSerializer(instanceType, true);
if (result == null) {
- result = computeHasCustomFieldSerializer(instanceType, true);
- if (result == null) {
- /*
- * Use (result == instanceType) as a sentinel value when the class has
- * no custom field serializer. We avoid using null as the sentinel
- * value, because that would necessitate an additional containsKey()
- * check in the most common case, inside the synchronized block.
- */
- result = instanceType;
- }
- classServerCustomSerializerCache.put(instanceType, result);
+ /*
+ * Use (result == instanceType) as a sentinel value when the class has
+ * no custom field serializer. We avoid using null as the sentinel
+ * value, because that would necessitate an additional containsKey()
+ * check in the most common case.
+ */
+ result = instanceType;
}
+ classServerCustomSerializerCache.put(instanceType, result);
}
return (result == instanceType) ? null : result;
}
@@ -500,13 +475,6 @@
*/
static CustomFieldSerializer<?> loadCustomFieldSerializer(final Class<?> customSerializerClass)
throws SerializationException {
- /**
- * Note that neither reading or writing to the CLASS_TO_SERIALIZER_INSTANCE
- * is synchronized for performance reasons. This could cause get misses, put
- * misses and the same CustomFieldSerializer to be instantiated more than
- * once, but none of these are critical operations as
- * CLASS_TO_SERIALIZER_INSTANCE is only a performance improving cache.
- */
CustomFieldSerializer<?> customFieldSerializer =
CLASS_TO_SERIALIZER_INSTANCE.get(customSerializerClass);
if (customFieldSerializer == null) {