From f53f9c6d3668490f6c68f5c094c28f645c1b3da3 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Mon, 10 Dec 2012 17:06:44 -0800 Subject: [PATCH] [DO NOT MERGE] GraphicBufferAllocator: stall alloc for async frees This change makes GraphicBufferAllocator::alloc wait for pending async frees to complete before attempting to allocate a gralloc buffer if there are more than 8 pending async frees. Bug: 7696861 Change-Id: I1fae86e13edefcaa153b8ce9fd057f335716059e --- libs/ui/GraphicBufferAllocator.cpp | 170 +++++++++++++++++++---------- 1 file changed, 113 insertions(+), 57 deletions(-) diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 72acd7dcb..fb434109a 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -90,6 +90,105 @@ void GraphicBufferAllocator::dumpToSystemLog() ALOGD("%s", s.string()); } +class BufferLiberatorThread : public Thread { +public: + + static void queueCaptiveBuffer(buffer_handle_t handle) { + size_t queueSize; + { + Mutex::Autolock lock(sMutex); + if (sThread == NULL) { + sThread = new BufferLiberatorThread; + sThread->run("BufferLiberator"); + } + + sThread->mQueue.push_back(handle); + sThread->mQueuedCondition.signal(); + queueSize = sThread->mQueue.size(); + } + } + + static void waitForLiberation() { + Mutex::Autolock lock(sMutex); + + waitForLiberationLocked(); + } + + static void maybeWaitForLiberation() { + Mutex::Autolock lock(sMutex); + if (sThread != NULL) { + if (sThread->mQueue.size() > 8) { + waitForLiberationLocked(); + } + } + } + +private: + + BufferLiberatorThread() {} + + virtual bool threadLoop() { + buffer_handle_t handle; + { // Scope for mutex + Mutex::Autolock lock(sMutex); + while (mQueue.isEmpty()) { + mQueuedCondition.wait(sMutex); + } + handle = mQueue[0]; + } + + status_t err; + GraphicBufferAllocator& gba(GraphicBufferAllocator::get()); + { // Scope for tracing + ATRACE_NAME("gralloc::free"); + err = gba.mAllocDev->free(gba.mAllocDev, handle); + } + ALOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err)); + + if (err == NO_ERROR) { + Mutex::Autolock _l(GraphicBufferAllocator::sLock); + KeyedVector& + list(GraphicBufferAllocator::sAllocList); + list.removeItem(handle); + } + + { // Scope for mutex + Mutex::Autolock lock(sMutex); + mQueue.removeAt(0); + mFreedCondition.broadcast(); + } + + return true; + } + + static void waitForLiberationLocked() { + if (sThread == NULL) { + return; + } + + const nsecs_t timeout = 500 * 1000 * 1000; + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t timeToStop = now + timeout; + while (!sThread->mQueue.isEmpty() && now < timeToStop) { + sThread->mFreedCondition.waitRelative(sMutex, timeToStop - now); + now = systemTime(SYSTEM_TIME_MONOTONIC); + } + + if (!sThread->mQueue.isEmpty()) { + ALOGW("waitForLiberationLocked timed out"); + } + } + + static Mutex sMutex; + static sp sThread; + Vector mQueue; + Condition mQueuedCondition; + Condition mFreedCondition; +}; + +Mutex BufferLiberatorThread::sMutex; +sp BufferLiberatorThread::sThread; + status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format, int usage, buffer_handle_t* handle, int32_t* stride) { @@ -100,13 +199,24 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma w = h = 1; // we have a h/w allocator and h/w buffer is requested - status_t err; - + status_t err; + + // If too many async frees are queued up then wait for some of them to + // complete before attempting to allocate more memory. This is exercised + // by the android.opengl.cts.GLSurfaceViewTest CTS test. + BufferLiberatorThread::maybeWaitForLiberation(); + err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride); + if (err != NO_ERROR) { + ALOGW("WOW! gralloc alloc failed, waiting for pending frees!"); + BufferLiberatorThread::waitForLiberation(); + err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride); + } + ALOGW_IF(err, "alloc(%u, %u, %d, %08x, ...) failed %d (%s)", w, h, format, usage, err, strerror(-err)); - + if (err == NO_ERROR) { Mutex::Autolock _l(sLock); KeyedVector& list(sAllocList); @@ -129,60 +239,6 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma return err; } -class BufferLiberatorThread : public Thread { -public: - - static void queueCaptiveBuffer(buffer_handle_t handle) { - static sp thread(new BufferLiberatorThread); - static bool running = false; - if (!running) { - thread->run("BufferLiberator"); - running = true; - } - { - Mutex::Autolock lock(thread->mMutex); - thread->mQueue.push_back(handle); - thread->mCondition.signal(); - } - } - -private: - - BufferLiberatorThread() {} - - virtual bool threadLoop() { - buffer_handle_t handle; - { - Mutex::Autolock lock(mMutex); - while (mQueue.isEmpty()) { - mCondition.wait(mMutex); - } - handle = mQueue[0]; - mQueue.removeAt(0); - } - - status_t err; - GraphicBufferAllocator& gba(GraphicBufferAllocator::get()); - { // Scope for tracing - ATRACE_NAME("gralloc::free"); - err = gba.mAllocDev->free(gba.mAllocDev, handle); - } - ALOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err)); - - if (err == NO_ERROR) { - Mutex::Autolock _l(GraphicBufferAllocator::sLock); - KeyedVector& - list(GraphicBufferAllocator::sAllocList); - list.removeItem(handle); - } - - return true; - } - - Vector mQueue; - Condition mCondition; - Mutex mMutex; -}; status_t GraphicBufferAllocator::free(buffer_handle_t handle) {