DO NOT MERGE GraphicBufferAllocator: make frees async

This change makes GraphicBufferAllocator::free queue a job to another thread to
perform the actual free operation.  This prevents potentially slow free
operations from blocking rendering.

Bug: 7675940
Change-Id: Id61099d66bb4c3949d04184e0d7f192ac18076b4
diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h
index dffa788..479cd3e 100644
--- a/include/ui/GraphicBufferAllocator.h
+++ b/include/ui/GraphicBufferAllocator.h
@@ -84,6 +84,7 @@
     static KeyedVector<buffer_handle_t, alloc_rec_t> sAllocList;
     
     friend class Singleton<GraphicBufferAllocator>;
+    friend class BufferLiberatorThread;
     GraphicBufferAllocator();
     ~GraphicBufferAllocator();
     
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index ff550d9..72acd7d 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -129,21 +129,65 @@
     return err;
 }
 
-status_t GraphicBufferAllocator::free(buffer_handle_t handle)
-{
-    ATRACE_CALL();
-    status_t err;
+class BufferLiberatorThread : public Thread {
+public:
 
-    err = mAllocDev->free(mAllocDev, handle);
-
-    ALOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err));
-    if (err == NO_ERROR) {
-        Mutex::Autolock _l(sLock);
-        KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
-        list.removeItem(handle);
+    static void queueCaptiveBuffer(buffer_handle_t handle) {
+        static sp<BufferLiberatorThread> 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();
+        }
     }
 
-    return err;
+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<buffer_handle_t, GraphicBufferAllocator::alloc_rec_t>&
+                    list(GraphicBufferAllocator::sAllocList);
+            list.removeItem(handle);
+        }
+
+        return true;
+    }
+
+    Vector<buffer_handle_t> mQueue;
+    Condition mCondition;
+    Mutex mMutex;
+};
+
+status_t GraphicBufferAllocator::free(buffer_handle_t handle)
+{
+    BufferLiberatorThread::queueCaptiveBuffer(handle);
+    return NO_ERROR;
 }
 
 // ---------------------------------------------------------------------------