--- 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;
}
/*