void BufferQueueCore::waitWhileAllocatingLocked(std::unique_lock<std::mutex>& lock) const {
    ATRACE_CALL();
    while (mIsAllocating) {
        mIsAllocatingCondition.wait(lock);
    }
}
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
                                            uint32_t width, uint32_t height, PixelFormat format,
                                            uint64_t usage, uint64_t* outBufferAge,
                                            FrameEventHistoryDelta* outTimestamps) {
    { // Autolock scope
        std::unique_lock<std::mutex> lock(mCore->mMutex);
        // If we don't have a free buffer, but we are currently allocating, we wait until allocation
        // is finished such that we don't allocate in parallel.
        if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) {
            mDequeueWaitingForAllocation = true;
            mCore->waitWhileAllocatingLocked(lock);
            mDequeueWaitingForAllocation = false;
            mDequeueWaitingForAllocationCondition.notify_all();
        }
        ...
        if ((buffer == nullptr) ||
                buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))
        {
            mCore->mIsAllocating = true;
            returnFlags |= BUFFER_NEEDS_REALLOCATION;
        }
    } // Autolock scope
    ...
    if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
        status_t error = graphicBuffer->initCheck();
        { // Autolock scope
            std::lock_guard<std::mutex> lock(mCore->mMutex);
            mCore->mIsAllocating = false;
            mCore->mIsAllocatingCondition.notify_all();
        } // Autolock scope
    }
}

void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height,
        PixelFormat format, uint64_t usage) {
    { // Autolock scope
        std::unique_lock<std::mutex> lock(mCore->mMutex);
        mCore->waitWhileAllocatingLocked(lock);
        ...
        mCore->mIsAllocating = true;
    } // Autolock scope
    status_t result = graphicBuffer->initCheck();
    ...
    { // Autolock scope
        std::unique_lock<std::mutex> lock(mCore->mMutex);
        mCore->mIsAllocating = false;
        mCore->mIsAllocatingCondition.notify_all();
        // If dequeue is waiting for to allocate a buffer, release the lock until it's not
        // waiting anymore so it can use the buffer we just allocated.
        while (mDequeueWaitingForAllocation) {
            mDequeueWaitingForAllocationCondition.wait(lock);
        }
    } // Autolock scope
}