BlastBufferQueue: Fix async worker deadlock

The async onBufferReleased callback can trigger another
onBufferReleased which will end up deadlocking the
async worker thread. Fix this by executing the callbacks
outside the lock.

Test: atest android.media.cts.MediaSyncTest#testPlaybackRateDouble --rerun-util-failure 100
Fixes: 201604213
Change-Id: I40d163c3644c6a0128936cf41e8bf8969766d9da
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index d1f57b0..5b9d912 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -738,14 +738,26 @@
         std::unique_lock<std::mutex> lock(mMutex);
         while (!mDone) {
             while (!mRunnables.empty()) {
-                std::function<void()> runnable = mRunnables.front();
-                mRunnables.pop_front();
-                runnable();
+                std::deque<std::function<void()>> runnables = std::move(mRunnables);
+                mRunnables.clear();
+                lock.unlock();
+                // Run outside the lock since the runnable might trigger another
+                // post to the async worker.
+                execute(runnables);
+                lock.lock();
             }
             mCv.wait(lock);
         }
     }
 
+    void execute(std::deque<std::function<void()>>& runnables) {
+        while (!runnables.empty()) {
+            std::function<void()> runnable = runnables.front();
+            runnables.pop_front();
+            runnable();
+        }
+    }
+
 public:
     AsyncWorker() : Singleton<AsyncWorker>() { mThread = std::thread(&AsyncWorker::run, this); }