Skip to content

Commit fd4edb2

Browse files
author
Jim Shargo
committed
BufferQueue: Fix deadlock in setMaxAcquiredBufferCount
Uncovered this while testing. The deadlock happens when: - ConsumerBase::setMaxAcquiredBufferCount locks itself - Calls IGBC::setMaxAcquiredBufferCount, which can call ConsumerListener::onBuffersReleased - Which, in ConsumerBase, will take the lock again Instead of this, we add a callback to be called instead of the IConsumerListener. This callback is called on the same stack, with the lock held, so that we can resolve everything atomically. Bug: b/393639203 Flag: EXEMPT small cleanup Test: new test Change-Id: Iddd8f902d1fd0aeed6aac095eaa6c0b870ffff70
1 parent b61e79d commit fd4edb2

7 files changed

Lines changed: 52 additions & 12 deletions

File tree

libs/gui/BufferQueueConsumer.cpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@
1414
* limitations under the License.
1515
*/
1616

17-
#include <inttypes.h>
18-
#include <pwd.h>
19-
#include <sys/types.h>
20-
2117
#define LOG_TAG "BufferQueueConsumer"
2218
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
2319
//#define LOG_NDEBUG 0
@@ -48,6 +44,11 @@
4844

4945
#include <com_android_graphics_libgui_flags.h>
5046

47+
#include <inttypes.h>
48+
#include <pwd.h>
49+
#include <sys/types.h>
50+
#include <optional>
51+
5152
namespace android {
5253

5354
// Macros for include BufferQueueCore information in log messages
@@ -767,11 +768,15 @@ status_t BufferQueueConsumer::setMaxBufferCount(int bufferCount) {
767768
return NO_ERROR;
768769
}
769770

771+
status_t BufferQueueConsumer::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
772+
return setMaxAcquiredBufferCount(maxAcquiredBuffers, std::nullopt);
773+
}
774+
770775
status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
771-
int maxAcquiredBuffers) {
776+
int maxAcquiredBuffers, std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) {
772777
ATRACE_FORMAT("%s(%d)", __func__, maxAcquiredBuffers);
773778

774-
sp<IConsumerListener> listener;
779+
std::optional<OnBufferReleasedCallback> callback;
775780
{ // Autolock scope
776781
std::unique_lock<std::mutex> lock(mCore->mMutex);
777782

@@ -833,13 +838,20 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
833838
BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
834839
mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
835840
VALIDATE_CONSISTENCY();
836-
if (delta < 0 && mCore->mBufferReleasedCbEnabled) {
837-
listener = mCore->mConsumerListener;
841+
if (delta < 0) {
842+
if (onBuffersReleasedCallback) {
843+
callback = std::move(onBuffersReleasedCallback);
844+
} else if (mCore->mBufferReleasedCbEnabled) {
845+
callback = [listener = mCore->mConsumerListener]() {
846+
listener->onBuffersReleased();
847+
};
848+
}
838849
}
839850
}
851+
840852
// Call back without lock held
841-
if (listener != nullptr) {
842-
listener->onBuffersReleased();
853+
if (callback) {
854+
(*callback)();
843855
}
844856

845857
return NO_ERROR;

libs/gui/ConsumerBase.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,10 @@ void ConsumerBase::onFrameReplaced(const BufferItem &item) {
264264

265265
void ConsumerBase::onBuffersReleased() {
266266
Mutex::Autolock lock(mMutex);
267+
onBuffersReleasedLocked();
268+
}
267269

270+
void ConsumerBase::onBuffersReleasedLocked() {
268271
CB_LOGV("onBuffersReleased");
269272

270273
if (mAbandoned) {
@@ -481,7 +484,8 @@ status_t ConsumerBase::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
481484
CB_LOGE("setMaxAcquiredBufferCount: ConsumerBase is abandoned!");
482485
return NO_INIT;
483486
}
484-
return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers);
487+
return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers,
488+
{[this]() { onBuffersReleasedLocked(); }});
485489
}
486490

487491
status_t ConsumerBase::setConsumerIsProtected(bool isProtected) {

libs/gui/include/gui/BufferQueueConsumer.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,10 @@ class BufferQueueConsumer : public IGraphicBufferConsumer {
122122
// setMaxAcquiredBufferCount sets the maximum number of buffers that can
123123
// be acquired by the consumer at one time (default 1). This call will
124124
// fail if a producer is connected to the BufferQueue.
125-
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
125+
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override;
126+
virtual status_t setMaxAcquiredBufferCount(
127+
int maxAcquiredBuffers,
128+
std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) override;
126129

127130
// setConsumerName sets the name used in logging
128131
status_t setConsumerName(const String8& name) override;

libs/gui/include/gui/ConsumerBase.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ class ConsumerBase : public virtual RefBase,
191191
#endif
192192
virtual int getSlotForBufferLocked(const sp<GraphicBuffer>& buffer);
193193

194+
virtual void onBuffersReleasedLocked();
195+
194196
virtual status_t detachBufferLocked(int slotIndex);
195197

196198
// freeBufferLocked frees up the given buffer slot. If the slot has been

libs/gui/include/gui/IGraphicBufferConsumer.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,9 @@ class IGraphicBufferConsumer : public RefBase {
243243
// maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS. It also cannot
244244
// cause the maxBufferCount value to be exceeded.
245245
//
246+
// If called with onBuffersReleasedCallback, that call back will be called in lieu of
247+
// IConsumerListener::onBuffersReleased.
248+
//
246249
// Return of a value other than NO_ERROR means an error has occurred:
247250
// * NO_INIT - the BufferQueue has been abandoned
248251
// * BAD_VALUE - one of the below conditions occurred:
@@ -253,6 +256,11 @@ class IGraphicBufferConsumer : public RefBase {
253256
// * INVALID_OPERATION - attempting to call this after a producer connected.
254257
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
255258

259+
using OnBufferReleasedCallback = std::function<void(void)>;
260+
virtual status_t setMaxAcquiredBufferCount(
261+
int maxAcquiredBuffers,
262+
std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) = 0;
263+
256264
// setConsumerName sets the name used in logging
257265
virtual status_t setConsumerName(const String8& name) = 0;
258266

libs/gui/include/gui/mock/GraphicBufferConsumer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class GraphicBufferConsumer : public IGraphicBufferConsumer {
4747
MOCK_METHOD2(setDefaultBufferSize, status_t(uint32_t, uint32_t));
4848
MOCK_METHOD1(setMaxBufferCount, status_t(int));
4949
MOCK_METHOD1(setMaxAcquiredBufferCount, status_t(int));
50+
MOCK_METHOD2(setMaxAcquiredBufferCount, status_t(int, std::optional<OnBufferReleasedCallback>));
5051
MOCK_METHOD1(setConsumerName, status_t(const String8&));
5152
MOCK_METHOD1(setDefaultBufferFormat, status_t(PixelFormat));
5253
MOCK_METHOD1(setDefaultBufferDataSpace, status_t(android_dataspace));

libs/gui/tests/BufferItemConsumer_test.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <gui/Surface.h>
2525
#include <ui/BufferQueueDefs.h>
2626
#include <ui/GraphicBuffer.h>
27+
#include <utils/Errors.h>
2728

2829
#include <unordered_set>
2930

@@ -235,6 +236,15 @@ TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DeleteBufferItemConsumer) {
235236
ASSERT_EQ(1, GetFreedBufferCount());
236237
}
237238

239+
TEST_F(BufferItemConsumerTest, ResizeAcquireCount) {
240+
EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 1));
241+
EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 2));
242+
EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 1));
243+
EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 2));
244+
EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 1));
245+
EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 1));
246+
}
247+
238248
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
239249
// Test that delete BufferItemConsumer triggers onBufferFreed.
240250
TEST_F(BufferItemConsumerTest, DetachBufferWithBuffer) {

0 commit comments

Comments
 (0)