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