Rewrite concurrency logic in SurfaceFlinger PowerAdvisor
This patch aims to make concurrency safer by making sure all checks to
"hint session running" use mHintSession directly rather than a proxy
variable, and lock it. This requires all access to
"ensurePowerHintSessionRunning" to be locked, which requires refactoring
some methods, but should make the class much safer. This patch also aims
to ensure the session pointer is being directly checked for validity,
rather than relying on PowerHAL to always pass valid sessions.
Bug: 308030899
Test: atest libsurfaceflinger_unittest:PowerAdvisorTest
Change-Id: I0ebe55ed3c62c57cfd6fb94280beff4b9e8fcc13
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index e005ad3..dd228b4 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -50,7 +50,6 @@
namespace impl {
using aidl::android::hardware::power::Boost;
-using aidl::android::hardware::power::IPowerHintSession;
using aidl::android::hardware::power::Mode;
using aidl::android::hardware::power::SessionHint;
using aidl::android::hardware::power::WorkDuration;
@@ -144,11 +143,13 @@
if (!mBootFinished.load()) {
return;
}
- if (usePowerHintSession() && ensurePowerHintSessionRunning()) {
+ if (usePowerHintSession()) {
std::lock_guard lock(mHintSessionMutex);
- auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_UP);
- if (!ret.isOk()) {
- mHintSessionRunning = false;
+ if (ensurePowerHintSessionRunning()) {
+ auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_UP);
+ if (!ret.isOk()) {
+ mHintSession = nullptr;
+ }
}
}
}
@@ -162,11 +163,13 @@
if (mSendUpdateImminent.exchange(false)) {
ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset");
- if (usePowerHintSession() && ensurePowerHintSessionRunning()) {
+ if (usePowerHintSession()) {
std::lock_guard lock(mHintSessionMutex);
- auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_RESET);
- if (!ret.isOk()) {
- mHintSessionRunning = false;
+ if (ensurePowerHintSessionRunning()) {
+ auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_RESET);
+ if (!ret.isOk()) {
+ mHintSession = nullptr;
+ }
}
}
@@ -193,14 +196,12 @@
}
}
-// checks both if it supports and if it's enabled
bool PowerAdvisor::usePowerHintSession() {
// uses cached value since the underlying support and flag are unlikely to change at runtime
return mHintSessionEnabled.value_or(false) && supportsPowerHintSession();
}
bool PowerAdvisor::supportsPowerHintSession() {
- // cache to avoid needing lock every time
if (!mSupportsHintSession.has_value()) {
mSupportsHintSession = getPowerHal().getHintSessionPreferredRate().isOk();
}
@@ -208,10 +209,15 @@
}
bool PowerAdvisor::ensurePowerHintSessionRunning() {
- if (!mHintSessionRunning && !mHintSessionThreadIds.empty() && usePowerHintSession()) {
- startPowerHintSession(mHintSessionThreadIds);
+ if (mHintSession == nullptr && !mHintSessionThreadIds.empty() && usePowerHintSession()) {
+ auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()),
+ mHintSessionThreadIds, mTargetDuration.ns());
+
+ if (ret.isOk()) {
+ mHintSession = ret.value();
+ }
}
- return mHintSessionRunning;
+ return mHintSession != nullptr;
}
void PowerAdvisor::updateTargetWorkDuration(Duration targetDuration) {
@@ -223,15 +229,16 @@
{
mTargetDuration = targetDuration;
if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration.ns());
- if (ensurePowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) {
+ if (targetDuration == mLastTargetDurationSent) return;
+ std::lock_guard lock(mHintSessionMutex);
+ if (ensurePowerHintSessionRunning()) {
ALOGV("Sending target time: %" PRId64 "ns", targetDuration.ns());
mLastTargetDurationSent = targetDuration;
- std::lock_guard lock(mHintSessionMutex);
auto ret = mHintSession->updateTargetWorkDuration(targetDuration.ns());
if (!ret.isOk()) {
ALOGW("Failed to set power hint target work duration with error: %s",
ret.getDescription().c_str());
- mHintSessionRunning = false;
+ mHintSession = nullptr;
}
}
}
@@ -244,21 +251,12 @@
}
ATRACE_CALL();
std::optional<Duration> actualDuration = estimateWorkDuration();
- if (!actualDuration.has_value() || actualDuration < 0ns || !ensurePowerHintSessionRunning()) {
+ if (!actualDuration.has_value() || actualDuration < 0ns) {
ALOGV("Failed to send actual work duration, skipping");
return;
}
actualDuration = std::make_optional(*actualDuration + sTargetSafetyMargin);
mActualDuration = actualDuration;
- WorkDuration duration;
- duration.workPeriodStartTimestampNanos = mCommitStartTimes[0].ns();
- // TODO(b/284324521): Correctly calculate total duration.
- duration.durationNanos = actualDuration->ns();
- duration.cpuDurationNanos = actualDuration->ns();
- // TODO(b/284324521): Calculate RenderEngine GPU time.
- duration.gpuDurationNanos = 0;
- duration.timeStampNanos = TimePoint::now().ns();
- mHintSessionQueue.push_back(duration);
if (sTraceHintSessionData) {
ATRACE_INT64("Measured duration", actualDuration->ns());
@@ -274,13 +272,34 @@
actualDuration->ns(), mLastTargetDurationSent.ns(),
Duration{*actualDuration - mLastTargetDurationSent}.ns());
+ if (mTimingTestingMode) {
+ mDelayReportActualMutexAcquisitonPromise.get_future().wait();
+ mDelayReportActualMutexAcquisitonPromise = std::promise<bool>{};
+ }
+
{
std::lock_guard lock(mHintSessionMutex);
+ if (!ensurePowerHintSessionRunning()) {
+ ALOGV("Hint session not running and could not be started, skipping");
+ return;
+ }
+
+ WorkDuration duration{
+ .timeStampNanos = TimePoint::now().ns(),
+ // TODO(b/284324521): Correctly calculate total duration.
+ .durationNanos = actualDuration->ns(),
+ .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(),
+ .cpuDurationNanos = actualDuration->ns(),
+ // TODO(b/284324521): Calculate RenderEngine GPU time.
+ .gpuDurationNanos = 0,
+ };
+ mHintSessionQueue.push_back(duration);
+
auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue);
if (!ret.isOk()) {
ALOGW("Failed to report actual work durations with error: %s",
ret.getDescription().c_str());
- mHintSessionRunning = false;
+ mHintSession = nullptr;
return;
}
}
@@ -291,7 +310,8 @@
mHintSessionEnabled = enabled;
}
-bool PowerAdvisor::startPowerHintSession(const std::vector<int32_t>& threadIds) {
+bool PowerAdvisor::startPowerHintSession(std::vector<int32_t>&& threadIds) {
+ mHintSessionThreadIds = threadIds;
if (!mBootFinished.load()) {
return false;
}
@@ -299,25 +319,14 @@
ALOGI("Cannot start power hint session: disabled or unsupported");
return false;
}
- if (mHintSessionRunning) {
+ LOG_ALWAYS_FATAL_IF(mHintSessionThreadIds.empty(),
+ "No thread IDs provided to power hint session!");
+ std::lock_guard lock(mHintSessionMutex);
+ if (mHintSession != nullptr) {
ALOGE("Cannot start power hint session: already running");
return false;
}
- LOG_ALWAYS_FATAL_IF(threadIds.empty(), "No thread IDs provided to power hint session!");
- {
- std::lock_guard lock(mHintSessionMutex);
- mHintSession = nullptr;
- mHintSessionThreadIds = threadIds;
-
- auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()),
- threadIds, mTargetDuration.ns());
-
- if (ret.isOk()) {
- mHintSessionRunning = true;
- mHintSession = ret.value();
- }
- }
- return mHintSessionRunning;
+ return ensurePowerHintSessionRunning();
}
void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) {