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