Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 547cc0c5 authored by Matt Buckley's avatar Matt Buckley
Browse files

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
parent b928c8de
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -38,11 +38,10 @@ public:
    MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
    MOCK_METHOD(bool, usePowerHintSession, (), (override));
    MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
    MOCK_METHOD(bool, ensurePowerHintSessionRunning, (), (override));
    MOCK_METHOD(void, updateTargetWorkDuration, (Duration targetDuration), (override));
    MOCK_METHOD(void, reportActualWorkDuration, (), (override));
    MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override));
    MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
    MOCK_METHOD(bool, startPowerHintSession, (std::vector<int32_t> && threadIds), (override));
    MOCK_METHOD(void, setGpuFenceTime,
                (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
    MOCK_METHOD(void, setHwcValidateTiming,
+54 −45
Original line number Diff line number Diff line
@@ -50,7 +50,6 @@ PowerAdvisor::~PowerAdvisor() = default;
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 @@ void PowerAdvisor::notifyCpuLoadUp() {
    if (!mBootFinished.load()) {
        return;
    }
    if (usePowerHintSession() && ensurePowerHintSessionRunning()) {
    if (usePowerHintSession()) {
        std::lock_guard lock(mHintSessionMutex);
        if (ensurePowerHintSessionRunning()) {
            auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_UP);
            if (!ret.isOk()) {
            mHintSessionRunning = false;
                mHintSession = nullptr;
            }
        }
    }
}
@@ -162,11 +163,13 @@ void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {

    if (mSendUpdateImminent.exchange(false)) {
        ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset");
        if (usePowerHintSession() && ensurePowerHintSessionRunning()) {
        if (usePowerHintSession()) {
            std::lock_guard lock(mHintSessionMutex);
            if (ensurePowerHintSessionRunning()) {
                auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_RESET);
                if (!ret.isOk()) {
                mHintSessionRunning = false;
                    mHintSession = nullptr;
                }
            }
        }

@@ -193,14 +196,12 @@ void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {
    }
}

// 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::supportsPowerHintSession() {
}

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 @@ void PowerAdvisor::updateTargetWorkDuration(Duration targetDuration) {
    {
        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 @@ void PowerAdvisor::reportActualWorkDuration() {
    }
    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 @@ void PowerAdvisor::reportActualWorkDuration() {
          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 @@ void PowerAdvisor::enablePowerHintSession(bool enabled) {
    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 @@ bool PowerAdvisor::startPowerHintSession(const std::vector<int32_t>& threadIds)
        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) {
+24 −12
Original line number Diff line number Diff line
@@ -46,16 +46,15 @@ public:

    // Initializes resources that cannot be initialized on construction
    virtual void init() = 0;
    // Used to indicate that power hints can now be reported
    virtual void onBootFinished() = 0;
    virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
    virtual bool isUsingExpensiveRendering() = 0;
    virtual void notifyCpuLoadUp() = 0;
    virtual void notifyDisplayUpdateImminentAndCpuReset() = 0;
    // Checks both if it supports and if it's enabled
    // Checks both if it's supported and if it's enabled; this is thread-safe since its values are
    // set before onBootFinished, which gates all methods that run on threads other than SF main
    virtual bool usePowerHintSession() = 0;
    virtual bool supportsPowerHintSession() = 0;

    virtual bool ensurePowerHintSessionRunning() = 0;
    // Sends a power hint that updates to the target work duration for the frame
    virtual void updateTargetWorkDuration(Duration targetDuration) = 0;
    // Sends a power hint for the actual known work duration at the end of the frame
@@ -63,7 +62,7 @@ public:
    // Sets whether the power hint session is enabled
    virtual void enablePowerHintSession(bool enabled) = 0;
    // Initializes the power hint session
    virtual bool startPowerHintSession(const std::vector<int32_t>& threadIds) = 0;
    virtual bool startPowerHintSession(std::vector<int32_t>&& threadIds) = 0;
    // Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time
    virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0;
    // Reports the start and end times of a hwc validate call this frame for a given display
@@ -94,6 +93,12 @@ public:
    virtual void setDisplays(std::vector<DisplayId>& displayIds) = 0;
    // Sets the target duration for the entire pipeline including the gpu
    virtual void setTotalFrameTargetWorkDuration(Duration targetDuration) = 0;

    // --- The following methods may run on threads besides SF main ---
    // Send a hint about an upcoming increase in the CPU workload
    virtual void notifyCpuLoadUp() = 0;
    // Send a hint about the imminent start of a new CPU workload
    virtual void notifyDisplayUpdateImminentAndCpuReset() = 0;
};

namespace impl {
@@ -109,16 +114,13 @@ public:
    void onBootFinished() override;
    void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
    bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; };
    void notifyCpuLoadUp() override;
    void notifyDisplayUpdateImminentAndCpuReset() override;
    bool usePowerHintSession() override;
    bool supportsPowerHintSession() override;
    bool ensurePowerHintSessionRunning() override;
    void updateTargetWorkDuration(Duration targetDuration) override;
    void reportActualWorkDuration() override;
    void enablePowerHintSession(bool enabled) override;
    bool startPowerHintSession(const std::vector<int32_t>& threadIds) override;
    void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime);
    bool startPowerHintSession(std::vector<int32_t>&& threadIds) override;
    void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) override;
    void setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime,
                              TimePoint validateEndTime) override;
    void setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime,
@@ -128,13 +130,16 @@ public:
    void setExpectedPresentTime(TimePoint expectedPresentTime) override;
    void setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) override;
    void setHwcPresentDelayedTime(DisplayId displayId, TimePoint earliestFrameStartTime) override;

    void setFrameDelay(Duration frameDelayDuration) override;
    void setCommitStart(TimePoint commitStartTime) override;
    void setCompositeEnd(TimePoint compositeEndTime) override;
    void setDisplays(std::vector<DisplayId>& displayIds) override;
    void setTotalFrameTargetWorkDuration(Duration targetDuration) override;

    // --- The following methods may run on threads besides SF main ---
    void notifyCpuLoadUp() override;
    void notifyDisplayUpdateImminentAndCpuReset() override;

private:
    friend class PowerAdvisorTest;

@@ -220,6 +225,7 @@ private:
    // this normalizes them together and takes the max of the two
    Duration combineTimingEstimates(Duration totalDuration, Duration flingerDuration);

    bool ensurePowerHintSessionRunning() REQUIRES(mHintSessionMutex);
    std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData;

    // Current frame's delay
@@ -242,9 +248,10 @@ private:
    // Ensure powerhal connection is initialized
    power::PowerHalController& getPowerHal();

    // These variables are set before mBootFinished and never mutated after, so it's safe to access
    // from threaded methods.
    std::optional<bool> mHintSessionEnabled;
    std::optional<bool> mSupportsHintSession;
    bool mHintSessionRunning = false;

    std::mutex mHintSessionMutex;
    std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mHintSession
@@ -261,6 +268,11 @@ private:
    // The list of thread ids, stored so we can restart the session from this class if needed
    std::vector<int32_t> mHintSessionThreadIds;
    Duration mLastTargetDurationSent = kDefaultTargetDuration;

    // Used to manage the execution ordering of reportActualWorkDuration for concurrency testing
    std::promise<bool> mDelayReportActualMutexAcquisitonPromise;
    bool mTimingTestingMode = false;

    // Whether we should emit ATRACE_INT data for hint sessions
    static const bool sTraceHintSessionData;

+4 −2
Original line number Diff line number Diff line
@@ -721,10 +721,12 @@ void SurfaceFlinger::bootFinished() {
        }

        readPersistentProperties();
        mPowerAdvisor->onBootFinished();
        const bool hintSessionEnabled = FlagManager::getInstance().use_adpf_cpu_hint();
        mPowerAdvisor->enablePowerHintSession(hintSessionEnabled);
        const bool hintSessionUsed = mPowerAdvisor->usePowerHintSession();
        // Ordering is important here, as onBootFinished signals to PowerAdvisor that concurrency
        // is safe because its variables are initialized.
        mPowerAdvisor->onBootFinished();
        ALOGD("Power hint is %s",
              hintSessionUsed ? "supported" : (hintSessionEnabled ? "unsupported" : "disabled"));
        if (hintSessionUsed) {
@@ -734,7 +736,7 @@ void SurfaceFlinger::bootFinished() {
            if (renderEngineTid.has_value()) {
                tidList.emplace_back(*renderEngineTid);
            }
            if (!mPowerAdvisor->startPowerHintSession(tidList)) {
            if (!mPowerAdvisor->startPowerHintSession(std::move(tidList))) {
                ALOGW("Cannot start power hint session");
            }
        }
+156 −7
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <powermanager/PowerHalWrapper.h>
#include <ui/DisplayId.h>
#include <chrono>
#include <future>
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockIPowerHintSession.h"
#include "mock/DisplayHardware/MockPowerHalController.h"
@@ -40,11 +41,14 @@ namespace android::Hwc2::impl {
class PowerAdvisorTest : public testing::Test {
public:
    void SetUp() override;
    void startPowerHintSession();
    void startPowerHintSession(bool returnValidSession = true);
    void fakeBasicFrameTiming(TimePoint startTime, Duration vsyncPeriod);
    void setExpectedTiming(Duration totalFrameTargetDuration, TimePoint expectedPresentTime);
    Duration getFenceWaitDelayDuration(bool skipValidate);
    Duration getErrorMargin();
    void setTimingTestingMode(bool testinMode);
    void allowReportActualToAcquireMutex();
    bool sessionExists();

protected:
    TestableSurfaceFlinger mFlinger;
@@ -53,6 +57,11 @@ protected:
    std::shared_ptr<MockIPowerHintSession> mMockPowerHintSession;
};

bool PowerAdvisorTest::sessionExists() {
    std::scoped_lock lock(mPowerAdvisor->mHintSessionMutex);
    return mPowerAdvisor->mHintSession != nullptr;
}

void PowerAdvisorTest::SetUp() {
    mPowerAdvisor = std::make_unique<impl::PowerAdvisor>(*mFlinger.flinger());
    mPowerAdvisor->mPowerHal = std::make_unique<NiceMock<MockPowerHalController>>();
@@ -62,14 +71,20 @@ void PowerAdvisorTest::SetUp() {
            .WillByDefault(Return(HalResult<int64_t>::fromStatus(binder::Status::ok(), 16000)));
}

void PowerAdvisorTest::startPowerHintSession() {
    const std::vector<int32_t> threadIds = {1, 2, 3};
void PowerAdvisorTest::startPowerHintSession(bool returnValidSession) {
    mMockPowerHintSession = ndk::SharedRefBase::make<NiceMock<MockIPowerHintSession>>();
    if (returnValidSession) {
        ON_CALL(*mMockPowerHalController, createHintSession)
            .WillByDefault(Return(HalResult<std::shared_ptr<IPowerHintSession>>::
                .WillByDefault(
                        Return(HalResult<std::shared_ptr<IPowerHintSession>>::
                                       fromStatus(binder::Status::ok(), mMockPowerHintSession)));
    } else {
        ON_CALL(*mMockPowerHalController, createHintSession)
                .WillByDefault(Return(HalResult<std::shared_ptr<IPowerHintSession>>::
                                              fromStatus(binder::Status::ok(), nullptr)));
    }
    mPowerAdvisor->enablePowerHintSession(true);
    mPowerAdvisor->startPowerHintSession(threadIds);
    mPowerAdvisor->startPowerHintSession({1, 2, 3});
    ON_CALL(*mMockPowerHintSession, updateTargetWorkDuration)
            .WillByDefault(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
}
@@ -86,6 +101,14 @@ void PowerAdvisorTest::fakeBasicFrameTiming(TimePoint startTime, Duration vsyncP
    mPowerAdvisor->updateTargetWorkDuration(vsyncPeriod);
}

void PowerAdvisorTest::setTimingTestingMode(bool testingMode) {
    mPowerAdvisor->mTimingTestingMode = testingMode;
}

void PowerAdvisorTest::allowReportActualToAcquireMutex() {
    mPowerAdvisor->mDelayReportActualMutexAcquisitonPromise.set_value(true);
}

Duration PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) {
    return (skipValidate ? PowerAdvisor::kFenceWaitStartDelaySkippedValidate
                         : PowerAdvisor::kFenceWaitStartDelayValidated);
@@ -221,5 +244,131 @@ TEST_F(PowerAdvisorTest, hintSessionUsingSecondaryVirtualDisplays) {
    mPowerAdvisor->reportActualWorkDuration();
}

TEST_F(PowerAdvisorTest, hintSessionValidWhenNullFromPowerHAL) {
    mPowerAdvisor->onBootFinished();

    startPowerHintSession(false);

    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};

    // 60hz
    const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60};
    const Duration presentDuration = 5ms;
    const Duration postCompDuration = 1ms;

    TimePoint startTime{100ns};

    // advisor only starts on frame 2 so do an initial no-op frame
    fakeBasicFrameTiming(startTime, vsyncPeriod);
    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
    mPowerAdvisor->setDisplays(displayIds);
    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);

    // increment the frame
    startTime += vsyncPeriod;

    const Duration expectedDuration = getErrorMargin() + presentDuration + postCompDuration;
    EXPECT_CALL(*mMockPowerHintSession,
                reportActualWorkDuration(ElementsAre(
                        Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
            .Times(0);
    fakeBasicFrameTiming(startTime, vsyncPeriod);
    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
    mPowerAdvisor->setDisplays(displayIds);
    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us);
    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 2500us);
    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
    mPowerAdvisor->reportActualWorkDuration();
}

TEST_F(PowerAdvisorTest, hintSessionOnlyCreatedOnce) {
    EXPECT_CALL(*mMockPowerHalController, createHintSession(_, _, _, _)).Times(1);
    mPowerAdvisor->onBootFinished();
    startPowerHintSession();
    mPowerAdvisor->startPowerHintSession({1, 2, 3});
}

TEST_F(PowerAdvisorTest, hintSessionTestNotifyReportRace) {
    // notifyDisplayUpdateImminentAndCpuReset or notifyCpuLoadUp gets called in background
    // reportActual gets called during callback and sees true session, passes ensure
    // first notify finishes, setting value to true. Another async method gets called, acquires the
    // lock between reportactual finishing ensure and acquiring the lock itself, and sets session to
    // nullptr. reportActual acquires the lock, and the session is now null, so it does nullptr
    // deref

    mPowerAdvisor->onBootFinished();
    startPowerHintSession();

    // --- fake a bunch of timing data
    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
    // 60hz
    const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60};
    const Duration presentDuration = 5ms;
    const Duration postCompDuration = 1ms;
    TimePoint startTime{100ns};
    // advisor only starts on frame 2 so do an initial no-op frame
    fakeBasicFrameTiming(startTime, vsyncPeriod);
    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
    mPowerAdvisor->setDisplays(displayIds);
    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
    // increment the frame
    startTime += vsyncPeriod;
    fakeBasicFrameTiming(startTime, vsyncPeriod);
    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
    mPowerAdvisor->setDisplays(displayIds);
    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us);
    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 2500us);
    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
    // --- Done faking timing data

    setTimingTestingMode(true);
    std::promise<bool> letSendHintFinish;

    ON_CALL(*mMockPowerHintSession, sendHint).WillByDefault([&letSendHintFinish] {
        letSendHintFinish.get_future().wait();
        return ndk::ScopedAStatus::fromExceptionCode(-127);
    });

    ON_CALL(*mMockPowerHintSession, reportActualWorkDuration).WillByDefault([] {
        return ndk::ScopedAStatus::fromExceptionCode(-127);
    });

    ON_CALL(*mMockPowerHalController, createHintSession)
            .WillByDefault(Return(
                    HalResult<std::shared_ptr<IPowerHintSession>>::
                            fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127), nullptr)));

    // First background call, to notice the session is down
    auto firstHint = std::async(std::launch::async, [this] {
        mPowerAdvisor->notifyCpuLoadUp();
        return true;
    });
    std::this_thread::sleep_for(10ms);

    // Call reportActual while callback is resolving to try and sneak past ensure
    auto reportActual =
            std::async(std::launch::async, [this] { mPowerAdvisor->reportActualWorkDuration(); });

    std::this_thread::sleep_for(10ms);
    // Let the first call finish
    letSendHintFinish.set_value(true);
    letSendHintFinish = std::promise<bool>{};
    firstHint.wait();

    // Do the second notify call, to ensure the session is nullptr
    auto secondHint = std::async(std::launch::async, [this] {
        mPowerAdvisor->notifyCpuLoadUp();
        return true;
    });
    letSendHintFinish.set_value(true);
    secondHint.wait();
    // Let report finish, potentially dereferencing
    allowReportActualToAcquireMutex();
    reportActual.wait();
    EXPECT_EQ(sessionExists(), false);
}

} // namespace
} // namespace android::Hwc2::impl
Loading