Loading core/java/android/database/sqlite/SQLiteConnectionPool.java +18 −6 Original line number Diff line number Diff line Loading @@ -170,8 +170,8 @@ public final class SQLiteConnectionPool implements Closeable { // If timeout is set, setup idle connection handler // In case of MAX_VALUE - idle connections are never closed if (mConfiguration.idleConnectionTimeoutMs != Long.MAX_VALUE) { setupIdleConnectionHandler(Looper.getMainLooper(), mConfiguration.idleConnectionTimeoutMs); setupIdleConnectionHandler( Looper.getMainLooper(), mConfiguration.idleConnectionTimeoutMs, null); } } Loading Loading @@ -425,7 +425,7 @@ public final class SQLiteConnectionPool implements Closeable { mAvailablePrimaryConnection = connection; } wakeConnectionWaitersLocked(); } else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize - 1) { } else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize) { closeConnectionAndLogExceptionsLocked(connection); } else { if (recycleConnectionLocked(connection, status)) { Loading Loading @@ -456,6 +456,11 @@ public final class SQLiteConnectionPool implements Closeable { return true; } @VisibleForTesting public boolean hasAnyAvailableNonPrimaryConnection() { return mAvailableNonPrimaryConnections.size() > 0; } /** * Returns true if the session should yield the connection due to * contention over available database connections. Loading Loading @@ -1061,9 +1066,11 @@ public final class SQLiteConnectionPool implements Closeable { * Set up the handler based on the provided looper and timeout. */ @VisibleForTesting public void setupIdleConnectionHandler(Looper looper, long timeoutMs) { public void setupIdleConnectionHandler( Looper looper, long timeoutMs, Runnable onAllConnectionsIdle) { synchronized (mLock) { mIdleConnectionHandler = new IdleConnectionHandler(looper, timeoutMs); mIdleConnectionHandler = new IdleConnectionHandler(looper, timeoutMs, onAllConnectionsIdle); } } Loading Loading @@ -1228,10 +1235,12 @@ public final class SQLiteConnectionPool implements Closeable { private class IdleConnectionHandler extends Handler { private final long mTimeout; private final Runnable mOnAllConnectionsIdle; IdleConnectionHandler(Looper looper, long timeout) { IdleConnectionHandler(Looper looper, long timeout, Runnable onAllConnectionsIdle) { super(looper); mTimeout = timeout; this.mOnAllConnectionsIdle = onAllConnectionsIdle; } @Override Loading @@ -1247,6 +1256,9 @@ public final class SQLiteConnectionPool implements Closeable { + " after " + mTimeout); } } if (mOnAllConnectionsIdle != null) { mOnAllConnectionsIdle.run(); } } } Loading core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java +29 −1 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; import java.util.concurrent.CountDownLatch; /** * Tests for {@link SQLiteConnectionPool} Loading Loading @@ -74,7 +75,7 @@ public class SQLiteConnectionPoolTest { Log.i(TAG, "Starting " + thread.getName()); thread.start(); SQLiteConnectionPool pool = SQLiteConnectionPool.open(mTestConf); pool.setupIdleConnectionHandler(thread.getLooper(), 100); pool.setupIdleConnectionHandler(thread.getLooper(), 100, null); SQLiteConnection c1 = pool.acquireConnection("pragma user_version", 0, null); assertEquals("First connection should be returned", 0, c1.getConnectionId()); pool.releaseConnection(c1); Loading @@ -89,4 +90,31 @@ public class SQLiteConnectionPoolTest { pool.close(); thread.quit(); } @Test public void testNonprimaryConnectionPoolRecycling() throws InterruptedException { HandlerThread thread = new HandlerThread("test-close-idle-connections-thread"); thread.start(); SQLiteConnectionPool pool = SQLiteConnectionPool.open(mTestConf); CountDownLatch latch = new CountDownLatch(1); Runnable onIdleConnectionTimeout = () -> latch.countDown(); pool.setupIdleConnectionHandler(thread.getLooper(), 1, onIdleConnectionTimeout); assertTrue("When the pool was just opened there should only be a primary connection.", !pool.hasAnyAvailableNonPrimaryConnection()); SQLiteConnection connection = pool.acquireConnection("pragma user_version", 0, null); pool.releaseConnection(connection); assertTrue("First time acquire should will return the primary connection.", !pool.hasAnyAvailableNonPrimaryConnection()); // Wait for primary connection to time out latch.await(); // Now that the primary is closed, acquiring again should open a non primary connection connection = pool.acquireConnection("pragma user_version", 0, null); pool.releaseConnection(connection); assertTrue("There should be an available non primary connection in the pool.", pool.hasAnyAvailableNonPrimaryConnection()); pool.close(); } } Loading
core/java/android/database/sqlite/SQLiteConnectionPool.java +18 −6 Original line number Diff line number Diff line Loading @@ -170,8 +170,8 @@ public final class SQLiteConnectionPool implements Closeable { // If timeout is set, setup idle connection handler // In case of MAX_VALUE - idle connections are never closed if (mConfiguration.idleConnectionTimeoutMs != Long.MAX_VALUE) { setupIdleConnectionHandler(Looper.getMainLooper(), mConfiguration.idleConnectionTimeoutMs); setupIdleConnectionHandler( Looper.getMainLooper(), mConfiguration.idleConnectionTimeoutMs, null); } } Loading Loading @@ -425,7 +425,7 @@ public final class SQLiteConnectionPool implements Closeable { mAvailablePrimaryConnection = connection; } wakeConnectionWaitersLocked(); } else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize - 1) { } else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize) { closeConnectionAndLogExceptionsLocked(connection); } else { if (recycleConnectionLocked(connection, status)) { Loading Loading @@ -456,6 +456,11 @@ public final class SQLiteConnectionPool implements Closeable { return true; } @VisibleForTesting public boolean hasAnyAvailableNonPrimaryConnection() { return mAvailableNonPrimaryConnections.size() > 0; } /** * Returns true if the session should yield the connection due to * contention over available database connections. Loading Loading @@ -1061,9 +1066,11 @@ public final class SQLiteConnectionPool implements Closeable { * Set up the handler based on the provided looper and timeout. */ @VisibleForTesting public void setupIdleConnectionHandler(Looper looper, long timeoutMs) { public void setupIdleConnectionHandler( Looper looper, long timeoutMs, Runnable onAllConnectionsIdle) { synchronized (mLock) { mIdleConnectionHandler = new IdleConnectionHandler(looper, timeoutMs); mIdleConnectionHandler = new IdleConnectionHandler(looper, timeoutMs, onAllConnectionsIdle); } } Loading Loading @@ -1228,10 +1235,12 @@ public final class SQLiteConnectionPool implements Closeable { private class IdleConnectionHandler extends Handler { private final long mTimeout; private final Runnable mOnAllConnectionsIdle; IdleConnectionHandler(Looper looper, long timeout) { IdleConnectionHandler(Looper looper, long timeout, Runnable onAllConnectionsIdle) { super(looper); mTimeout = timeout; this.mOnAllConnectionsIdle = onAllConnectionsIdle; } @Override Loading @@ -1247,6 +1256,9 @@ public final class SQLiteConnectionPool implements Closeable { + " after " + mTimeout); } } if (mOnAllConnectionsIdle != null) { mOnAllConnectionsIdle.run(); } } } Loading
core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java +29 −1 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; import java.util.concurrent.CountDownLatch; /** * Tests for {@link SQLiteConnectionPool} Loading Loading @@ -74,7 +75,7 @@ public class SQLiteConnectionPoolTest { Log.i(TAG, "Starting " + thread.getName()); thread.start(); SQLiteConnectionPool pool = SQLiteConnectionPool.open(mTestConf); pool.setupIdleConnectionHandler(thread.getLooper(), 100); pool.setupIdleConnectionHandler(thread.getLooper(), 100, null); SQLiteConnection c1 = pool.acquireConnection("pragma user_version", 0, null); assertEquals("First connection should be returned", 0, c1.getConnectionId()); pool.releaseConnection(c1); Loading @@ -89,4 +90,31 @@ public class SQLiteConnectionPoolTest { pool.close(); thread.quit(); } @Test public void testNonprimaryConnectionPoolRecycling() throws InterruptedException { HandlerThread thread = new HandlerThread("test-close-idle-connections-thread"); thread.start(); SQLiteConnectionPool pool = SQLiteConnectionPool.open(mTestConf); CountDownLatch latch = new CountDownLatch(1); Runnable onIdleConnectionTimeout = () -> latch.countDown(); pool.setupIdleConnectionHandler(thread.getLooper(), 1, onIdleConnectionTimeout); assertTrue("When the pool was just opened there should only be a primary connection.", !pool.hasAnyAvailableNonPrimaryConnection()); SQLiteConnection connection = pool.acquireConnection("pragma user_version", 0, null); pool.releaseConnection(connection); assertTrue("First time acquire should will return the primary connection.", !pool.hasAnyAvailableNonPrimaryConnection()); // Wait for primary connection to time out latch.await(); // Now that the primary is closed, acquiring again should open a non primary connection connection = pool.acquireConnection("pragma user_version", 0, null); pool.releaseConnection(connection); assertTrue("There should be an available non primary connection in the pool.", pool.hasAnyAvailableNonPrimaryConnection()); pool.close(); } }