src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java
branchJDK-8229867-branch
changeset 57968 8595871a5446
parent 57956 e0b8b019d2f5
equal deleted inserted replaced
57966:e89c7aaf2906 57968:8595871a5446
    34 import java.security.PrivilegedAction;
    34 import java.security.PrivilegedAction;
    35 import java.util.ArrayDeque;
    35 import java.util.ArrayDeque;
    36 import java.util.ArrayList;
    36 import java.util.ArrayList;
    37 import java.util.HashMap;
    37 import java.util.HashMap;
    38 import java.util.List;
    38 import java.util.List;
       
    39 import java.util.concurrent.locks.Lock;
       
    40 import java.util.concurrent.locks.ReentrantLock;
    39 
    41 
    40 import jdk.internal.misc.InnocuousThread;
    42 import jdk.internal.misc.InnocuousThread;
    41 import sun.security.action.GetIntegerAction;
    43 import sun.security.action.GetIntegerAction;
    42 
    44 
    43 /**
    45 /**
    72         return result;
    74         return result;
    73     }
    75     }
    74 
    76 
    75     static final int LIFETIME = 5000;
    77     static final int LIFETIME = 5000;
    76 
    78 
       
    79     private final ReentrantLock cacheLock = new ReentrantLock();
    77     private Thread keepAliveTimer = null;
    80     private Thread keepAliveTimer = null;
    78 
    81 
    79     /**
    82     /**
    80      * Constructor
    83      * Constructor
    81      */
    84      */
    84     /**
    87     /**
    85      * Register this URL and HttpClient (that supports keep-alive) with the cache
    88      * Register this URL and HttpClient (that supports keep-alive) with the cache
    86      * @param url  The URL contains info about the host and port
    89      * @param url  The URL contains info about the host and port
    87      * @param http The HttpClient to be cached
    90      * @param http The HttpClient to be cached
    88      */
    91      */
    89     public synchronized void put(final URL url, Object obj, HttpClient http) {
    92     public void put(final URL url, Object obj, HttpClient http) {
    90         boolean startThread = (keepAliveTimer == null);
    93         cacheLock.lock();
    91         if (!startThread) {
    94         try {
    92             if (!keepAliveTimer.isAlive()) {
    95             boolean startThread = (keepAliveTimer == null);
    93                 startThread = true;
    96             if (!startThread) {
    94             }
    97                 if (!keepAliveTimer.isAlive()) {
    95         }
    98                     startThread = true;
    96         if (startThread) {
    99                 }
    97             clear();
   100             }
    98             /* Unfortunately, we can't always believe the keep-alive timeout we got
   101             if (startThread) {
    99              * back from the server.  If I'm connected through a Netscape proxy
   102                 clear();
   100              * to a server that sent me a keep-alive
   103                 /* Unfortunately, we can't always believe the keep-alive timeout we got
   101              * time of 15 sec, the proxy unilaterally terminates my connection
   104                  * back from the server.  If I'm connected through a Netscape proxy
   102              * The robustness to get around this is in HttpClient.parseHTTP()
   105                  * to a server that sent me a keep-alive
   103              */
   106                  * time of 15 sec, the proxy unilaterally terminates my connection
   104             final KeepAliveCache cache = this;
   107                  * The robustness to get around this is in HttpClient.parseHTTP()
   105             AccessController.doPrivileged(new PrivilegedAction<>() {
   108                  */
   106                 public Void run() {
   109                 final KeepAliveCache cache = this;
   107                     keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);
   110                 AccessController.doPrivileged(new PrivilegedAction<>() {
   108                     keepAliveTimer.setDaemon(true);
   111                     public Void run() {
   109                     keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
   112                         keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);
   110                     keepAliveTimer.start();
   113                         keepAliveTimer.setDaemon(true);
   111                     return null;
   114                         keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
   112                 }
   115                         keepAliveTimer.start();
   113             });
   116                         return null;
   114         }
   117                     }
   115 
   118                 });
   116         KeepAliveKey key = new KeepAliveKey(url, obj);
   119             }
   117         ClientVector v = super.get(key);
   120 
   118 
   121             KeepAliveKey key = new KeepAliveKey(url, obj);
   119         if (v == null) {
   122             ClientVector v = super.get(key);
   120             int keepAliveTimeout = http.getKeepAliveTimeout();
   123 
   121             v = new ClientVector(keepAliveTimeout > 0 ?
   124             if (v == null) {
   122                                  keepAliveTimeout * 1000 : LIFETIME);
   125                 int keepAliveTimeout = http.getKeepAliveTimeout();
   123             v.put(http);
   126                 v = new ClientVector(keepAliveTimeout > 0 ?
   124             super.put(key, v);
   127                         keepAliveTimeout * 1000 : LIFETIME);
   125         } else {
   128                 v.put(http);
   126             v.put(http);
   129                 super.put(key, v);
       
   130             } else {
       
   131                 v.put(http);
       
   132             }
       
   133         } finally {
       
   134             cacheLock.unlock();
   127         }
   135         }
   128     }
   136     }
   129 
   137 
   130     /* remove an obsolete HttpClient from its VectorCache */
   138     /* remove an obsolete HttpClient from its VectorCache */
   131     public synchronized void remove(HttpClient h, Object obj) {
   139     public void remove(HttpClient h, Object obj) {
   132         KeepAliveKey key = new KeepAliveKey(h.url, obj);
   140         cacheLock.lock();
   133         ClientVector v = super.get(key);
   141         try {
   134         if (v != null) {
   142             KeepAliveKey key = new KeepAliveKey(h.url, obj);
   135             v.remove(h);
   143             ClientVector v = super.get(key);
   136             if (v.isEmpty()) {
   144             if (v != null) {
   137                 removeVector(key);
   145                 v.remove(h);
   138             }
   146                 if (v.isEmpty()) {
   139         }
   147                     super.remove(key);
   140     }
   148                 }
   141 
   149             }
   142     /* called by a clientVector thread when all its connections have timed out
   150         } finally {
   143      * and that vector of connections should be removed.
   151             cacheLock.unlock();
   144      */
   152         }
   145     synchronized void removeVector(KeepAliveKey k) {
       
   146         super.remove(k);
       
   147     }
   153     }
   148 
   154 
   149     /**
   155     /**
   150      * Check to see if this URL has a cached HttpClient
   156      * Check to see if this URL has a cached HttpClient
   151      */
   157      */
   152     public synchronized HttpClient get(URL url, Object obj) {
   158     public HttpClient get(URL url, Object obj) {
   153         KeepAliveKey key = new KeepAliveKey(url, obj);
   159         cacheLock.lock();
   154         ClientVector v = super.get(key);
   160         try {
   155         if (v == null) { // nothing in cache yet
   161             KeepAliveKey key = new KeepAliveKey(url, obj);
   156             return null;
   162             ClientVector v = super.get(key);
   157         }
   163             if (v == null) { // nothing in cache yet
   158         return v.get();
   164                 return null;
       
   165             }
       
   166             return v.get();
       
   167         } finally {
       
   168             cacheLock.unlock();
       
   169         }
   159     }
   170     }
   160 
   171 
   161     /* Sleeps for an alloted timeout, then checks for timed out connections.
   172     /* Sleeps for an alloted timeout, then checks for timed out connections.
   162      * Errs on the side of caution (leave connections idle for a relatively
   173      * Errs on the side of caution (leave connections idle for a relatively
   163      * short time).
   174      * short time).
   168             try {
   179             try {
   169                 Thread.sleep(LIFETIME);
   180                 Thread.sleep(LIFETIME);
   170             } catch (InterruptedException e) {}
   181             } catch (InterruptedException e) {}
   171 
   182 
   172             // Remove all outdated HttpClients.
   183             // Remove all outdated HttpClients.
   173             synchronized (this) {
   184             cacheLock.lock();
       
   185             try {
   174                 long currentTime = System.currentTimeMillis();
   186                 long currentTime = System.currentTimeMillis();
   175                 List<KeepAliveKey> keysToRemove = new ArrayList<>();
   187                 List<KeepAliveKey> keysToRemove = new ArrayList<>();
   176 
   188 
   177                 for (KeepAliveKey key : keySet()) {
   189                 for (KeepAliveKey key : keySet()) {
   178                     ClientVector v = get(key);
   190                     ClientVector v = get(key);
   179                     synchronized (v) {
   191                     final Lock lock = v.lock;
       
   192                     lock.lock();
       
   193                     try {
   180                         KeepAliveEntry e = v.peek();
   194                         KeepAliveEntry e = v.peek();
   181                         while (e != null) {
   195                         while (e != null) {
   182                             if ((currentTime - e.idleStartTime) > v.nap) {
   196                             if ((currentTime - e.idleStartTime) > v.nap) {
   183                                 v.poll();
   197                                 v.poll();
   184                                 e.hc.closeServer();
   198                                 e.hc.closeServer();
   189                         }
   203                         }
   190 
   204 
   191                         if (v.isEmpty()) {
   205                         if (v.isEmpty()) {
   192                             keysToRemove.add(key);
   206                             keysToRemove.add(key);
   193                         }
   207                         }
       
   208                     } finally {
       
   209                         lock.unlock();
   194                     }
   210                     }
   195                 }
   211                 }
   196 
   212 
   197                 for (KeepAliveKey key : keysToRemove) {
   213                 for (KeepAliveKey key : keysToRemove) {
   198                     removeVector(key);
   214                     super.remove(key);
   199                 }
   215                 }
       
   216             } finally {
       
   217                 cacheLock.unlock();
   200             }
   218             }
   201         } while (!isEmpty());
   219         } while (!isEmpty());
   202     }
   220     }
   203 
   221 
   204     /*
   222     /*
   221  * to time them out.  If > maxConns are in use, block.
   239  * to time them out.  If > maxConns are in use, block.
   222  */
   240  */
   223 class ClientVector extends ArrayDeque<KeepAliveEntry> {
   241 class ClientVector extends ArrayDeque<KeepAliveEntry> {
   224     @java.io.Serial
   242     @java.io.Serial
   225     private static final long serialVersionUID = -8680532108106489459L;
   243     private static final long serialVersionUID = -8680532108106489459L;
       
   244     final ReentrantLock lock = new ReentrantLock();
   226 
   245 
   227     // sleep time in milliseconds, before cache clear
   246     // sleep time in milliseconds, before cache clear
   228     int nap;
   247     int nap;
   229 
   248 
   230     ClientVector(int nap) {
   249     ClientVector(int nap) {
   231         this.nap = nap;
   250         this.nap = nap;
   232     }
   251     }
   233 
   252 
   234     synchronized HttpClient get() {
   253     HttpClient get() {
   235         if (isEmpty()) {
   254         lock.lock();
   236             return null;
   255         try {
   237         }
   256             if (isEmpty()) {
   238 
   257                 return null;
   239         // Loop until we find a connection that has not timed out
   258             }
   240         HttpClient hc = null;
   259 
   241         long currentTime = System.currentTimeMillis();
   260             // Loop until we find a connection that has not timed out
   242         do {
   261             HttpClient hc = null;
   243             KeepAliveEntry e = pop();
   262             long currentTime = System.currentTimeMillis();
   244             if ((currentTime - e.idleStartTime) > nap) {
   263             do {
   245                 e.hc.closeServer();
   264                 KeepAliveEntry e = pop();
       
   265                 if ((currentTime - e.idleStartTime) > nap) {
       
   266                     e.hc.closeServer();
       
   267                 } else {
       
   268                     hc = e.hc;
       
   269                 }
       
   270             } while ((hc == null) && (!isEmpty()));
       
   271             return hc;
       
   272         } finally {
       
   273             lock.unlock();
       
   274         }
       
   275     }
       
   276 
       
   277     /* return a still valid, unused HttpClient */
       
   278     void put(HttpClient h) {
       
   279         lock.lock();
       
   280         try {
       
   281             if (size() >= KeepAliveCache.getMaxConnections()) {
       
   282                 h.closeServer(); // otherwise the connection remains in limbo
   246             } else {
   283             } else {
   247                 hc = e.hc;
   284                 push(new KeepAliveEntry(h, System.currentTimeMillis()));
   248             }
   285             }
   249         } while ((hc == null) && (!isEmpty()));
   286         } finally {
   250         return hc;
   287             lock.unlock();
   251     }
       
   252 
       
   253     /* return a still valid, unused HttpClient */
       
   254     synchronized void put(HttpClient h) {
       
   255         if (size() >= KeepAliveCache.getMaxConnections()) {
       
   256             h.closeServer(); // otherwise the connection remains in limbo
       
   257         } else {
       
   258             push(new KeepAliveEntry(h, System.currentTimeMillis()));
       
   259         }
   288         }
   260     }
   289     }
   261 
   290 
   262     /* remove an HttpClient */
   291     /* remove an HttpClient */
   263     synchronized boolean remove(HttpClient h) {
   292     boolean remove(HttpClient h) {
   264         for (KeepAliveEntry curr : this) {
   293         lock.lock();
   265             if (curr.hc == h) {
   294         try {
   266                 return super.remove(curr);
   295             for (KeepAliveEntry curr : this) {
   267             }
   296                 if (curr.hc == h) {
   268         }
   297                     return super.remove(curr);
   269         return false;
   298                 }
       
   299             }
       
   300             return false;
       
   301         } finally {
       
   302             lock.unlock();
       
   303         }
   270     }
   304     }
   271 
   305 
   272     /*
   306     /*
   273      * Do not serialize this class!
   307      * Do not serialize this class!
   274      */
   308      */