7107615: scalability bloker in javax.crypto.JceSecurity
Summary: Change to use ConcurrentHashMap instead of syncing on whole method
Reviewed-by: xuelei, alanb, dfuchs
--- 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;
- }
- 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");
+ 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);
+ }
+ }
+ }
}
- 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);
+ }
+ }
}