BufferQueue: release mutex while allocating. DO NOT MERGE
BufferQueueProducer::allocateBuffers used to keep the BufferQueueCore
mutex while doing the buffer allocation, which would cause the consumer
(which also needs the mutex) to block if the allocation takes a long
time.
Instead, release the mutex while doing the allocation, and grab it again
before filling the slots. Keep a bool state and a condvar to prevent
other producers from trying to allocate the slots while the mutex is
released.
Bug: 11792166
Change-Id: I4ab1319995ef892be2beba892f1fdbf50ce0416d
(cherry picked from commit ea96044470a29133321c681080870b9d31f81a19)
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 40e6884..ec1e631 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -64,7 +64,9 @@
mMaxAcquiredBufferCount(1),
mBufferHasBeenQueued(false),
mFrameCounter(0),
- mTransformHint(0)
+ mTransformHint(0),
+ mIsAllocating(false),
+ mIsAllocatingCondition()
{
if (allocator == NULL) {
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
@@ -226,4 +228,11 @@
(item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle);
}
+void BufferQueueCore::waitWhileAllocatingLocked() const {
+ ATRACE_CALL();
+ while (mIsAllocating) {
+ mIsAllocatingCondition.wait(mMutex);
+ }
+}
+
} // namespace android
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 6feebf7..cdc810d 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -74,6 +74,7 @@
sp<IConsumerListener> listener;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked();
if (mCore->mIsAbandoned) {
BQ_LOGE("setBufferCount: BufferQueue has been abandoned");
@@ -266,6 +267,7 @@
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked();
if (format == 0) {
format = mCore->mDefaultBufferFormat;
@@ -424,6 +426,7 @@
}
Mutex::Autolock lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked();
if (mCore->mIsAbandoned) {
BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
@@ -468,6 +471,7 @@
}
Mutex::Autolock lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked();
status_t returnFlags = NO_ERROR;
int found;
@@ -796,6 +800,7 @@
sp<IConsumerListener> listener;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked();
if (mCore->mIsAbandoned) {
// It's not really an error to disconnect after the surface has
@@ -862,55 +867,101 @@
void BufferQueueProducer::allocateBuffers(bool async, uint32_t width,
uint32_t height, uint32_t format, uint32_t usage) {
- Vector<int> freeSlots;
+ ATRACE_CALL();
+ while (true) {
+ Vector<int> freeSlots;
+ size_t newBufferCount = 0;
+ uint32_t allocWidth = 0;
+ uint32_t allocHeight = 0;
+ uint32_t allocFormat = 0;
+ uint32_t allocUsage = 0;
+ { // Autolock scope
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked();
- Mutex::Autolock lock(mCore->mMutex);
+ int currentBufferCount = 0;
+ for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
+ if (mSlots[slot].mGraphicBuffer != NULL) {
+ ++currentBufferCount;
+ } else {
+ if (mSlots[slot].mBufferState != BufferSlot::FREE) {
+ BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE",
+ slot);
+ continue;
+ }
- int currentBufferCount = 0;
- for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
- if (mSlots[slot].mGraphicBuffer != NULL) {
- ++currentBufferCount;
- } else {
- if (mSlots[slot].mBufferState != BufferSlot::FREE) {
- BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE",
- slot);
+ freeSlots.push_front(slot);
+ }
+ }
+
+ int maxBufferCount = mCore->getMaxBufferCountLocked(async);
+ BQ_LOGV("allocateBuffers: allocating from %d buffers up to %d buffers",
+ currentBufferCount, maxBufferCount);
+ if (maxBufferCount <= currentBufferCount)
+ return;
+ newBufferCount = maxBufferCount - currentBufferCount;
+ if (freeSlots.size() < newBufferCount) {
+ BQ_LOGE("allocateBuffers: ran out of free slots");
+ return;
+ }
+ allocWidth = width > 0 ? width : mCore->mDefaultWidth;
+ allocHeight = height > 0 ? height : mCore->mDefaultHeight;
+ allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
+ allocUsage = usage | mCore->mConsumerUsageBits;
+
+ mCore->mIsAllocating = true;
+ } // Autolock scope
+
+ Vector<sp<GraphicBuffer> > buffers;
+ for (size_t i = 0; i < newBufferCount; ++i) {
+ status_t result = NO_ERROR;
+ sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
+ allocWidth, allocHeight, allocFormat, allocUsage, &result));
+ if (result != NO_ERROR) {
+ BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
+ " %u, usage %u)", width, height, format, usage);
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->mIsAllocating = false;
+ mCore->mIsAllocatingCondition.broadcast();
+ return;
+ }
+ buffers.push_back(graphicBuffer);
+ }
+
+ { // Autolock scope
+ Mutex::Autolock lock(mCore->mMutex);
+ uint32_t checkWidth = width > 0 ? width : mCore->mDefaultWidth;
+ uint32_t checkHeight = height > 0 ? height : mCore->mDefaultHeight;
+ uint32_t checkFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
+ uint32_t checkUsage = usage | mCore->mConsumerUsageBits;
+ if (checkWidth != allocWidth || checkHeight != allocHeight ||
+ checkFormat != allocFormat || checkUsage != allocUsage) {
+ // Something changed while we released the lock. Retry.
+ BQ_LOGV("allocateBuffers: size/format/usage changed while allocating. Retrying.");
+ mCore->mIsAllocating = false;
+ mCore->mIsAllocatingCondition.broadcast();
continue;
}
- freeSlots.push_front(slot);
- }
- }
+ for (size_t i = 0; i < newBufferCount; ++i) {
+ int slot = freeSlots[i];
+ if (mSlots[slot].mBufferState != BufferSlot::FREE) {
+ // A consumer allocated the FREE slot with attachBuffer. Discard the buffer we
+ // allocated.
+ BQ_LOGV("allocateBuffers: slot %d was acquired while allocating. "
+ "Dropping allocated buffer.", slot);
+ continue;
+ }
+ mCore->freeBufferLocked(slot); // Clean up the slot first
+ mSlots[slot].mGraphicBuffer = buffers[i];
+ mSlots[slot].mFrameNumber = 0;
+ mSlots[slot].mFence = Fence::NO_FENCE;
+ BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", slot);
+ }
- int maxBufferCount = mCore->getMaxBufferCountLocked(async);
- BQ_LOGV("allocateBuffers: allocating from %d buffers up to %d buffers",
- currentBufferCount, maxBufferCount);
- for (; currentBufferCount < maxBufferCount; ++currentBufferCount) {
- if (freeSlots.empty()) {
- BQ_LOGE("allocateBuffers: ran out of free slots");
- return;
- }
-
- width = width > 0 ? width : mCore->mDefaultWidth;
- height = height > 0 ? height : mCore->mDefaultHeight;
- format = format != 0 ? format : mCore->mDefaultBufferFormat;
- usage |= mCore->mConsumerUsageBits;
-
- status_t result = NO_ERROR;
- sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
- width, height, format, usage, &result));
- if (result != NO_ERROR) {
- BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
- " %u, usage %u)", width, height, format, usage);
- return;
- }
-
- int slot = freeSlots[freeSlots.size() - 1];
- mCore->freeBufferLocked(slot); // Clean up the slot first
- mSlots[slot].mGraphicBuffer = graphicBuffer;
- mSlots[slot].mFrameNumber = 0;
- mSlots[slot].mFence = Fence::NO_FENCE;
- BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", slot);
- freeSlots.pop();
+ mCore->mIsAllocating = false;
+ mCore->mIsAllocatingCondition.broadcast();
+ } // Autolock scope
}
}