Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ae3c3682 authored by Dan Stoza's avatar Dan Stoza
Browse files

BufferQueue: Guard against unbounded queue growth

Adds logic to dequeueBuffer that blocks if there are currently too
many buffers in the queue. This prevents unbounded growth around
times where the slots are cleared but the queue is not (e.g.,
during rapid connect/disconnect or setBufferCount activity). This
replaces the fix from ag/377958 in a more general way.

Bug: 11293214
Change-Id: Ieb7adfcd076ff7ffe3d4d369397b2c29cf5099c3
parent 7f605bd4
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -160,8 +160,10 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
    }

    mCore->mQueue.erase(front);
    // TODO: Should this call be after we free a slot while dropping buffers?
    // Simply acquiring the next buffer doesn't enable a producer to dequeue.

    // We might have freed a slot while dropping old buffers, or the producer
    // may be blocked waiting for the number of buffers in the queue to
    // decrease.
    mCore->mDequeueCondition.broadcast();

    ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
+31 −35
Original line number Diff line number Diff line
@@ -205,9 +205,21 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller,
            }
        }

        // If no buffer is found, wait for a buffer to be released or for
        // the max buffer count to change
        tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT);
        // If we disconnect and reconnect quickly, we can be in a state where
        // our slots are empty but we have many buffers in the queue. This can
        // cause us to run out of memory if we outrun the consumer. Wait here if
        // it looks like we have too many buffers queued up.
        bool tooManyBuffers = mCore->mQueue.size() > maxBufferCount;
        if (tooManyBuffers) {
            BQ_LOGV("%s: queue size is %d, waiting", caller,
                    mCore->mQueue.size());
        }

        // If no buffer is found, or if the queue has too many buffers
        // outstanding, wait for a buffer to be acquired or released, or for the
        // max buffer count to change.
        tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) ||
                   tooManyBuffers;
        if (tryAgain) {
            // Return an error if we're in non-blocking mode (producer and
            // consumer are controlled by the application).
@@ -663,11 +675,6 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
    BQ_LOGV("connect(P): api=%d producerControlledByApp=%s", api,
            producerControlledByApp ? "true" : "false");

    // If we disconnect and reconnect quickly, we can be in a state where our
    // slots are empty but we have many buffers in the queue. This can cause us
    // to run out of memory if we outrun the consumer. Wait here if it looks
    // like we have too many buffers queued up.
    while (true) {
    if (mCore->mIsAbandoned) {
        BQ_LOGE("connect(P): BufferQueue has been abandoned");
        return NO_INIT;
@@ -689,17 +696,6 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
        return BAD_VALUE;
    }

        size_t maxBufferCount = mCore->getMaxBufferCountLocked(false);
        if (mCore->mQueue.size() <= maxBufferCount) {
            // The queue size seems small enough to proceed
            // TODO: Make this bound tighter?
            break;
        }

        BQ_LOGV("connect(P): queue size is %d, waiting", mCore->mQueue.size());
        mCore->mDequeueCondition.wait(mCore->mMutex);
    }

    int status = NO_ERROR;
    switch (api) {
        case NATIVE_WINDOW_API_EGL: