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
17package android.database.sqlite;
18
19import dalvik.system.CloseGuard;
20
21import android.database.sqlite.SQLiteDebug.DbStats;
22import android.os.CancellationSignal;
23import android.os.OperationCanceledException;
24import android.os.SystemClock;
25import android.util.Log;
26import android.util.PrefixPrinter;
27import android.util.Printer;
28
29import java.io.Closeable;
30import java.util.ArrayList;
31import java.util.Map;
32import java.util.WeakHashMap;
33import java.util.concurrent.atomic.AtomicBoolean;
34import 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 */
72public 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