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

Commit aadcf949 authored by Alec Mouri's avatar Alec Mouri
Browse files

Reduce gratuitous resets for OneShotTimer

OneShotTimer::reset just resets an internal timer thread for it to go
back to sleep. When just implemented with semaphores, this introduces a
lot of syscall churn due to posting a semaphore -> waking up the timer
thread -> having the thread go back to sleep. Instead, we can avoid
resetting the timer if we know that the thread is about to wait for a
short amount of time, and only reset the timer if the thread is in an
idle state.

This patch reduces instruction counts by 5%, and CPU cycles incurred by
an additional 5%.

Bug: 232272570
Test: bouncy ball

Change-Id: I83f968042395857237875aab8dca0e6b90d392cb
parent 8ffb0288
Loading
Loading
Loading
Loading
+23 −8
Original line number Original line Diff line number Diff line
@@ -47,6 +47,7 @@ OneShotTimer::OneShotTimer(std::string name, const Interval& interval,
        mInterval(interval),
        mInterval(interval),
        mResetCallback(resetCallback),
        mResetCallback(resetCallback),
        mTimeoutCallback(timeoutCallback) {
        mTimeoutCallback(timeoutCallback) {
    mLastResetTime = std::chrono::steady_clock::time_point::min();
    LOG_ALWAYS_FATAL_IF(!mClock, "Clock must not be provided");
    LOG_ALWAYS_FATAL_IF(!mClock, "Clock must not be provided");
}
}


@@ -116,9 +117,10 @@ void OneShotTimer::loop() {


        auto triggerTime = mClock->now() + mInterval;
        auto triggerTime = mClock->now() + mInterval;
        state = TimerState::WAITING;
        state = TimerState::WAITING;
        while (state == TimerState::WAITING) {
        while (true) {
            mWaiting = true;
            constexpr auto zero = std::chrono::steady_clock::duration::zero();
            constexpr auto zero = std::chrono::steady_clock::duration::zero();
            // Wait for mInterval time for semaphore signal.
            // Wait for mInterval time to check if we need to reset or drop into the idle state.
            struct timespec ts;
            struct timespec ts;
            calculateTimeoutTime(std::chrono::nanoseconds(mInterval), &ts);
            calculateTimeoutTime(std::chrono::nanoseconds(mInterval), &ts);
            int result = sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &ts);
            int result = sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &ts);
@@ -128,13 +130,21 @@ void OneShotTimer::loop() {
                LOG_ALWAYS_FATAL("%s", ss.str().c_str());
                LOG_ALWAYS_FATAL("%s", ss.str().c_str());
            }
            }


            mWaiting = false;
            state = checkForResetAndStop(state);
            state = checkForResetAndStop(state);
            if (state == TimerState::RESET) {
            if (state == TimerState::STOPPED) {
                triggerTime = mClock->now() + mInterval;
                break;
                state = TimerState::WAITING;
            }
            } else if (state == TimerState::WAITING && (triggerTime - mClock->now()) <= zero) {

            if (state == TimerState::WAITING && (triggerTime - mClock->now()) <= zero) {
                triggerTimeout = true;
                triggerTimeout = true;
                state = TimerState::IDLE;
                state = TimerState::IDLE;
                break;
            }

            if (state == TimerState::RESET) {
                triggerTime = mLastResetTime.load() + mInterval;
                state = TimerState::WAITING;
            }
            }
        }
        }


@@ -158,9 +168,14 @@ OneShotTimer::TimerState OneShotTimer::checkForResetAndStop(TimerState state) {
}
}


void OneShotTimer::reset() {
void OneShotTimer::reset() {
    mLastResetTime = mClock->now();
    mResetTriggered = true;
    mResetTriggered = true;
    int result = sem_post(&mSemaphore);
    // If mWaiting is true, then we are guaranteed to be in a block where we are waiting on
    LOG_ALWAYS_FATAL_IF(result, "sem_post failed");
    // mSemaphore for a timeout, rather than idling. So we can avoid a sem_post call since we can
    // just check that we triggered a reset on timeout.
    if (!mWaiting) {
        LOG_ALWAYS_FATAL_IF(sem_post(&mSemaphore), "sem_post failed");
    }
}
}


std::string OneShotTimer::dump() const {
std::string OneShotTimer::dump() const {
+2 −0
Original line number Original line Diff line number Diff line
@@ -103,6 +103,8 @@ private:
    // check in the main loop if they were.
    // check in the main loop if they were.
    std::atomic<bool> mResetTriggered = false;
    std::atomic<bool> mResetTriggered = false;
    std::atomic<bool> mStopTriggered = false;
    std::atomic<bool> mStopTriggered = false;
    std::atomic<bool> mWaiting = false;
    std::atomic<std::chrono::steady_clock::time_point> mLastResetTime;
};
};


} // namespace scheduler
} // namespace scheduler