From 8507586903fa803abf535853a169913f2cf2e555 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Fri, 18 Oct 2013 15:50:24 -0700 Subject: [PATCH] Wait for buffers to drain When a BufferQueue producer disconnects and reconnects, we retain the previously-queued buffers but empty the slots. This allows the number of queued buffers to grow without limit. The low-memory killer does not approve. Bug 11069934 Change-Id: Ia2eaa954c7a3904b54209a3701dba01689e204d8 --- libs/gui/BufferQueue.cpp | 45 ++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index c165a6820..2aecb6778 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -644,6 +644,7 @@ status_t BufferQueue::connect(const sp& token, producerControlledByApp ? "true" : "false"); Mutex::Autolock lock(mMutex); +retry: if (mAbandoned) { ST_LOGE("connect: BufferQueue has been abandoned!"); return NO_INIT; @@ -654,29 +655,41 @@ status_t BufferQueue::connect(const sp& token, return NO_INIT; } + if (mConnectedApi != NO_CONNECTED_API) { + ST_LOGE("connect: already connected (cur=%d, req=%d)", + mConnectedApi, api); + return -EINVAL; + } + + // 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. + int maxBufferCount = getMaxBufferCountLocked(false); // worst-case, i.e. largest value + if (mQueue.size() > (size_t) maxBufferCount) { + // TODO: make this bound tighter? + ST_LOGV("queue size is %d, waiting", mQueue.size()); + mDequeueCondition.wait(mMutex); + goto retry; + } + int err = NO_ERROR; switch (api) { case NATIVE_WINDOW_API_EGL: case NATIVE_WINDOW_API_CPU: case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: - if (mConnectedApi != NO_CONNECTED_API) { - ST_LOGE("connect: already connected (cur=%d, req=%d)", - mConnectedApi, api); - err = -EINVAL; - } else { - mConnectedApi = api; - output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, mQueue.size()); + mConnectedApi = api; + output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, mQueue.size()); - // set-up a death notification so that we can disconnect - // automatically when/if the remote producer dies. - if (token != NULL && token->remoteBinder() != NULL) { - status_t err = token->linkToDeath(static_cast(this)); - if (err == NO_ERROR) { - mConnectedProducerToken = token; - } else { - ALOGE("linkToDeath failed: %s (%d)", strerror(-err), err); - } + // set-up a death notification so that we can disconnect + // automatically when/if the remote producer dies. + if (token != NULL && token->remoteBinder() != NULL) { + status_t err = token->linkToDeath(static_cast(this)); + if (err == NO_ERROR) { + mConnectedProducerToken = token; + } else { + ALOGE("linkToDeath failed: %s (%d)", strerror(-err), err); } } break;