changeset 55030:3081932e7efd

7107615: scalability bloker in javax.crypto.JceSecurity Summary: Change to use ConcurrentHashMap instead of syncing on whole method Reviewed-by: xuelei, alanb, dfuchs
author valeriep
date Wed, 22 May 2019 21:38:16 +0000
parents 181986c54764
children a573f58bc798
files src/java.base/share/classes/javax/crypto/JceSecurity.java.template
diffstat 1 files changed, 64 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/javax/crypto/JceSecurity.java.template	Wed May 22 21:40:58 2019 +0200
+++ b/src/java.base/share/classes/javax/crypto/JceSecurity.java.template	Wed May 22 21:38:16 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -50,6 +50,7 @@
 package javax.crypto;
 
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 import java.io.*;
 import java.net.URL;
 import java.nio.file.*;
@@ -84,11 +85,11 @@
     private static CryptoPermissions defaultPolicy = null;
     private static CryptoPermissions exemptPolicy = null;
 
-    // Map<Provider,?> of the providers we already have verified
-    // value == PROVIDER_VERIFIED is successfully verified
-    // value is failure cause Exception in error case
-    private static final Map<Provider, Object> verificationResults =
-            new IdentityHashMap<>();
+    // Map of the providers we already have verified.
+    // If verified ok, value == PROVIDER_VERIFIED, otherwise
+    // the cause of verification failure is stored as value.
+    private static final Map<IdentityWrapper, Object>
+        verificationResults = new ConcurrentHashMap<>();
 
     // Map<Provider,?> of the providers currently being verified
     private static final Map<Provider, Object> verifyingProviders =
@@ -199,31 +200,39 @@
      * JCE trusted CA.
      * Return null if ok, failure Exception if verification failed.
      */
-    static synchronized Exception getVerificationResult(Provider p) {
-        Object o = verificationResults.get(p);
-        if (o == PROVIDER_VERIFIED) {
-            return null;
-        } else if (o != null) {
-            return (Exception)o;
+    static Exception getVerificationResult(Provider p) {
+        IdentityWrapper pKey = new IdentityWrapper(p);
+        Object o = verificationResults.get(pKey);
+        // no mapping found
+        if (o == null) {
+            synchronized (JceSecurity.class) {
+                // check cache again in case the result is now available
+                o = verificationResults.get(pKey);
+                if (o == null) {
+                    if (verifyingProviders.get(p) != null) {
+                        // recursion; return failure now
+                        return new NoSuchProviderException
+                                ("Recursion during verification");
+                    }
+                    try {
+                        verifyingProviders.put(p, Boolean.FALSE);
+                        URL providerURL = getCodeBase(p.getClass());
+                        verifyProvider(providerURL, p);
+                        o = PROVIDER_VERIFIED;
+                    } catch (Exception e) {
+                        o = e;
+                    } finally {
+                        verifyingProviders.remove(p);
+                    }
+                    verificationResults.put(pKey, o);
+                    if (debug != null) {
+                        debug.println("Provider " + p.getName() +
+                                " verification result: " + o);
+                    }
+                }
+            }
         }
-        if (verifyingProviders.get(p) != null) {
-            // this method is static synchronized, must be recursion
-            // return failure now but do not save the result
-            return new NoSuchProviderException("Recursion during verification");
-        }
-        try {
-            verifyingProviders.put(p, Boolean.FALSE);
-            URL providerURL = getCodeBase(p.getClass());
-            verifyProvider(providerURL, p);
-            // Verified ok, cache result
-            verificationResults.put(p, PROVIDER_VERIFIED);
-            return null;
-        } catch (Exception e) {
-            verificationResults.put(p, e);
-            return e;
-        } finally {
-            verifyingProviders.remove(p);
-        }
+        return (o == PROVIDER_VERIFIED? null : (Exception) o);
     }
 
     // return whether this provider is properly signed and can be used by JCE
@@ -391,4 +400,29 @@
     static boolean isRestricted() {
         return isRestricted;
     }
+
+    private static final class IdentityWrapper {
+
+        final Provider obj;
+
+        IdentityWrapper(Provider obj) {
+            this.obj = obj;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof IdentityWrapper)) {
+                return false;
+            }
+            return this.obj == ((IdentityWrapper)o).obj;
+        }
+
+        @Override
+        public int hashCode() {
+            return System.identityHashCode(obj);
+        }
+    }
 }