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

Commit 0bc7c529 authored by Kevin DuBois's avatar Kevin DuBois Committed by Android (Google) Code Review
Browse files

Merge "SF: avoid updating queue if schedule() is called." into rvc-dev

parents 810a7553 5c18c1cf
Loading
Loading
Loading
Loading
+27 −2
Original line number Original line Diff line number Diff line
@@ -87,10 +87,26 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsec
    return ScheduleResult::Scheduled;
    return ScheduleResult::Scheduled;
}
}


void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(nsecs_t workDuration,
                                                            nsecs_t earliestVsync) {
    mWorkloadUpdateInfo = {.earliestVsync = earliestVsync, .duration = workDuration};
}

bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const {
    return mWorkloadUpdateInfo.has_value();
}

void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
    if (!mArmedInfo) {
    if (!mArmedInfo && !mWorkloadUpdateInfo) {
        return;
        return;
    }
    }

    if (mWorkloadUpdateInfo) {
        mEarliestVsync = mWorkloadUpdateInfo->earliestVsync;
        mWorkDuration = mWorkloadUpdateInfo->duration;
        mWorkloadUpdateInfo.reset();
    }

    auto const nextVsyncTime =
    auto const nextVsyncTime =
            tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
            tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
    mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
    mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
@@ -192,7 +208,7 @@ void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
    std::optional<std::string_view> nextWakeupName;
    std::optional<std::string_view> nextWakeupName;
    for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
    for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
        auto& callback = it->second;
        auto& callback = it->second;
        if (!callback->wakeupTime()) {
        if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
            continue;
            continue;
        }
        }


@@ -291,6 +307,15 @@ ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t wo
        }
        }
        auto& callback = it->second;
        auto& callback = it->second;
        auto const now = mTimeKeeper->now();
        auto const now = mTimeKeeper->now();

        /* If the timer thread will run soon, we'll apply this work update via the callback
         * timer recalculation to avoid cancelling a callback that is about to fire. */
        auto const rearmImminent = now > mIntendedWakeupTime;
        if (CC_UNLIKELY(rearmImminent)) {
            callback->addPendingWorkloadUpdate(workDuration, earliestVsync);
            return ScheduleResult::Scheduled;
        }

        result = callback->schedule(workDuration, earliestVsync, mTracker, now);
        result = callback->schedule(workDuration, earliestVsync, mTracker, now);
        if (result == ScheduleResult::CannotSchedule) {
        if (result == ScheduleResult::CannotSchedule) {
            return result;
            return result;
+13 −0
Original line number Original line Diff line number Diff line
@@ -64,6 +64,13 @@ public:
    // This moves the state from armed->running.
    // This moves the state from armed->running.
    // Store the timestamp that this was intended for as the last called timestamp.
    // Store the timestamp that this was intended for as the last called timestamp.
    nsecs_t executing();
    nsecs_t executing();

    // Adds a pending upload of the earliestVSync and workDuration that will be applied on the next
    // call to update()
    void addPendingWorkloadUpdate(nsecs_t workDuration, nsecs_t earliestVsync);

    // Checks if there is a pending update to the workload, returning true if so.
    bool hasPendingWorkloadUpdate() const;
    // End: functions that are not threadsafe.
    // End: functions that are not threadsafe.


    // Invoke the callback with the two given timestamps, moving the state from running->disarmed.
    // Invoke the callback with the two given timestamps, moving the state from running->disarmed.
@@ -88,6 +95,12 @@ private:
    std::optional<ArmingInfo> mArmedInfo;
    std::optional<ArmingInfo> mArmedInfo;
    std::optional<nsecs_t> mLastDispatchTime;
    std::optional<nsecs_t> mLastDispatchTime;


    struct WorkloadUpdateInfo {
        nsecs_t duration;
        nsecs_t earliestVsync;
    };
    std::optional<WorkloadUpdateInfo> mWorkloadUpdateInfo;

    mutable std::mutex mRunningMutex;
    mutable std::mutex mRunningMutex;
    std::condition_variable mCv;
    std::condition_variable mCv;
    bool mRunning GUARDED_BY(mRunningMutex) = false;
    bool mRunning GUARDED_BY(mRunningMutex) = false;
+57 −1
Original line number Original line Diff line number Diff line
@@ -89,15 +89,18 @@ public:


    void advanceBy(nsecs_t advancement) {
    void advanceBy(nsecs_t advancement) {
        mCurrentTime += advancement;
        mCurrentTime += advancement;
        if (mCurrentTime >= mNextCallbackTime && mCallback) {
        if (mCurrentTime >= (mNextCallbackTime + mLag) && mCallback) {
            mCallback();
            mCallback();
        }
        }
    };
    };


    void setLag(nsecs_t lag) { mLag = lag; }

private:
private:
    std::function<void()> mCallback;
    std::function<void()> mCallback;
    nsecs_t mNextCallbackTime = 0;
    nsecs_t mNextCallbackTime = 0;
    nsecs_t mCurrentTime = 0;
    nsecs_t mCurrentTime = 0;
    nsecs_t mLag = 0;
};
};


class CountingCallback {
class CountingCallback {
@@ -658,6 +661,46 @@ TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) {
    cb1.cancel();
    cb1.cancel();
}
}


// b/154303580
TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent) {
    Sequence seq;
    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
    EXPECT_CALL(mMockClock, alarmIn(_, 1200)).InSequence(seq);
    CountingCallback cb1(mDispatch);
    CountingCallback cb2(mDispatch);

    EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);

    mMockClock.setLag(100);
    mMockClock.advanceBy(620);

    EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
    mMockClock.advanceBy(80);

    EXPECT_THAT(cb1.mCalls.size(), Eq(1));
    EXPECT_THAT(cb2.mCalls.size(), Eq(0));
}

// b/154303580.
// If the same callback tries to reschedule itself after it's too late, timer opts to apply the
// update later, as opposed to blocking the calling thread.
TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminentSameCallback) {
    Sequence seq;
    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
    EXPECT_CALL(mMockClock, alarmIn(_, 930)).InSequence(seq);
    CountingCallback cb(mDispatch);

    EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);

    mMockClock.setLag(100);
    mMockClock.advanceBy(620);

    EXPECT_EQ(mDispatch.schedule(cb, 370, 2000), ScheduleResult::Scheduled);
    mMockClock.advanceBy(80);

    EXPECT_THAT(cb.mCalls.size(), Eq(1));
}

class VSyncDispatchTimerQueueEntryTest : public testing::Test {
class VSyncDispatchTimerQueueEntryTest : public testing::Test {
protected:
protected:
    nsecs_t const mPeriod = 1000;
    nsecs_t const mPeriod = 1000;
@@ -817,6 +860,19 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
    EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
    EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
}
}


TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) {
    static constexpr auto effectualOffset = 200;
    VSyncDispatchTimerQueueEntry entry(
            "test", [](auto, auto) {}, mVsyncMoveThreshold);
    EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
    entry.addPendingWorkloadUpdate(100, 400);
    entry.addPendingWorkloadUpdate(effectualOffset, 700);
    EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
    entry.update(mStubTracker, 0);
    EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
    EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset));
}

} // namespace android::scheduler
} // namespace android::scheduler


// TODO(b/129481165): remove the #pragma below and fix conversion issues
// TODO(b/129481165): remove the #pragma below and fix conversion issues