Home | History | Annotate | Download | only in http
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /**
     18  * Hangs onto idle live connections for a little while
     19  */
     20 
     21 package android.net.http;
     22 
     23 import org.apache.http.HttpHost;
     24 
     25 import android.os.SystemClock;
     26 
     27 /**
     28  * {@hide}
     29  */
     30 class IdleCache {
     31 
     32     class Entry {
     33         HttpHost mHost;
     34         Connection mConnection;
     35         long mTimeout;
     36     };
     37 
     38     private final static int IDLE_CACHE_MAX = 8;
     39 
     40     /* Allow five consecutive empty queue checks before shutdown */
     41     private final static int EMPTY_CHECK_MAX = 5;
     42 
     43     /* six second timeout for connections */
     44     private final static int TIMEOUT = 6 * 1000;
     45     private final static int CHECK_INTERVAL = 2 * 1000;
     46     private Entry[] mEntries = new Entry[IDLE_CACHE_MAX];
     47 
     48     private int mCount = 0;
     49 
     50     private IdleReaper mThread = null;
     51 
     52     /* stats */
     53     private int mCached = 0;
     54     private int mReused = 0;
     55 
     56     IdleCache() {
     57         for (int i = 0; i < IDLE_CACHE_MAX; i++) {
     58             mEntries[i] = new Entry();
     59         }
     60     }
     61 
     62     /**
     63      * Caches connection, if there is room.
     64      * @return true if connection cached
     65      */
     66     synchronized boolean cacheConnection(
     67             HttpHost host, Connection connection) {
     68 
     69         boolean ret = false;
     70 
     71         if (HttpLog.LOGV) {
     72             HttpLog.v("IdleCache size " + mCount + " host "  + host);
     73         }
     74 
     75         if (mCount < IDLE_CACHE_MAX) {
     76             long time = SystemClock.uptimeMillis();
     77             for (int i = 0; i < IDLE_CACHE_MAX; i++) {
     78                 Entry entry = mEntries[i];
     79                 if (entry.mHost == null) {
     80                     entry.mHost = host;
     81                     entry.mConnection = connection;
     82                     entry.mTimeout = time + TIMEOUT;
     83                     mCount++;
     84                     if (HttpLog.LOGV) mCached++;
     85                     ret = true;
     86                     if (mThread == null) {
     87                         mThread = new IdleReaper();
     88                         mThread.start();
     89                     }
     90                     break;
     91                 }
     92             }
     93         }
     94         return ret;
     95     }
     96 
     97     synchronized Connection getConnection(HttpHost host) {
     98         Connection ret = null;
     99 
    100         if (mCount > 0) {
    101             for (int i = 0; i < IDLE_CACHE_MAX; i++) {
    102                 Entry entry = mEntries[i];
    103                 HttpHost eHost = entry.mHost;
    104                 if (eHost != null && eHost.equals(host)) {
    105                     ret = entry.mConnection;
    106                     entry.mHost = null;
    107                     entry.mConnection = null;
    108                     mCount--;
    109                     if (HttpLog.LOGV) mReused++;
    110                     break;
    111                 }
    112             }
    113         }
    114         return ret;
    115     }
    116 
    117     synchronized void clear() {
    118         for (int i = 0; mCount > 0 && i < IDLE_CACHE_MAX; i++) {
    119             Entry entry = mEntries[i];
    120             if (entry.mHost != null) {
    121                 entry.mHost = null;
    122                 entry.mConnection.closeConnection();
    123                 entry.mConnection = null;
    124                 mCount--;
    125             }
    126         }
    127     }
    128 
    129     private synchronized void clearIdle() {
    130         if (mCount > 0) {
    131             long time = SystemClock.uptimeMillis();
    132             for (int i = 0; i < IDLE_CACHE_MAX; i++) {
    133                 Entry entry = mEntries[i];
    134                 if (entry.mHost != null && time > entry.mTimeout) {
    135                     entry.mHost = null;
    136                     entry.mConnection.closeConnection();
    137                     entry.mConnection = null;
    138                     mCount--;
    139                 }
    140             }
    141         }
    142     }
    143 
    144     private class IdleReaper extends Thread {
    145 
    146         public void run() {
    147             int check = 0;
    148 
    149             setName("IdleReaper");
    150             android.os.Process.setThreadPriority(
    151                     android.os.Process.THREAD_PRIORITY_BACKGROUND);
    152             synchronized (IdleCache.this) {
    153                 while (check < EMPTY_CHECK_MAX) {
    154                     try {
    155                         IdleCache.this.wait(CHECK_INTERVAL);
    156                     } catch (InterruptedException ex) {
    157                     }
    158                     if (mCount == 0) {
    159                         check++;
    160                     } else {
    161                         check = 0;
    162                         clearIdle();
    163                     }
    164                 }
    165                 mThread = null;
    166             }
    167             if (HttpLog.LOGV) {
    168                 HttpLog.v("IdleCache IdleReaper shutdown: cached " + mCached +
    169                           " reused " + mReused);
    170                 mCached = 0;
    171                 mReused = 0;
    172             }
    173         }
    174     }
    175 }
    176