Use semaphore instead of condition variable

BackgroundExecutor uses a condition variable to synchronize processing
to a work queue, which means that there is possible mutex contention on
the main thread in rare scenarios.

Instead, we can make the main thread non-blocking with a thread-safe
queue, and use semaphores for signaling the background thread to wake
up. Using semaphores saves a small number of instructions, but overall
reduces contention

Bug: 232113929
Test: bouncy ball

Change-Id: Iced25e8349bdb2a70cac1ed681059a0b14258407
diff --git a/services/surfaceflinger/BackgroundExecutor.cpp b/services/surfaceflinger/BackgroundExecutor.cpp
index de8e6b3..a15de2b 100644
--- a/services/surfaceflinger/BackgroundExecutor.cpp
+++ b/services/surfaceflinger/BackgroundExecutor.cpp
@@ -19,6 +19,8 @@
 #define LOG_TAG "BackgroundExecutor"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <utils/Log.h>
+
 #include "BackgroundExecutor.h"
 
 namespace android {
@@ -27,41 +29,49 @@
 
 BackgroundExecutor::BackgroundExecutor() : Singleton<BackgroundExecutor>() {
     mThread = std::thread([&]() {
-        bool done = false;
-        while (!done) {
-            std::vector<std::function<void()>> tasks;
-            {
-                std::unique_lock lock(mMutex);
-                android::base::ScopedLockAssertion assumeLock(mMutex);
-                mWorkAvailableCv.wait(lock,
-                                      [&]() REQUIRES(mMutex) { return mDone || !mTasks.empty(); });
-                tasks = std::move(mTasks);
-                mTasks.clear();
-                done = mDone;
-            } // unlock mMutex
+        LOG_ALWAYS_FATAL_IF(sem_init(&mSemaphore, 0, 0), "sem_init failed");
+        while (!mDone) {
+            LOG_ALWAYS_FATAL_IF(sem_wait(&mSemaphore), "sem_wait failed (%d)", errno);
 
-            for (auto& task : tasks) {
-                task();
+            ftl::SmallVector<Work*, 10> workItems;
+
+            Work* work = mWorks.pop();
+            while (work) {
+                workItems.push_back(work);
+                work = mWorks.pop();
+            }
+
+            // Sequence numbers are guaranteed to be in intended order, as we assume a single
+            // producer and single consumer.
+            std::stable_sort(workItems.begin(), workItems.end(), [](Work* left, Work* right) {
+                return left->sequence < right->sequence;
+            });
+            for (Work* work : workItems) {
+                for (auto& task : work->tasks) {
+                    task();
+                }
+                delete work;
             }
         }
     });
 }
 
 BackgroundExecutor::~BackgroundExecutor() {
-    {
-        std::scoped_lock lock(mMutex);
-        mDone = true;
-        mWorkAvailableCv.notify_all();
-    }
+    mDone = true;
+    LOG_ALWAYS_FATAL_IF(sem_post(&mSemaphore), "sem_post failed");
     if (mThread.joinable()) {
         mThread.join();
+        LOG_ALWAYS_FATAL_IF(sem_destroy(&mSemaphore), "sem_destroy failed");
     }
 }
 
-void BackgroundExecutor::execute(std::function<void()> task) {
-    std::scoped_lock lock(mMutex);
-    mTasks.emplace_back(std::move(task));
-    mWorkAvailableCv.notify_all();
+void BackgroundExecutor::sendCallbacks(Callbacks&& tasks) {
+    Work* work = new Work();
+    work->sequence = mSequence;
+    work->tasks = std::move(tasks);
+    mWorks.push(work);
+    mSequence++;
+    LOG_ALWAYS_FATAL_IF(sem_post(&mSemaphore), "sem_post failed");
 }
 
 } // namespace android
\ No newline at end of file