gnunet-android

GNUnet for Android
Log | Files | Refs | README

SQLiteConnectionPool.java (45432B)


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