src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java
branchJDK-8229867-branch
changeset 57968 8595871a5446
parent 57956 e0b8b019d2f5
--- a/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java	Fri Aug 30 13:11:16 2019 +0100
+++ b/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java	Fri Aug 30 15:42:27 2019 +0100
@@ -36,6 +36,8 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import jdk.internal.misc.InnocuousThread;
 import sun.security.action.GetIntegerAction;
@@ -74,6 +76,7 @@
 
     static final int LIFETIME = 5000;
 
+    private final ReentrantLock cacheLock = new ReentrantLock();
     private Thread keepAliveTimer = null;
 
     /**
@@ -86,76 +89,84 @@
      * @param url  The URL contains info about the host and port
      * @param http The HttpClient to be cached
      */
-    public synchronized void put(final URL url, Object obj, HttpClient http) {
-        boolean startThread = (keepAliveTimer == null);
-        if (!startThread) {
-            if (!keepAliveTimer.isAlive()) {
-                startThread = true;
+    public void put(final URL url, Object obj, HttpClient http) {
+        cacheLock.lock();
+        try {
+            boolean startThread = (keepAliveTimer == null);
+            if (!startThread) {
+                if (!keepAliveTimer.isAlive()) {
+                    startThread = true;
+                }
             }
-        }
-        if (startThread) {
-            clear();
-            /* Unfortunately, we can't always believe the keep-alive timeout we got
-             * back from the server.  If I'm connected through a Netscape proxy
-             * to a server that sent me a keep-alive
-             * time of 15 sec, the proxy unilaterally terminates my connection
-             * The robustness to get around this is in HttpClient.parseHTTP()
-             */
-            final KeepAliveCache cache = this;
-            AccessController.doPrivileged(new PrivilegedAction<>() {
-                public Void run() {
-                    keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);
-                    keepAliveTimer.setDaemon(true);
-                    keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
-                    keepAliveTimer.start();
-                    return null;
-                }
-            });
-        }
+            if (startThread) {
+                clear();
+                /* Unfortunately, we can't always believe the keep-alive timeout we got
+                 * back from the server.  If I'm connected through a Netscape proxy
+                 * to a server that sent me a keep-alive
+                 * time of 15 sec, the proxy unilaterally terminates my connection
+                 * The robustness to get around this is in HttpClient.parseHTTP()
+                 */
+                final KeepAliveCache cache = this;
+                AccessController.doPrivileged(new PrivilegedAction<>() {
+                    public Void run() {
+                        keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);
+                        keepAliveTimer.setDaemon(true);
+                        keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
+                        keepAliveTimer.start();
+                        return null;
+                    }
+                });
+            }
 
-        KeepAliveKey key = new KeepAliveKey(url, obj);
-        ClientVector v = super.get(key);
+            KeepAliveKey key = new KeepAliveKey(url, obj);
+            ClientVector v = super.get(key);
 
-        if (v == null) {
-            int keepAliveTimeout = http.getKeepAliveTimeout();
-            v = new ClientVector(keepAliveTimeout > 0 ?
-                                 keepAliveTimeout * 1000 : LIFETIME);
-            v.put(http);
-            super.put(key, v);
-        } else {
-            v.put(http);
+            if (v == null) {
+                int keepAliveTimeout = http.getKeepAliveTimeout();
+                v = new ClientVector(keepAliveTimeout > 0 ?
+                        keepAliveTimeout * 1000 : LIFETIME);
+                v.put(http);
+                super.put(key, v);
+            } else {
+                v.put(http);
+            }
+        } finally {
+            cacheLock.unlock();
         }
     }
 
     /* remove an obsolete HttpClient from its VectorCache */
-    public synchronized void remove(HttpClient h, Object obj) {
-        KeepAliveKey key = new KeepAliveKey(h.url, obj);
-        ClientVector v = super.get(key);
-        if (v != null) {
-            v.remove(h);
-            if (v.isEmpty()) {
-                removeVector(key);
+    public void remove(HttpClient h, Object obj) {
+        cacheLock.lock();
+        try {
+            KeepAliveKey key = new KeepAliveKey(h.url, obj);
+            ClientVector v = super.get(key);
+            if (v != null) {
+                v.remove(h);
+                if (v.isEmpty()) {
+                    super.remove(key);
+                }
             }
+        } finally {
+            cacheLock.unlock();
         }
     }
 
-    /* called by a clientVector thread when all its connections have timed out
-     * and that vector of connections should be removed.
-     */
-    synchronized void removeVector(KeepAliveKey k) {
-        super.remove(k);
-    }
-
     /**
      * Check to see if this URL has a cached HttpClient
      */
-    public synchronized HttpClient get(URL url, Object obj) {
-        KeepAliveKey key = new KeepAliveKey(url, obj);
-        ClientVector v = super.get(key);
-        if (v == null) { // nothing in cache yet
-            return null;
+    public HttpClient get(URL url, Object obj) {
+        cacheLock.lock();
+        try {
+            KeepAliveKey key = new KeepAliveKey(url, obj);
+            ClientVector v = super.get(key);
+            if (v == null) { // nothing in cache yet
+                return null;
+            }
+            return v.get();
+        } finally {
+            cacheLock.unlock();
         }
-        return v.get();
     }
 
     /* Sleeps for an alloted timeout, then checks for timed out connections.
@@ -170,13 +181,16 @@
             } catch (InterruptedException e) {}
 
             // Remove all outdated HttpClients.
-            synchronized (this) {
+            cacheLock.lock();
+            try {
                 long currentTime = System.currentTimeMillis();
                 List<KeepAliveKey> keysToRemove = new ArrayList<>();
 
                 for (KeepAliveKey key : keySet()) {
                     ClientVector v = get(key);
-                    synchronized (v) {
+                    final Lock lock = v.lock;
+                    lock.lock();
+                    try {
                         KeepAliveEntry e = v.peek();
                         while (e != null) {
                             if ((currentTime - e.idleStartTime) > v.nap) {
@@ -191,12 +205,16 @@
                         if (v.isEmpty()) {
                             keysToRemove.add(key);
                         }
+                    } finally {
+                        lock.unlock();
                     }
                 }
 
                 for (KeepAliveKey key : keysToRemove) {
-                    removeVector(key);
+                    super.remove(key);
                 }
+            } finally {
+                cacheLock.unlock();
             }
         } while (!isEmpty());
     }
@@ -223,6 +241,7 @@
 class ClientVector extends ArrayDeque<KeepAliveEntry> {
     @java.io.Serial
     private static final long serialVersionUID = -8680532108106489459L;
+    final ReentrantLock lock = new ReentrantLock();
 
     // sleep time in milliseconds, before cache clear
     int nap;
@@ -231,42 +250,57 @@
         this.nap = nap;
     }
 
-    synchronized HttpClient get() {
-        if (isEmpty()) {
-            return null;
-        }
+    HttpClient get() {
+        lock.lock();
+        try {
+            if (isEmpty()) {
+                return null;
+            }
 
-        // Loop until we find a connection that has not timed out
-        HttpClient hc = null;
-        long currentTime = System.currentTimeMillis();
-        do {
-            KeepAliveEntry e = pop();
-            if ((currentTime - e.idleStartTime) > nap) {
-                e.hc.closeServer();
-            } else {
-                hc = e.hc;
-            }
-        } while ((hc == null) && (!isEmpty()));
-        return hc;
+            // Loop until we find a connection that has not timed out
+            HttpClient hc = null;
+            long currentTime = System.currentTimeMillis();
+            do {
+                KeepAliveEntry e = pop();
+                if ((currentTime - e.idleStartTime) > nap) {
+                    e.hc.closeServer();
+                } else {
+                    hc = e.hc;
+                }
+            } while ((hc == null) && (!isEmpty()));
+            return hc;
+        } finally {
+            lock.unlock();
+        }
     }
 
     /* return a still valid, unused HttpClient */
-    synchronized void put(HttpClient h) {
-        if (size() >= KeepAliveCache.getMaxConnections()) {
-            h.closeServer(); // otherwise the connection remains in limbo
-        } else {
-            push(new KeepAliveEntry(h, System.currentTimeMillis()));
+    void put(HttpClient h) {
+        lock.lock();
+        try {
+            if (size() >= KeepAliveCache.getMaxConnections()) {
+                h.closeServer(); // otherwise the connection remains in limbo
+            } else {
+                push(new KeepAliveEntry(h, System.currentTimeMillis()));
+            }
+        } finally {
+            lock.unlock();
         }
     }
 
     /* remove an HttpClient */
-    synchronized boolean remove(HttpClient h) {
-        for (KeepAliveEntry curr : this) {
-            if (curr.hc == h) {
-                return super.remove(curr);
+    boolean remove(HttpClient h) {
+        lock.lock();
+        try {
+            for (KeepAliveEntry curr : this) {
+                if (curr.hc == h) {
+                    return super.remove(curr);
+                }
             }
+            return false;
+        } finally {
+            lock.unlock();
         }
-        return false;
     }
 
     /*