예제 #1
0
  // Can't throw.
  private void wakeConnectionWaitersLocked() {
    // Unpark all waiters that have requests that we can fulfill.
    // This method is designed to not throw runtime exceptions, although we might send
    // a waiter an exception for it to rethrow.
    ConnectionWaiter predecessor = null;
    ConnectionWaiter waiter = mConnectionWaiterQueue;
    boolean primaryConnectionNotAvailable = false;
    boolean nonPrimaryConnectionNotAvailable = false;
    while (waiter != null) {
      boolean unpark = false;
      if (!mIsOpen) {
        unpark = true;
      } else {
        try {
          SQLiteConnection connection = null;
          if (!waiter.mWantPrimaryConnection && !nonPrimaryConnectionNotAvailable) {
            connection =
                tryAcquireNonPrimaryConnectionLocked(
                    waiter.mSql, waiter.mConnectionFlags); // might throw
            if (connection == null) {
              nonPrimaryConnectionNotAvailable = true;
            }
          }
          if (connection == null && !primaryConnectionNotAvailable) {
            connection = tryAcquirePrimaryConnectionLocked(waiter.mConnectionFlags); // might throw
            if (connection == null) {
              primaryConnectionNotAvailable = true;
            }
          }
          if (connection != null) {
            waiter.mAssignedConnection = connection;
            unpark = true;
          } else if (nonPrimaryConnectionNotAvailable && primaryConnectionNotAvailable) {
            // There are no connections available and the pool is still open.
            // We cannot fulfill any more connection requests, so stop here.
            break;
          }
        } catch (RuntimeException ex) {
          // Let the waiter handle the exception from acquiring a connection.
          waiter.mException = ex;
          unpark = true;
        }
      }

      final ConnectionWaiter successor = waiter.mNext;
      if (unpark) {
        if (predecessor != null) {
          predecessor.mNext = successor;
        } else {
          mConnectionWaiterQueue = successor;
        }
        waiter.mNext = null;

        LockSupport.unpark(waiter.mThread);
      } else {
        predecessor = waiter;
      }
      waiter = successor;
    }
  }
예제 #2
0
  // Can't throw.
  private void cancelConnectionWaiterLocked(ConnectionWaiter waiter) {
    if (waiter.mAssignedConnection != null || waiter.mException != null) {
      // Waiter is done waiting but has not woken up yet.
      return;
    }

    // Waiter must still be waiting.  Dequeue it.
    ConnectionWaiter predecessor = null;
    ConnectionWaiter current = mConnectionWaiterQueue;
    while (current != waiter) {
      assert current != null;
      predecessor = current;
      current = current.mNext;
    }
    if (predecessor != null) {
      predecessor.mNext = waiter.mNext;
    } else {
      mConnectionWaiterQueue = waiter.mNext;
    }

    // Send the waiter an exception and unpark it.
    waiter.mException = new OperationCanceledException();
    LockSupport.unpark(waiter.mThread);

    // Check whether removing this waiter will enable other waiters to make progress.
    wakeConnectionWaitersLocked();
  }
예제 #3
0
 private void recycleConnectionWaiterLocked(ConnectionWaiter waiter) {
   waiter.mNext = mConnectionWaiterPool;
   waiter.mThread = null;
   waiter.mSql = null;
   waiter.mAssignedConnection = null;
   waiter.mException = null;
   waiter.mNonce += 1;
   mConnectionWaiterPool = waiter;
 }
예제 #4
0
 private ConnectionWaiter obtainConnectionWaiterLocked(
     Thread thread,
     long startTime,
     int priority,
     boolean wantPrimaryConnection,
     String sql,
     int connectionFlags) {
   ConnectionWaiter waiter = mConnectionWaiterPool;
   if (waiter != null) {
     mConnectionWaiterPool = waiter.mNext;
     waiter.mNext = null;
   } else {
     waiter = new ConnectionWaiter();
   }
   waiter.mThread = thread;
   waiter.mStartTime = startTime;
   waiter.mPriority = priority;
   waiter.mWantPrimaryConnection = wantPrimaryConnection;
   waiter.mSql = sql;
   waiter.mConnectionFlags = connectionFlags;
   return waiter;
 }
예제 #5
0
  // Might throw.
  private SQLiteConnection waitForConnection(
      String sql, int connectionFlags, CancellationSignal cancellationSignal) {
    final boolean wantPrimaryConnection =
        (connectionFlags & CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY) != 0;

    final ConnectionWaiter waiter;
    final int nonce;
    synchronized (mLock) {
      throwIfClosedLocked();

      // Abort if canceled.
      if (cancellationSignal != null) {
        cancellationSignal.throwIfCanceled();
      }

      // Try to acquire a connection.
      SQLiteConnection connection = null;
      if (!wantPrimaryConnection) {
        connection = tryAcquireNonPrimaryConnectionLocked(sql, connectionFlags); // might throw
      }
      if (connection == null) {
        connection = tryAcquirePrimaryConnectionLocked(connectionFlags); // might throw
      }
      if (connection != null) {
        return connection;
      }

      // No connections available.  Enqueue a waiter in priority order.
      final int priority = getPriority(connectionFlags);
      final long startTime = SystemClock.uptimeMillis();
      waiter =
          obtainConnectionWaiterLocked(
              Thread.currentThread(),
              startTime,
              priority,
              wantPrimaryConnection,
              sql,
              connectionFlags);
      ConnectionWaiter predecessor = null;
      ConnectionWaiter successor = mConnectionWaiterQueue;
      while (successor != null) {
        if (priority > successor.mPriority) {
          waiter.mNext = successor;
          break;
        }
        predecessor = successor;
        successor = successor.mNext;
      }
      if (predecessor != null) {
        predecessor.mNext = waiter;
      } else {
        mConnectionWaiterQueue = waiter;
      }

      nonce = waiter.mNonce;
    }

    // Set up the cancellation listener.
    if (cancellationSignal != null) {
      cancellationSignal.setOnCancelListener(
          new CancellationSignal.OnCancelListener() {
            @Override
            public void onCancel() {
              synchronized (mLock) {
                if (waiter.mNonce == nonce) {
                  cancelConnectionWaiterLocked(waiter);
                }
              }
            }
          });
    }
    try {
      // Park the thread until a connection is assigned or the pool is closed.
      // Rethrow an exception from the wait, if we got one.
      long busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
      long nextBusyTimeoutTime = waiter.mStartTime + busyTimeoutMillis;
      for (; ; ) {
        // Detect and recover from connection leaks.
        if (mConnectionLeaked.compareAndSet(true, false)) {
          synchronized (mLock) {
            wakeConnectionWaitersLocked();
          }
        }

        // Wait to be unparked (may already have happened), a timeout, or interruption.
        LockSupport.parkNanos(this, busyTimeoutMillis * 1000000L);

        // Clear the interrupted flag, just in case.
        Thread.interrupted();

        // Check whether we are done waiting yet.
        synchronized (mLock) {
          throwIfClosedLocked();

          final SQLiteConnection connection = waiter.mAssignedConnection;
          final RuntimeException ex = waiter.mException;
          if (connection != null || ex != null) {
            recycleConnectionWaiterLocked(waiter);
            if (connection != null) {
              return connection;
            }
            throw ex; // rethrow!
          }

          final long now = SystemClock.uptimeMillis();
          if (now < nextBusyTimeoutTime) {
            busyTimeoutMillis = now - nextBusyTimeoutTime;
          } else {
            logConnectionPoolBusyLocked(now - waiter.mStartTime, connectionFlags);
            busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
            nextBusyTimeoutTime = now + busyTimeoutMillis;
          }
        }
      }
    } finally {
      // Remove the cancellation listener.
      if (cancellationSignal != null) {
        cancellationSignal.setOnCancelListener(null);
      }
    }
  }