// waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may
// block if there are no available slots and we are not in non-blocking
// mode (producer and consumer controlled by the application). If it blocks,
// it will release mCore->mMutex while blocked so that other operations on
// the BufferQueue may succeed.
status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
        std::unique_lock<std::mutex>& lock, int* found) const {
    auto callerString = (caller == FreeSlotCaller::Dequeue) ?
            "dequeueBuffer" : "attachBuffer";

找最老的 FREE slot,如果没有可用的 slots,且在非阻塞模式该函数会阻塞。

bool tryAgain = true;
while (tryAgain) {
    if (mCore->mIsAbandoned) {
        BQ_LOGE("%s: BufferQueue has been abandoned", callerString);
        return NO_INIT;
    }

    int dequeuedCount = 0;
    int acquiredCount = 0;
    for (int s : mCore->mActiveBuffers) {
        if (mSlots[s].mBufferState.isDequeued()) {
            ++dequeuedCount;
        }
        if (mSlots[s].mBufferState.isAcquired()) {
            ++acquiredCount;
        }
    }

遍历 mActiveBuffers,计算 dequeuedCountacquiredCount

    // Producers are not allowed to dequeue more than
    // mMaxDequeuedBufferCount buffers.
    // This check is only done if a buffer has already been queued
    if (mCore->mBufferHasBeenQueued &&
            dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
        // Supress error logs when timeout is non-negative.
        if (mDequeueTimeout < 0) {
            BQ_LOGE("%s: attempting to exceed the max dequeued buffer "
                    "count (%d)", callerString,
                    mCore->mMaxDequeuedBufferCount);
        }
        return INVALID_OPERATION;
    }

如果 dequeueCount 已经达到了 mMaxDequeuedBufferCount,则返回 INVALID_OPERATIONmMaxDequeuedBufferCount 默认值是 1。

<aside> ❓ 疑问:为什么要检查 mBufferHasBeenQueued,意思是生产者 queueBuffer() 之前可以无限 dequeueBuffer()

</aside>

    *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.
    const int maxBufferCount = mCore->getMaxBufferCountLocked();
    bool tooManyBuffers = mCore->mQueue.size()
                        > static_cast<size_t>(maxBufferCount);
    if (tooManyBuffers) {
        BQ_LOGV("%s: queue size is %zu, waiting", callerString,
                mCore->mQueue.size());
    } else {
        // If in shared buffer mode and a shared buffer exists, always
        // return it.
        if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot !=
                BufferQueueCore::INVALID_BUFFER_SLOT) {
            *found = mCore->mSharedBufferSlot;
        } else {
            if (caller == FreeSlotCaller::Dequeue) {
                // If we're calling this from dequeue, prefer free buffers
                int slot = getFreeBufferLocked();
                if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                    *found = slot;
                } else if (mCore->mAllowAllocation) {
                    *found = getFreeSlotLocked();
                }
            } else {
                // If we're calling this from attach, prefer free slots
                int slot = getFreeSlotLocked();
                if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                    *found = slot;
                } else {
                    *found = getFreeBufferLocked();
                }
            }
        }
    }

当快速断开并重新连接时,所有的 slot 都是 FREE,但是 mQueue 中还有 buffer。如果这时候生产者比消费者执行更快,可能会造成内存耗尽。所以如果 tooManyBufferstrue,则打印 log,而不找 FREE 的 slot,等待消费 buffer。

如果调用者是 BufferQueueProducer::dequeueBuffer(),则优先调用 getFreeBufferLocked()mFreeBuffers 取一个 slot。如果调用者是 attachBuffer(),表示生产者带着 buffer 来的,所以优先调用 getFreeSlotLocked()mFreeSlots 取一个 slot。

    // 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).
        // However, the consumer is allowed to briefly acquire an extra
        // buffer (which could cause us to have to wait here), which is
        // okay, since it is only used to implement an atomic acquire +
        // release (e.g., in GLConsumer::updateTexImage())
        if ((mCore->mDequeueBufferCannotBlock || mCore->mAsyncMode) &&
                (acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
            return WOULD_BLOCK;
        }
        if (mDequeueTimeout >= 0) {
            std::cv_status result = mCore->mDequeueCondition.wait_for(lock,
                    std::chrono::nanoseconds(mDequeueTimeout));
            if (result == std::cv_status::timeout) {
                return TIMED_OUT;
            }
        } else {
            mCore->mDequeueCondition.wait(lock);
        }
    }
} // while (tryAgain)