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.h b/services/surfaceflinger/BackgroundExecutor.h
index 6db7dda..eeaf3bd 100644
--- a/services/surfaceflinger/BackgroundExecutor.h
+++ b/services/surfaceflinger/BackgroundExecutor.h
@@ -16,9 +16,11 @@
#pragma once
+#include <Tracing/LocklessStack.h>
#include <android-base/thread_annotations.h>
+#include <ftl/small_vector.h>
+#include <semaphore.h>
#include <utils/Singleton.h>
-#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
@@ -30,13 +32,26 @@
public:
BackgroundExecutor();
~BackgroundExecutor();
- void execute(std::function<void()>);
+ using Callbacks = ftl::SmallVector<std::function<void()>, 10>;
+ // Queues callbacks onto a work queue to be executed by a background thread.
+ // Note that this is not thread-safe - a single producer is assumed.
+ void sendCallbacks(Callbacks&& tasks);
private:
- std::mutex mMutex;
- std::condition_variable mWorkAvailableCv GUARDED_BY(mMutex);
- bool mDone GUARDED_BY(mMutex) = false;
- std::vector<std::function<void()>> mTasks GUARDED_BY(mMutex);
+ sem_t mSemaphore;
+ std::atomic_bool mDone = false;
+
+ // Sequence number for work items.
+ // Work items are batched by sequence number. Work items for earlier sequence numbers are
+ // executed first. Work items with the same sequence number are executed in the same order they
+ // were added to the stack (meaning the stack must reverse the order after popping from the
+ // queue)
+ int32_t mSequence = 0;
+ struct Work {
+ int32_t sequence = 0;
+ Callbacks tasks;
+ };
+ LocklessStack<Work> mWorks;
std::thread mThread;
};