Home | History | Annotate | Download | only in sqlite
      1 /*
      2  * Copyright (C) 2011 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 package android.database.sqlite;
     18 
     19 import android.database.sqlite.SQLiteDebug.DbStats;
     20 import android.os.CancellationSignal;
     21 import android.os.Handler;
     22 import android.os.Looper;
     23 import android.os.Message;
     24 import android.os.OperationCanceledException;
     25 import android.os.SystemClock;
     26 import android.util.Log;
     27 import android.util.PrefixPrinter;
     28 import android.util.Printer;
     29 
     30 import com.android.internal.annotations.GuardedBy;
     31 import com.android.internal.annotations.VisibleForTesting;
     32 
     33 import dalvik.system.CloseGuard;
     34 
     35 import java.io.Closeable;
     36 import java.util.ArrayList;
     37 import java.util.Map;
     38 import java.util.WeakHashMap;
     39 import java.util.concurrent.atomic.AtomicBoolean;
     40 import java.util.concurrent.locks.LockSupport;
     41 
     42 /**
     43  * Maintains a pool of active SQLite database connections.
     44  * <p>
     45  * At any given time, a connection is either owned by the pool, or it has been
     46  * acquired by a {@link SQLiteSession}.  When the {@link SQLiteSession} is
     47  * finished with the connection it is using, it must return the connection
     48  * back to the pool.
     49  * </p><p>
     50  * The pool holds strong references to the connections it owns.  However,
     51  * it only holds <em>weak references</em> to the connections that sessions
     52  * have acquired from it.  Using weak references in the latter case ensures
     53  * that the connection pool can detect when connections have been improperly
     54  * abandoned so that it can create new connections to replace them if needed.
     55  * </p><p>
     56  * The connection pool is thread-safe (but the connections themselves are not).
     57  * </p>
     58  *
     59  * <h2>Exception safety</h2>
     60  * <p>
     61  * This code attempts to maintain the invariant that opened connections are
     62  * always owned.  Unfortunately that means it needs to handle exceptions
     63  * all over to ensure that broken connections get cleaned up.  Most
     64  * operations invokving SQLite can throw {@link SQLiteException} or other
     65  * runtime exceptions.  This is a bit of a pain to deal with because the compiler
     66  * cannot help us catch missing exception handling code.
     67  * </p><p>
     68  * The general rule for this file: If we are making calls out to
     69  * {@link SQLiteConnection} then we must be prepared to handle any
     70  * runtime exceptions it might throw at us.  Note that out-of-memory
     71  * is an {@link Error}, not a {@link RuntimeException}.  We don't trouble ourselves
     72  * handling out of memory because it is hard to do anything at all sensible then
     73  * and most likely the VM is about to crash.
     74  * </p>
     75  *
     76  * @hide
     77  */
     78 public final class SQLiteConnectionPool implements Closeable {
     79     private static final String TAG = "SQLiteConnectionPool";
     80 
     81     // Amount of time to wait in milliseconds before unblocking acquireConnection
     82     // and logging a message about the connection pool being busy.
     83     private static final long CONNECTION_POOL_BUSY_MILLIS = 30 * 1000; // 30 seconds
     84 
     85     private final CloseGuard mCloseGuard = CloseGuard.get();
     86 
     87     private final Object mLock = new Object();
     88     private final AtomicBoolean mConnectionLeaked = new AtomicBoolean();
     89     private final SQLiteDatabaseConfiguration mConfiguration;
     90     private int mMaxConnectionPoolSize;
     91     private boolean mIsOpen;
     92     private int mNextConnectionId;
     93 
     94     private ConnectionWaiter mConnectionWaiterPool;
     95     private ConnectionWaiter mConnectionWaiterQueue;
     96 
     97     // Strong references to all available connections.
     98     private final ArrayList<SQLiteConnection> mAvailableNonPrimaryConnections =
     99             new ArrayList<SQLiteConnection>();
    100     private SQLiteConnection mAvailablePrimaryConnection;
    101 
    102     @GuardedBy("mLock")
    103     private IdleConnectionHandler mIdleConnectionHandler;
    104 
    105     // Describes what should happen to an acquired connection when it is returned to the pool.
    106     enum AcquiredConnectionStatus {
    107         // The connection should be returned to the pool as usual.
    108         NORMAL,
    109 
    110         // The connection must be reconfigured before being returned.
    111         RECONFIGURE,
    112 
    113         // The connection must be closed and discarded.
    114         DISCARD,
    115     }
    116 
    117     // Weak references to all acquired connections.  The associated value
    118     // indicates whether the connection must be reconfigured before being
    119     // returned to the available connection list or discarded.
    120     // For example, the prepared statement cache size may have changed and
    121     // need to be updated in preparation for the next client.
    122     private final WeakHashMap<SQLiteConnection, AcquiredConnectionStatus> mAcquiredConnections =
    123             new WeakHashMap<SQLiteConnection, AcquiredConnectionStatus>();
    124 
    125     /**
    126      * Connection flag: Read-only.
    127      * <p>
    128      * This flag indicates that the connection will only be used to
    129      * perform read-only operations.
    130      * </p>
    131      */
    132     public static final int CONNECTION_FLAG_READ_ONLY = 1 << 0;
    133 
    134     /**
    135      * Connection flag: Primary connection affinity.
    136      * <p>
    137      * This flag indicates that the primary connection is required.
    138      * This flag helps support legacy applications that expect most data modifying
    139      * operations to be serialized by locking the primary database connection.
    140      * Setting this flag essentially implements the old "db lock" concept by preventing
    141      * an operation from being performed until it can obtain exclusive access to
    142      * the primary connection.
    143      * </p>
    144      */
    145     public static final int CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY = 1 << 1;
    146 
    147     /**
    148      * Connection flag: Connection is being used interactively.
    149      * <p>
    150      * This flag indicates that the connection is needed by the UI thread.
    151      * The connection pool can use this flag to elevate the priority
    152      * of the database connection request.
    153      * </p>
    154      */
    155     public static final int CONNECTION_FLAG_INTERACTIVE = 1 << 2;
    156 
    157     private SQLiteConnectionPool(SQLiteDatabaseConfiguration configuration) {
    158         mConfiguration = new SQLiteDatabaseConfiguration(configuration);
    159         setMaxConnectionPoolSizeLocked();
    160         // If timeout is set, setup idle connection handler
    161         // In case of MAX_VALUE - idle connections are never closed
    162         if (mConfiguration.idleConnectionTimeoutMs != Long.MAX_VALUE) {
    163             setupIdleConnectionHandler(Looper.getMainLooper(),
    164                     mConfiguration.idleConnectionTimeoutMs);
    165         }
    166     }
    167 
    168     @Override
    169     protected void finalize() throws Throwable {
    170         try {
    171             dispose(true);
    172         } finally {
    173             super.finalize();
    174         }
    175     }
    176 
    177     /**
    178      * Opens a connection pool for the specified database.
    179      *
    180      * @param configuration The database configuration.
    181      * @return The connection pool.
    182      *
    183      * @throws SQLiteException if a database error occurs.
    184      */
    185     public static SQLiteConnectionPool open(SQLiteDatabaseConfiguration configuration) {
    186         if (configuration == null) {
    187             throw new IllegalArgumentException("configuration must not be null.");
    188         }
    189 
    190         // Create the pool.
    191         SQLiteConnectionPool pool = new SQLiteConnectionPool(configuration);
    192         pool.open(); // might throw
    193         return pool;
    194     }
    195 
    196     // Might throw
    197     private void open() {
    198         // Open the primary connection.
    199         // This might throw if the database is corrupt.
    200         mAvailablePrimaryConnection = openConnectionLocked(mConfiguration,
    201                 true /*primaryConnection*/); // might throw
    202         // Mark it released so it can be closed after idle timeout
    203         synchronized (mLock) {
    204             if (mIdleConnectionHandler != null) {
    205                 mIdleConnectionHandler.connectionReleased(mAvailablePrimaryConnection);
    206             }
    207         }
    208 
    209         // Mark the pool as being open for business.
    210         mIsOpen = true;
    211         mCloseGuard.open("close");
    212     }
    213 
    214     /**
    215      * Closes the connection pool.
    216      * <p>
    217      * When the connection pool is closed, it will refuse all further requests
    218      * to acquire connections.  All connections that are currently available in
    219      * the pool are closed immediately.  Any connections that are still in use
    220      * will be closed as soon as they are returned to the pool.
    221      * </p>
    222      *
    223      * @throws IllegalStateException if the pool has been closed.
    224      */
    225     public void close() {
    226         dispose(false);
    227     }
    228 
    229     private void dispose(boolean finalized) {
    230         if (mCloseGuard != null) {
    231             if (finalized) {
    232                 mCloseGuard.warnIfOpen();
    233             }
    234             mCloseGuard.close();
    235         }
    236 
    237         if (!finalized) {
    238             // Close all connections.  We don't need (or want) to do this
    239             // when finalized because we don't know what state the connections
    240             // themselves will be in.  The finalizer is really just here for CloseGuard.
    241             // The connections will take care of themselves when their own finalizers run.
    242             synchronized (mLock) {
    243                 throwIfClosedLocked();
    244 
    245                 mIsOpen = false;
    246 
    247                 closeAvailableConnectionsAndLogExceptionsLocked();
    248 
    249                 final int pendingCount = mAcquiredConnections.size();
    250                 if (pendingCount != 0) {
    251                     Log.i(TAG, "The connection pool for " + mConfiguration.label
    252                             + " has been closed but there are still "
    253                             + pendingCount + " connections in use.  They will be closed "
    254                             + "as they are released back to the pool.");
    255                 }
    256 
    257                 wakeConnectionWaitersLocked();
    258             }
    259         }
    260     }
    261 
    262     /**
    263      * Reconfigures the database configuration of the connection pool and all of its
    264      * connections.
    265      * <p>
    266      * Configuration changes are propagated down to connections immediately if
    267      * they are available or as soon as they are released.  This includes changes
    268      * that affect the size of the pool.
    269      * </p>
    270      *
    271      * @param configuration The new configuration.
    272      *
    273      * @throws IllegalStateException if the pool has been closed.
    274      */
    275     public void reconfigure(SQLiteDatabaseConfiguration configuration) {
    276         if (configuration == null) {
    277             throw new IllegalArgumentException("configuration must not be null.");
    278         }
    279 
    280         synchronized (mLock) {
    281             throwIfClosedLocked();
    282 
    283             boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
    284                     & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
    285             if (walModeChanged) {
    286                 // WAL mode can only be changed if there are no acquired connections
    287                 // because we need to close all but the primary connection first.
    288                 if (!mAcquiredConnections.isEmpty()) {
    289                     throw new IllegalStateException("Write Ahead Logging (WAL) mode cannot "
    290                             + "be enabled or disabled while there are transactions in "
    291                             + "progress.  Finish all transactions and release all active "
    292                             + "database connections first.");
    293                 }
    294 
    295                 // Close all non-primary connections.  This should happen immediately
    296                 // because none of them are in use.
    297                 closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
    298                 assert mAvailableNonPrimaryConnections.isEmpty();
    299             }
    300 
    301             boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
    302                     != mConfiguration.foreignKeyConstraintsEnabled;
    303             if (foreignKeyModeChanged) {
    304                 // Foreign key constraints can only be changed if there are no transactions
    305                 // in progress.  To make this clear, we throw an exception if there are
    306                 // any acquired connections.
    307                 if (!mAcquiredConnections.isEmpty()) {
    308                     throw new IllegalStateException("Foreign Key Constraints cannot "
    309                             + "be enabled or disabled while there are transactions in "
    310                             + "progress.  Finish all transactions and release all active "
    311                             + "database connections first.");
    312                 }
    313             }
    314 
    315             if (mConfiguration.openFlags != configuration.openFlags) {
    316                 // If we are changing open flags and WAL mode at the same time, then
    317                 // we have no choice but to close the primary connection beforehand
    318                 // because there can only be one connection open when we change WAL mode.
    319                 if (walModeChanged) {
    320                     closeAvailableConnectionsAndLogExceptionsLocked();
    321                 }
    322 
    323                 // Try to reopen the primary connection using the new open flags then
    324                 // close and discard all existing connections.
    325                 // This might throw if the database is corrupt or cannot be opened in
    326                 // the new mode in which case existing connections will remain untouched.
    327                 SQLiteConnection newPrimaryConnection = openConnectionLocked(configuration,
    328                         true /*primaryConnection*/); // might throw
    329 
    330                 closeAvailableConnectionsAndLogExceptionsLocked();
    331                 discardAcquiredConnectionsLocked();
    332 
    333                 mAvailablePrimaryConnection = newPrimaryConnection;
    334                 mConfiguration.updateParametersFrom(configuration);
    335                 setMaxConnectionPoolSizeLocked();
    336             } else {
    337                 // Reconfigure the database connections in place.
    338                 mConfiguration.updateParametersFrom(configuration);
    339                 setMaxConnectionPoolSizeLocked();
    340 
    341                 closeExcessConnectionsAndLogExceptionsLocked();
    342                 reconfigureAllConnectionsLocked();
    343             }
    344 
    345             wakeConnectionWaitersLocked();
    346         }
    347     }
    348 
    349     /**
    350      * Acquires a connection from the pool.
    351      * <p>
    352      * The caller must call {@link #releaseConnection} to release the connection
    353      * back to the pool when it is finished.  Failure to do so will result
    354      * in much unpleasantness.
    355      * </p>
    356      *
    357      * @param sql If not null, try to find a connection that already has
    358      * the specified SQL statement in its prepared statement cache.
    359      * @param connectionFlags The connection request flags.
    360      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
    361      * @return The connection that was acquired, never null.
    362      *
    363      * @throws IllegalStateException if the pool has been closed.
    364      * @throws SQLiteException if a database error occurs.
    365      * @throws OperationCanceledException if the operation was canceled.
    366      */
    367     public SQLiteConnection acquireConnection(String sql, int connectionFlags,
    368             CancellationSignal cancellationSignal) {
    369         SQLiteConnection con = waitForConnection(sql, connectionFlags, cancellationSignal);
    370         synchronized (mLock) {
    371             if (mIdleConnectionHandler != null) {
    372                 mIdleConnectionHandler.connectionAcquired(con);
    373             }
    374         }
    375         return con;
    376     }
    377 
    378     /**
    379      * Releases a connection back to the pool.
    380      * <p>
    381      * It is ok to call this method after the pool has closed, to release
    382      * connections that were still in use at the time of closure.
    383      * </p>
    384      *
    385      * @param connection The connection to release.  Must not be null.
    386      *
    387      * @throws IllegalStateException if the connection was not acquired
    388      * from this pool or if it has already been released.
    389      */
    390     public void releaseConnection(SQLiteConnection connection) {
    391         synchronized (mLock) {
    392             if (mIdleConnectionHandler != null) {
    393                 mIdleConnectionHandler.connectionReleased(connection);
    394             }
    395             AcquiredConnectionStatus status = mAcquiredConnections.remove(connection);
    396             if (status == null) {
    397                 throw new IllegalStateException("Cannot perform this operation "
    398                         + "because the specified connection was not acquired "
    399                         + "from this pool or has already been released.");
    400             }
    401 
    402             if (!mIsOpen) {
    403                 closeConnectionAndLogExceptionsLocked(connection);
    404             } else if (connection.isPrimaryConnection()) {
    405                 if (recycleConnectionLocked(connection, status)) {
    406                     assert mAvailablePrimaryConnection == null;
    407                     mAvailablePrimaryConnection = connection;
    408                 }
    409                 wakeConnectionWaitersLocked();
    410             } else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize - 1) {
    411                 closeConnectionAndLogExceptionsLocked(connection);
    412             } else {
    413                 if (recycleConnectionLocked(connection, status)) {
    414                     mAvailableNonPrimaryConnections.add(connection);
    415                 }
    416                 wakeConnectionWaitersLocked();
    417             }
    418         }
    419     }
    420 
    421     // Can't throw.
    422     private boolean recycleConnectionLocked(SQLiteConnection connection,
    423             AcquiredConnectionStatus status) {
    424         if (status == AcquiredConnectionStatus.RECONFIGURE) {
    425             try {
    426                 connection.reconfigure(mConfiguration); // might throw
    427             } catch (RuntimeException ex) {
    428                 Log.e(TAG, "Failed to reconfigure released connection, closing it: "
    429                         + connection, ex);
    430                 status = AcquiredConnectionStatus.DISCARD;
    431             }
    432         }
    433         if (status == AcquiredConnectionStatus.DISCARD) {
    434             closeConnectionAndLogExceptionsLocked(connection);
    435             return false;
    436         }
    437         return true;
    438     }
    439 
    440     /**
    441      * Returns true if the session should yield the connection due to
    442      * contention over available database connections.
    443      *
    444      * @param connection The connection owned by the session.
    445      * @param connectionFlags The connection request flags.
    446      * @return True if the session should yield its connection.
    447      *
    448      * @throws IllegalStateException if the connection was not acquired
    449      * from this pool or if it has already been released.
    450      */
    451     public boolean shouldYieldConnection(SQLiteConnection connection, int connectionFlags) {
    452         synchronized (mLock) {
    453             if (!mAcquiredConnections.containsKey(connection)) {
    454                 throw new IllegalStateException("Cannot perform this operation "
    455                         + "because the specified connection was not acquired "
    456                         + "from this pool or has already been released.");
    457             }
    458 
    459             if (!mIsOpen) {
    460                 return false;
    461             }
    462 
    463             return isSessionBlockingImportantConnectionWaitersLocked(
    464                     connection.isPrimaryConnection(), connectionFlags);
    465         }
    466     }
    467 
    468     /**
    469      * Collects statistics about database connection memory usage.
    470      *
    471      * @param dbStatsList The list to populate.
    472      */
    473     public void collectDbStats(ArrayList<DbStats> dbStatsList) {
    474         synchronized (mLock) {
    475             if (mAvailablePrimaryConnection != null) {
    476                 mAvailablePrimaryConnection.collectDbStats(dbStatsList);
    477             }
    478 
    479             for (SQLiteConnection connection : mAvailableNonPrimaryConnections) {
    480                 connection.collectDbStats(dbStatsList);
    481             }
    482 
    483             for (SQLiteConnection connection : mAcquiredConnections.keySet()) {
    484                 connection.collectDbStatsUnsafe(dbStatsList);
    485             }
    486         }
    487     }
    488 
    489     // Might throw.
    490     private SQLiteConnection openConnectionLocked(SQLiteDatabaseConfiguration configuration,
    491             boolean primaryConnection) {
    492         final int connectionId = mNextConnectionId++;
    493         return SQLiteConnection.open(this, configuration,
    494                 connectionId, primaryConnection); // might throw
    495     }
    496 
    497     void onConnectionLeaked() {
    498         // This code is running inside of the SQLiteConnection finalizer.
    499         //
    500         // We don't know whether it is just the connection that has been finalized (and leaked)
    501         // or whether the connection pool has also been or is about to be finalized.
    502         // Consequently, it would be a bad idea to try to grab any locks or to
    503         // do any significant work here.  So we do the simplest possible thing and
    504         // set a flag.  waitForConnection() periodically checks this flag (when it
    505         // times out) so that it can recover from leaked connections and wake
    506         // itself or other threads up if necessary.
    507         //
    508         // You might still wonder why we don't try to do more to wake up the waiters
    509         // immediately.  First, as explained above, it would be hard to do safely
    510         // unless we started an extra Thread to function as a reference queue.  Second,
    511         // this is never supposed to happen in normal operation.  Third, there is no
    512         // guarantee that the GC will actually detect the leak in a timely manner so
    513         // it's not all that important that we recover from the leak in a timely manner
    514         // either.  Fourth, if a badly behaved application finds itself hung waiting for
    515         // several seconds while waiting for a leaked connection to be detected and recreated,
    516         // then perhaps its authors will have added incentive to fix the problem!
    517 
    518         Log.w(TAG, "A SQLiteConnection object for database '"
    519                 + mConfiguration.label + "' was leaked!  Please fix your application "
    520                 + "to end transactions in progress properly and to close the database "
    521                 + "when it is no longer needed.");
    522 
    523         mConnectionLeaked.set(true);
    524     }
    525 
    526     // Can't throw.
    527     private void closeAvailableConnectionsAndLogExceptionsLocked() {
    528         closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
    529 
    530         if (mAvailablePrimaryConnection != null) {
    531             closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
    532             mAvailablePrimaryConnection = null;
    533         }
    534     }
    535 
    536     // Can't throw.
    537     private boolean closeAvailableConnectionLocked(int connectionId) {
    538         final int count = mAvailableNonPrimaryConnections.size();
    539         for (int i = count - 1; i >= 0; i--) {
    540             SQLiteConnection c = mAvailableNonPrimaryConnections.get(i);
    541             if (c.getConnectionId() == connectionId) {
    542                 closeConnectionAndLogExceptionsLocked(c);
    543                 mAvailableNonPrimaryConnections.remove(i);
    544                 return true;
    545             }
    546         }
    547 
    548         if (mAvailablePrimaryConnection != null
    549                 && mAvailablePrimaryConnection.getConnectionId() == connectionId) {
    550             closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
    551             mAvailablePrimaryConnection = null;
    552             return true;
    553         }
    554         return false;
    555     }
    556 
    557     // Can't throw.
    558     private void closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked() {
    559         final int count = mAvailableNonPrimaryConnections.size();
    560         for (int i = 0; i < count; i++) {
    561             closeConnectionAndLogExceptionsLocked(mAvailableNonPrimaryConnections.get(i));
    562         }
    563         mAvailableNonPrimaryConnections.clear();
    564     }
    565 
    566     // Can't throw.
    567     private void closeExcessConnectionsAndLogExceptionsLocked() {
    568         int availableCount = mAvailableNonPrimaryConnections.size();
    569         while (availableCount-- > mMaxConnectionPoolSize - 1) {
    570             SQLiteConnection connection =
    571                     mAvailableNonPrimaryConnections.remove(availableCount);
    572             closeConnectionAndLogExceptionsLocked(connection);
    573         }
    574     }
    575 
    576     // Can't throw.
    577     private void closeConnectionAndLogExceptionsLocked(SQLiteConnection connection) {
    578         try {
    579             connection.close(); // might throw
    580             if (mIdleConnectionHandler != null) {
    581                 mIdleConnectionHandler.connectionClosed(connection);
    582             }
    583         } catch (RuntimeException ex) {
    584             Log.e(TAG, "Failed to close connection, its fate is now in the hands "
    585                     + "of the merciful GC: " + connection, ex);
    586         }
    587     }
    588 
    589     // Can't throw.
    590     private void discardAcquiredConnectionsLocked() {
    591         markAcquiredConnectionsLocked(AcquiredConnectionStatus.DISCARD);
    592     }
    593 
    594     // Can't throw.
    595     private void reconfigureAllConnectionsLocked() {
    596         if (mAvailablePrimaryConnection != null) {
    597             try {
    598                 mAvailablePrimaryConnection.reconfigure(mConfiguration); // might throw
    599             } catch (RuntimeException ex) {
    600                 Log.e(TAG, "Failed to reconfigure available primary connection, closing it: "
    601                         + mAvailablePrimaryConnection, ex);
    602                 closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
    603                 mAvailablePrimaryConnection = null;
    604             }
    605         }
    606 
    607         int count = mAvailableNonPrimaryConnections.size();
    608         for (int i = 0; i < count; i++) {
    609             final SQLiteConnection connection = mAvailableNonPrimaryConnections.get(i);
    610             try {
    611                 connection.reconfigure(mConfiguration); // might throw
    612             } catch (RuntimeException ex) {
    613                 Log.e(TAG, "Failed to reconfigure available non-primary connection, closing it: "
    614                         + connection, ex);
    615                 closeConnectionAndLogExceptionsLocked(connection);
    616                 mAvailableNonPrimaryConnections.remove(i--);
    617                 count -= 1;
    618             }
    619         }
    620 
    621         markAcquiredConnectionsLocked(AcquiredConnectionStatus.RECONFIGURE);
    622     }
    623 
    624     // Can't throw.
    625     private void markAcquiredConnectionsLocked(AcquiredConnectionStatus status) {
    626         if (!mAcquiredConnections.isEmpty()) {
    627             ArrayList<SQLiteConnection> keysToUpdate = new ArrayList<SQLiteConnection>(
    628                     mAcquiredConnections.size());
    629             for (Map.Entry<SQLiteConnection, AcquiredConnectionStatus> entry
    630                     : mAcquiredConnections.entrySet()) {
    631                 AcquiredConnectionStatus oldStatus = entry.getValue();
    632                 if (status != oldStatus
    633                         && oldStatus != AcquiredConnectionStatus.DISCARD) {
    634                     keysToUpdate.add(entry.getKey());
    635                 }
    636             }
    637             final int updateCount = keysToUpdate.size();
    638             for (int i = 0; i < updateCount; i++) {
    639                 mAcquiredConnections.put(keysToUpdate.get(i), status);
    640             }
    641         }
    642     }
    643 
    644     // Might throw.
    645     private SQLiteConnection waitForConnection(String sql, int connectionFlags,
    646             CancellationSignal cancellationSignal) {
    647         final boolean wantPrimaryConnection =
    648                 (connectionFlags & CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY) != 0;
    649 
    650         final ConnectionWaiter waiter;
    651         final int nonce;
    652         synchronized (mLock) {
    653             throwIfClosedLocked();
    654 
    655             // Abort if canceled.
    656             if (cancellationSignal != null) {
    657                 cancellationSignal.throwIfCanceled();
    658             }
    659 
    660             // Try to acquire a connection.
    661             SQLiteConnection connection = null;
    662             if (!wantPrimaryConnection) {
    663                 connection = tryAcquireNonPrimaryConnectionLocked(
    664                         sql, connectionFlags); // might throw
    665             }
    666             if (connection == null) {
    667                 connection = tryAcquirePrimaryConnectionLocked(connectionFlags); // might throw
    668             }
    669             if (connection != null) {
    670                 return connection;
    671             }
    672 
    673             // No connections available.  Enqueue a waiter in priority order.
    674             final int priority = getPriority(connectionFlags);
    675             final long startTime = SystemClock.uptimeMillis();
    676             waiter = obtainConnectionWaiterLocked(Thread.currentThread(), startTime,
    677                     priority, wantPrimaryConnection, sql, connectionFlags);
    678             ConnectionWaiter predecessor = null;
    679             ConnectionWaiter successor = mConnectionWaiterQueue;
    680             while (successor != null) {
    681                 if (priority > successor.mPriority) {
    682                     waiter.mNext = successor;
    683                     break;
    684                 }
    685                 predecessor = successor;
    686                 successor = successor.mNext;
    687             }
    688             if (predecessor != null) {
    689                 predecessor.mNext = waiter;
    690             } else {
    691                 mConnectionWaiterQueue = waiter;
    692             }
    693 
    694             nonce = waiter.mNonce;
    695         }
    696 
    697         // Set up the cancellation listener.
    698         if (cancellationSignal != null) {
    699             cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
    700                 @Override
    701                 public void onCancel() {
    702                     synchronized (mLock) {
    703                         if (waiter.mNonce == nonce) {
    704                             cancelConnectionWaiterLocked(waiter);
    705                         }
    706                     }
    707                 }
    708             });
    709         }
    710         try {
    711             // Park the thread until a connection is assigned or the pool is closed.
    712             // Rethrow an exception from the wait, if we got one.
    713             long busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
    714             long nextBusyTimeoutTime = waiter.mStartTime + busyTimeoutMillis;
    715             for (;;) {
    716                 // Detect and recover from connection leaks.
    717                 if (mConnectionLeaked.compareAndSet(true, false)) {
    718                     synchronized (mLock) {
    719                         wakeConnectionWaitersLocked();
    720                     }
    721                 }
    722 
    723                 // Wait to be unparked (may already have happened), a timeout, or interruption.
    724                 LockSupport.parkNanos(this, busyTimeoutMillis * 1000000L);
    725 
    726                 // Clear the interrupted flag, just in case.
    727                 Thread.interrupted();
    728 
    729                 // Check whether we are done waiting yet.
    730                 synchronized (mLock) {
    731                     throwIfClosedLocked();
    732 
    733                     final SQLiteConnection connection = waiter.mAssignedConnection;
    734                     final RuntimeException ex = waiter.mException;
    735                     if (connection != null || ex != null) {
    736                         recycleConnectionWaiterLocked(waiter);
    737                         if (connection != null) {
    738                             return connection;
    739                         }
    740                         throw ex; // rethrow!
    741                     }
    742 
    743                     final long now = SystemClock.uptimeMillis();
    744                     if (now < nextBusyTimeoutTime) {
    745                         busyTimeoutMillis = now - nextBusyTimeoutTime;
    746                     } else {
    747                         logConnectionPoolBusyLocked(now - waiter.mStartTime, connectionFlags);
    748                         busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
    749                         nextBusyTimeoutTime = now + busyTimeoutMillis;
    750                     }
    751                 }
    752             }
    753         } finally {
    754             // Remove the cancellation listener.
    755             if (cancellationSignal != null) {
    756                 cancellationSignal.setOnCancelListener(null);
    757             }
    758         }
    759     }
    760 
    761     // Can't throw.
    762     private void cancelConnectionWaiterLocked(ConnectionWaiter waiter) {
    763         if (waiter.mAssignedConnection != null || waiter.mException != null) {
    764             // Waiter is done waiting but has not woken up yet.
    765             return;
    766         }
    767 
    768         // Waiter must still be waiting.  Dequeue it.
    769         ConnectionWaiter predecessor = null;
    770         ConnectionWaiter current = mConnectionWaiterQueue;
    771         while (current != waiter) {
    772             assert current != null;
    773             predecessor = current;
    774             current = current.mNext;
    775         }
    776         if (predecessor != null) {
    777             predecessor.mNext = waiter.mNext;
    778         } else {
    779             mConnectionWaiterQueue = waiter.mNext;
    780         }
    781 
    782         // Send the waiter an exception and unpark it.
    783         waiter.mException = new OperationCanceledException();
    784         LockSupport.unpark(waiter.mThread);
    785 
    786         // Check whether removing this waiter will enable other waiters to make progress.
    787         wakeConnectionWaitersLocked();
    788     }
    789 
    790     // Can't throw.
    791     private void logConnectionPoolBusyLocked(long waitMillis, int connectionFlags) {
    792         final Thread thread = Thread.currentThread();
    793         StringBuilder msg = new StringBuilder();
    794         msg.append("The connection pool for database '").append(mConfiguration.label);
    795         msg.append("' has been unable to grant a connection to thread ");
    796         msg.append(thread.getId()).append(" (").append(thread.getName()).append(") ");
    797         msg.append("with flags 0x").append(Integer.toHexString(connectionFlags));
    798         msg.append(" for ").append(waitMillis * 0.001f).append(" seconds.\n");
    799 
    800         ArrayList<String> requests = new ArrayList<String>();
    801         int activeConnections = 0;
    802         int idleConnections = 0;
    803         if (!mAcquiredConnections.isEmpty()) {
    804             for (SQLiteConnection connection : mAcquiredConnections.keySet()) {
    805                 String description = connection.describeCurrentOperationUnsafe();
    806                 if (description != null) {
    807                     requests.add(description);
    808                     activeConnections += 1;
    809                 } else {
    810                     idleConnections += 1;
    811                 }
    812             }
    813         }
    814         int availableConnections = mAvailableNonPrimaryConnections.size();
    815         if (mAvailablePrimaryConnection != null) {
    816             availableConnections += 1;
    817         }
    818 
    819         msg.append("Connections: ").append(activeConnections).append(" active, ");
    820         msg.append(idleConnections).append(" idle, ");
    821         msg.append(availableConnections).append(" available.\n");
    822 
    823         if (!requests.isEmpty()) {
    824             msg.append("\nRequests in progress:\n");
    825             for (String request : requests) {
    826                 msg.append("  ").append(request).append("\n");
    827             }
    828         }
    829 
    830         Log.w(TAG, msg.toString());
    831     }
    832 
    833     // Can't throw.
    834     private void wakeConnectionWaitersLocked() {
    835         // Unpark all waiters that have requests that we can fulfill.
    836         // This method is designed to not throw runtime exceptions, although we might send
    837         // a waiter an exception for it to rethrow.
    838         ConnectionWaiter predecessor = null;
    839         ConnectionWaiter waiter = mConnectionWaiterQueue;
    840         boolean primaryConnectionNotAvailable = false;
    841         boolean nonPrimaryConnectionNotAvailable = false;
    842         while (waiter != null) {
    843             boolean unpark = false;
    844             if (!mIsOpen) {
    845                 unpark = true;
    846             } else {
    847                 try {
    848                     SQLiteConnection connection = null;
    849                     if (!waiter.mWantPrimaryConnection && !nonPrimaryConnectionNotAvailable) {
    850                         connection = tryAcquireNonPrimaryConnectionLocked(
    851                                 waiter.mSql, waiter.mConnectionFlags); // might throw
    852                         if (connection == null) {
    853                             nonPrimaryConnectionNotAvailable = true;
    854                         }
    855                     }
    856                     if (connection == null && !primaryConnectionNotAvailable) {
    857                         connection = tryAcquirePrimaryConnectionLocked(
    858                                 waiter.mConnectionFlags); // might throw
    859                         if (connection == null) {
    860                             primaryConnectionNotAvailable = true;
    861                         }
    862                     }
    863                     if (connection != null) {
    864                         waiter.mAssignedConnection = connection;
    865                         unpark = true;
    866                     } else if (nonPrimaryConnectionNotAvailable && primaryConnectionNotAvailable) {
    867                         // There are no connections available and the pool is still open.
    868                         // We cannot fulfill any more connection requests, so stop here.
    869                         break;
    870                     }
    871                 } catch (RuntimeException ex) {
    872                     // Let the waiter handle the exception from acquiring a connection.
    873                     waiter.mException = ex;
    874                     unpark = true;
    875                 }
    876             }
    877 
    878             final ConnectionWaiter successor = waiter.mNext;
    879             if (unpark) {
    880                 if (predecessor != null) {
    881                     predecessor.mNext = successor;
    882                 } else {
    883                     mConnectionWaiterQueue = successor;
    884                 }
    885                 waiter.mNext = null;
    886 
    887                 LockSupport.unpark(waiter.mThread);
    888             } else {
    889                 predecessor = waiter;
    890             }
    891             waiter = successor;
    892         }
    893     }
    894 
    895     // Might throw.
    896     private SQLiteConnection tryAcquirePrimaryConnectionLocked(int connectionFlags) {
    897         // If the primary connection is available, acquire it now.
    898         SQLiteConnection connection = mAvailablePrimaryConnection;
    899         if (connection != null) {
    900             mAvailablePrimaryConnection = null;
    901             finishAcquireConnectionLocked(connection, connectionFlags); // might throw
    902             return connection;
    903         }
    904 
    905         // Make sure that the primary connection actually exists and has just been acquired.
    906         for (SQLiteConnection acquiredConnection : mAcquiredConnections.keySet()) {
    907             if (acquiredConnection.isPrimaryConnection()) {
    908                 return null;
    909             }
    910         }
    911 
    912         // Uhoh.  No primary connection!  Either this is the first time we asked
    913         // for it, or maybe it leaked?
    914         connection = openConnectionLocked(mConfiguration,
    915                 true /*primaryConnection*/); // might throw
    916         finishAcquireConnectionLocked(connection, connectionFlags); // might throw
    917         return connection;
    918     }
    919 
    920     // Might throw.
    921     private SQLiteConnection tryAcquireNonPrimaryConnectionLocked(
    922             String sql, int connectionFlags) {
    923         // Try to acquire the next connection in the queue.
    924         SQLiteConnection connection;
    925         final int availableCount = mAvailableNonPrimaryConnections.size();
    926         if (availableCount > 1 && sql != null) {
    927             // If we have a choice, then prefer a connection that has the
    928             // prepared statement in its cache.
    929             for (int i = 0; i < availableCount; i++) {
    930                 connection = mAvailableNonPrimaryConnections.get(i);
    931                 if (connection.isPreparedStatementInCache(sql)) {
    932                     mAvailableNonPrimaryConnections.remove(i);
    933                     finishAcquireConnectionLocked(connection, connectionFlags); // might throw
    934                     return connection;
    935                 }
    936             }
    937         }
    938         if (availableCount > 0) {
    939             // Otherwise, just grab the next one.
    940             connection = mAvailableNonPrimaryConnections.remove(availableCount - 1);
    941             finishAcquireConnectionLocked(connection, connectionFlags); // might throw
    942             return connection;
    943         }
    944 
    945         // Expand the pool if needed.
    946         int openConnections = mAcquiredConnections.size();
    947         if (mAvailablePrimaryConnection != null) {
    948             openConnections += 1;
    949         }
    950         if (openConnections >= mMaxConnectionPoolSize) {
    951             return null;
    952         }
    953         connection = openConnectionLocked(mConfiguration,
    954                 false /*primaryConnection*/); // might throw
    955         finishAcquireConnectionLocked(connection, connectionFlags); // might throw
    956         return connection;
    957     }
    958 
    959     // Might throw.
    960     private void finishAcquireConnectionLocked(SQLiteConnection connection, int connectionFlags) {
    961         try {
    962             final boolean readOnly = (connectionFlags & CONNECTION_FLAG_READ_ONLY) != 0;
    963             connection.setOnlyAllowReadOnlyOperations(readOnly);
    964 
    965             mAcquiredConnections.put(connection, AcquiredConnectionStatus.NORMAL);
    966         } catch (RuntimeException ex) {
    967             Log.e(TAG, "Failed to prepare acquired connection for session, closing it: "
    968                     + connection +", connectionFlags=" + connectionFlags);
    969             closeConnectionAndLogExceptionsLocked(connection);
    970             throw ex; // rethrow!
    971         }
    972     }
    973 
    974     private boolean isSessionBlockingImportantConnectionWaitersLocked(
    975             boolean holdingPrimaryConnection, int connectionFlags) {
    976         ConnectionWaiter waiter = mConnectionWaiterQueue;
    977         if (waiter != null) {
    978             final int priority = getPriority(connectionFlags);
    979             do {
    980                 // Only worry about blocked connections that have same or lower priority.
    981                 if (priority > waiter.mPriority) {
    982                     break;
    983                 }
    984 
    985                 // If we are holding the primary connection then we are blocking the waiter.
    986                 // Likewise, if we are holding a non-primary connection and the waiter
    987                 // would accept a non-primary connection, then we are blocking the waier.
    988                 if (holdingPrimaryConnection || !waiter.mWantPrimaryConnection) {
    989                     return true;
    990                 }
    991 
    992                 waiter = waiter.mNext;
    993             } while (waiter != null);
    994         }
    995         return false;
    996     }
    997 
    998     private static int getPriority(int connectionFlags) {
    999         return (connectionFlags & CONNECTION_FLAG_INTERACTIVE) != 0 ? 1 : 0;
   1000     }
   1001 
   1002     private void setMaxConnectionPoolSizeLocked() {
   1003         if (!mConfiguration.isInMemoryDb()
   1004                 && (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
   1005             mMaxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
   1006         } else {
   1007             // We don't actually need to always restrict the connection pool size to 1
   1008             // for non-WAL databases.  There might be reasons to use connection pooling
   1009             // with other journal modes. However, we should always keep pool size of 1 for in-memory
   1010             // databases since every :memory: db is separate from another.
   1011             // For now, enabling connection pooling and using WAL are the same thing in the API.
   1012             mMaxConnectionPoolSize = 1;
   1013         }
   1014     }
   1015 
   1016     /**
   1017      * Set up the handler based on the provided looper and timeout.
   1018      */
   1019     @VisibleForTesting
   1020     public void setupIdleConnectionHandler(Looper looper, long timeoutMs) {
   1021         synchronized (mLock) {
   1022             mIdleConnectionHandler = new IdleConnectionHandler(looper, timeoutMs);
   1023         }
   1024     }
   1025 
   1026     void disableIdleConnectionHandler() {
   1027         synchronized (mLock) {
   1028             mIdleConnectionHandler = null;
   1029         }
   1030     }
   1031 
   1032     private void throwIfClosedLocked() {
   1033         if (!mIsOpen) {
   1034             throw new IllegalStateException("Cannot perform this operation "
   1035                     + "because the connection pool has been closed.");
   1036         }
   1037     }
   1038 
   1039     private ConnectionWaiter obtainConnectionWaiterLocked(Thread thread, long startTime,
   1040             int priority, boolean wantPrimaryConnection, String sql, int connectionFlags) {
   1041         ConnectionWaiter waiter = mConnectionWaiterPool;
   1042         if (waiter != null) {
   1043             mConnectionWaiterPool = waiter.mNext;
   1044             waiter.mNext = null;
   1045         } else {
   1046             waiter = new ConnectionWaiter();
   1047         }
   1048         waiter.mThread = thread;
   1049         waiter.mStartTime = startTime;
   1050         waiter.mPriority = priority;
   1051         waiter.mWantPrimaryConnection = wantPrimaryConnection;
   1052         waiter.mSql = sql;
   1053         waiter.mConnectionFlags = connectionFlags;
   1054         return waiter;
   1055     }
   1056 
   1057     private void recycleConnectionWaiterLocked(ConnectionWaiter waiter) {
   1058         waiter.mNext = mConnectionWaiterPool;
   1059         waiter.mThread = null;
   1060         waiter.mSql = null;
   1061         waiter.mAssignedConnection = null;
   1062         waiter.mException = null;
   1063         waiter.mNonce += 1;
   1064         mConnectionWaiterPool = waiter;
   1065     }
   1066 
   1067     /**
   1068      * Dumps debugging information about this connection pool.
   1069      *
   1070      * @param printer The printer to receive the dump, not null.
   1071      * @param verbose True to dump more verbose information.
   1072      */
   1073     public void dump(Printer printer, boolean verbose) {
   1074         Printer indentedPrinter = PrefixPrinter.create(printer, "    ");
   1075         synchronized (mLock) {
   1076             printer.println("Connection pool for " + mConfiguration.path + ":");
   1077             printer.println("  Open: " + mIsOpen);
   1078             printer.println("  Max connections: " + mMaxConnectionPoolSize);
   1079             if (mConfiguration.isLookasideConfigSet()) {
   1080                 printer.println("  Lookaside config: sz=" + mConfiguration.lookasideSlotSize
   1081                         + " cnt=" + mConfiguration.lookasideSlotCount);
   1082             }
   1083             if (mConfiguration.idleConnectionTimeoutMs != Long.MAX_VALUE) {
   1084                 printer.println(
   1085                         "  Idle connection timeout: " + mConfiguration.idleConnectionTimeoutMs);
   1086             }
   1087             printer.println("  Available primary connection:");
   1088             if (mAvailablePrimaryConnection != null) {
   1089                 mAvailablePrimaryConnection.dump(indentedPrinter, verbose);
   1090             } else {
   1091                 indentedPrinter.println("<none>");
   1092             }
   1093 
   1094             printer.println("  Available non-primary connections:");
   1095             if (!mAvailableNonPrimaryConnections.isEmpty()) {
   1096                 final int count = mAvailableNonPrimaryConnections.size();
   1097                 for (int i = 0; i < count; i++) {
   1098                     mAvailableNonPrimaryConnections.get(i).dump(indentedPrinter, verbose);
   1099                 }
   1100             } else {
   1101                 indentedPrinter.println("<none>");
   1102             }
   1103 
   1104             printer.println("  Acquired connections:");
   1105             if (!mAcquiredConnections.isEmpty()) {
   1106                 for (Map.Entry<SQLiteConnection, AcquiredConnectionStatus> entry :
   1107                         mAcquiredConnections.entrySet()) {
   1108                     final SQLiteConnection connection = entry.getKey();
   1109                     connection.dumpUnsafe(indentedPrinter, verbose);
   1110                     indentedPrinter.println("  Status: " + entry.getValue());
   1111                 }
   1112             } else {
   1113                 indentedPrinter.println("<none>");
   1114             }
   1115 
   1116             printer.println("  Connection waiters:");
   1117             if (mConnectionWaiterQueue != null) {
   1118                 int i = 0;
   1119                 final long now = SystemClock.uptimeMillis();
   1120                 for (ConnectionWaiter waiter = mConnectionWaiterQueue; waiter != null;
   1121                         waiter = waiter.mNext, i++) {
   1122                     indentedPrinter.println(i + ": waited for "
   1123                             + ((now - waiter.mStartTime) * 0.001f)
   1124                             + " ms - thread=" + waiter.mThread
   1125                             + ", priority=" + waiter.mPriority
   1126                             + ", sql='" + waiter.mSql + "'");
   1127                 }
   1128             } else {
   1129                 indentedPrinter.println("<none>");
   1130             }
   1131         }
   1132     }
   1133 
   1134     @Override
   1135     public String toString() {
   1136         return "SQLiteConnectionPool: " + mConfiguration.path;
   1137     }
   1138 
   1139     private static final class ConnectionWaiter {
   1140         public ConnectionWaiter mNext;
   1141         public Thread mThread;
   1142         public long mStartTime;
   1143         public int mPriority;
   1144         public boolean mWantPrimaryConnection;
   1145         public String mSql;
   1146         public int mConnectionFlags;
   1147         public SQLiteConnection mAssignedConnection;
   1148         public RuntimeException mException;
   1149         public int mNonce;
   1150     }
   1151 
   1152     private class IdleConnectionHandler extends Handler {
   1153         private final long mTimeout;
   1154 
   1155         IdleConnectionHandler(Looper looper, long timeout) {
   1156             super(looper);
   1157             mTimeout = timeout;
   1158         }
   1159 
   1160         @Override
   1161         public void handleMessage(Message msg) {
   1162             // Skip the (obsolete) message if the handler has changed
   1163             synchronized (mLock) {
   1164                 if (this != mIdleConnectionHandler) {
   1165                     return;
   1166                 }
   1167                 if (closeAvailableConnectionLocked(msg.what)) {
   1168                     if (Log.isLoggable(TAG, Log.DEBUG)) {
   1169                         Log.d(TAG, "Closed idle connection " + mConfiguration.label + " " + msg.what
   1170                                 + " after " + mTimeout);
   1171                     }
   1172                 }
   1173             }
   1174         }
   1175 
   1176         void connectionReleased(SQLiteConnection con) {
   1177             sendEmptyMessageDelayed(con.getConnectionId(), mTimeout);
   1178         }
   1179 
   1180         void connectionAcquired(SQLiteConnection con) {
   1181             // Remove any pending close operations
   1182             removeMessages(con.getConnectionId());
   1183         }
   1184 
   1185         void connectionClosed(SQLiteConnection con) {
   1186             removeMessages(con.getConnectionId());
   1187         }
   1188     }
   1189 }
   1190