Loading services/surfaceflinger/Scheduler/OneShotTimer.cpp +74 −45 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,21 @@ #include <sstream> #include <sstream> #include <thread> #include <thread> namespace { using namespace std::chrono_literals; constexpr int64_t kNsToSeconds = std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); // The syscall interface uses a pair of integers for the timestamp. The first // (tv_sec) is the whole count of seconds. The second (tv_nsec) is the // nanosecond part of the count. This function takes care of translation. void calculateTimeoutTime(std::chrono::nanoseconds timestamp, timespec* spec) { clock_gettime(CLOCK_MONOTONIC, spec); spec->tv_sec += static_cast<__kernel_time_t>(timestamp.count() / kNsToSeconds); spec->tv_nsec += timestamp.count() % kNsToSeconds; } } // namespace namespace android { namespace android { namespace scheduler { namespace scheduler { Loading @@ -32,81 +47,95 @@ OneShotTimer::~OneShotTimer() { } } void OneShotTimer::start() { void OneShotTimer::start() { { sem_init(&mSemaphore, 0, 0); std::lock_guard<std::mutex> lock(mMutex); mState = TimerState::RESET; if (!mThread.joinable()) { } // Only create thread if it has not been created. mThread = std::thread(&OneShotTimer::loop, this); mThread = std::thread(&OneShotTimer::loop, this); } } } void OneShotTimer::stop() { void OneShotTimer::stop() { { mStopTriggered = true; std::lock_guard<std::mutex> lock(mMutex); sem_post(&mSemaphore); mState = TimerState::STOPPED; } mCondition.notify_all(); if (mThread.joinable()) { if (mThread.joinable()) { mThread.join(); mThread.join(); sem_destroy(&mSemaphore); } } } } void OneShotTimer::loop() { void OneShotTimer::loop() { TimerState state = TimerState::RESET; while (true) { while (true) { bool triggerReset = false; bool triggerReset = false; bool triggerTimeout = false; bool triggerTimeout = false; { std::lock_guard<std::mutex> lock(mMutex); state = checkForResetAndStop(state); if (mState == TimerState::STOPPED) { if (state == TimerState::STOPPED) { break; break; } } if (mState == TimerState::IDLE) { if (state == TimerState::IDLE) { mCondition.wait(mMutex); sem_wait(&mSemaphore); continue; continue; } } if (mState == TimerState::RESET) { if (state == TimerState::RESET) { triggerReset = true; triggerReset = true; } } } if (triggerReset && mResetCallback) { if (triggerReset && mResetCallback) { mResetCallback(); mResetCallback(); } } { // lock the mutex again. someone might have called stop meanwhile state = checkForResetAndStop(state); std::lock_guard<std::mutex> lock(mMutex); if (state == TimerState::STOPPED) { if (mState == TimerState::STOPPED) { break; break; } } auto triggerTime = std::chrono::steady_clock::now() + mInterval; auto triggerTime = std::chrono::steady_clock::now() + mInterval; mState = TimerState::WAITING; state = TimerState::WAITING; while (mState == TimerState::WAITING) { while (state == TimerState::WAITING) { constexpr auto zero = std::chrono::steady_clock::duration::zero(); constexpr auto zero = std::chrono::steady_clock::duration::zero(); auto waitTime = triggerTime - std::chrono::steady_clock::now(); // Wait for mInterval time for semaphore signal. if (waitTime > zero) mCondition.wait_for(mMutex, waitTime); struct timespec ts; if (mState == TimerState::RESET) { calculateTimeoutTime(std::chrono::nanoseconds(mInterval), &ts); sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &ts); state = checkForResetAndStop(state); if (state == TimerState::RESET) { triggerTime = std::chrono::steady_clock::now() + mInterval; triggerTime = std::chrono::steady_clock::now() + mInterval; mState = TimerState::WAITING; state = TimerState::WAITING; } else if (mState == TimerState::WAITING && } else if (state == TimerState::WAITING && (triggerTime - std::chrono::steady_clock::now()) <= zero) { (triggerTime - std::chrono::steady_clock::now()) <= zero) { triggerTimeout = true; triggerTimeout = true; mState = TimerState::IDLE; state = TimerState::IDLE; } } } } } if (triggerTimeout && mTimeoutCallback) { if (triggerTimeout && mTimeoutCallback) { mTimeoutCallback(); mTimeoutCallback(); } } } } } } void OneShotTimer::reset() { OneShotTimer::TimerState OneShotTimer::checkForResetAndStop(TimerState state) { { // Stop takes precedence of the reset. std::lock_guard<std::mutex> lock(mMutex); if (mStopTriggered.exchange(false)) { mState = TimerState::RESET; return TimerState::STOPPED; } } mCondition.notify_all(); // If the state was stopped, the thread was joined, and we cannot reset // the timer anymore. if (state != TimerState::STOPPED && mResetTriggered.exchange(false)) { return TimerState::RESET; } return state; } void OneShotTimer::reset() { mResetTriggered = true; sem_post(&mSemaphore); } } std::string OneShotTimer::dump() const { std::string OneShotTimer::dump() const { Loading services/surfaceflinger/Scheduler/OneShotTimer.h +13 −8 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ #pragma once #pragma once #include <semaphore.h> #include <chrono> #include <chrono> #include <condition_variable> #include <condition_variable> #include <thread> #include <thread> Loading Loading @@ -70,17 +71,15 @@ private: // Function that loops until the condition for stopping is met. // Function that loops until the condition for stopping is met. void loop(); void loop(); // Checks whether mResetTriggered and mStopTriggered were set and updates // mState if so. TimerState checkForResetAndStop(TimerState state); // Thread waiting for timer to expire. // Thread waiting for timer to expire. std::thread mThread; std::thread mThread; // Condition used to notify mThread. // Semaphore to keep mThread synchronized. std::condition_variable_any mCondition; sem_t mSemaphore; // Lock used for synchronizing the waiting thread with the application thread. std::mutex mMutex; // Current timer state TimerState mState GUARDED_BY(mMutex) = TimerState::RESET; // Interval after which timer expires. // Interval after which timer expires. const Interval mInterval; const Interval mInterval; Loading @@ -90,6 +89,12 @@ private: // Callback that happens when timer expires. // Callback that happens when timer expires. const TimeoutCallback mTimeoutCallback; const TimeoutCallback mTimeoutCallback; // After removing lock guarding mState, the state can be now accessed at // any time. Keep a bool if the reset or stop were requested, and occasionally // check in the main loop if they were. std::atomic<bool> mResetTriggered = false; std::atomic<bool> mStopTriggered = false; }; }; } // namespace scheduler } // namespace scheduler Loading Loading
services/surfaceflinger/Scheduler/OneShotTimer.cpp +74 −45 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,21 @@ #include <sstream> #include <sstream> #include <thread> #include <thread> namespace { using namespace std::chrono_literals; constexpr int64_t kNsToSeconds = std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); // The syscall interface uses a pair of integers for the timestamp. The first // (tv_sec) is the whole count of seconds. The second (tv_nsec) is the // nanosecond part of the count. This function takes care of translation. void calculateTimeoutTime(std::chrono::nanoseconds timestamp, timespec* spec) { clock_gettime(CLOCK_MONOTONIC, spec); spec->tv_sec += static_cast<__kernel_time_t>(timestamp.count() / kNsToSeconds); spec->tv_nsec += timestamp.count() % kNsToSeconds; } } // namespace namespace android { namespace android { namespace scheduler { namespace scheduler { Loading @@ -32,81 +47,95 @@ OneShotTimer::~OneShotTimer() { } } void OneShotTimer::start() { void OneShotTimer::start() { { sem_init(&mSemaphore, 0, 0); std::lock_guard<std::mutex> lock(mMutex); mState = TimerState::RESET; if (!mThread.joinable()) { } // Only create thread if it has not been created. mThread = std::thread(&OneShotTimer::loop, this); mThread = std::thread(&OneShotTimer::loop, this); } } } void OneShotTimer::stop() { void OneShotTimer::stop() { { mStopTriggered = true; std::lock_guard<std::mutex> lock(mMutex); sem_post(&mSemaphore); mState = TimerState::STOPPED; } mCondition.notify_all(); if (mThread.joinable()) { if (mThread.joinable()) { mThread.join(); mThread.join(); sem_destroy(&mSemaphore); } } } } void OneShotTimer::loop() { void OneShotTimer::loop() { TimerState state = TimerState::RESET; while (true) { while (true) { bool triggerReset = false; bool triggerReset = false; bool triggerTimeout = false; bool triggerTimeout = false; { std::lock_guard<std::mutex> lock(mMutex); state = checkForResetAndStop(state); if (mState == TimerState::STOPPED) { if (state == TimerState::STOPPED) { break; break; } } if (mState == TimerState::IDLE) { if (state == TimerState::IDLE) { mCondition.wait(mMutex); sem_wait(&mSemaphore); continue; continue; } } if (mState == TimerState::RESET) { if (state == TimerState::RESET) { triggerReset = true; triggerReset = true; } } } if (triggerReset && mResetCallback) { if (triggerReset && mResetCallback) { mResetCallback(); mResetCallback(); } } { // lock the mutex again. someone might have called stop meanwhile state = checkForResetAndStop(state); std::lock_guard<std::mutex> lock(mMutex); if (state == TimerState::STOPPED) { if (mState == TimerState::STOPPED) { break; break; } } auto triggerTime = std::chrono::steady_clock::now() + mInterval; auto triggerTime = std::chrono::steady_clock::now() + mInterval; mState = TimerState::WAITING; state = TimerState::WAITING; while (mState == TimerState::WAITING) { while (state == TimerState::WAITING) { constexpr auto zero = std::chrono::steady_clock::duration::zero(); constexpr auto zero = std::chrono::steady_clock::duration::zero(); auto waitTime = triggerTime - std::chrono::steady_clock::now(); // Wait for mInterval time for semaphore signal. if (waitTime > zero) mCondition.wait_for(mMutex, waitTime); struct timespec ts; if (mState == TimerState::RESET) { calculateTimeoutTime(std::chrono::nanoseconds(mInterval), &ts); sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &ts); state = checkForResetAndStop(state); if (state == TimerState::RESET) { triggerTime = std::chrono::steady_clock::now() + mInterval; triggerTime = std::chrono::steady_clock::now() + mInterval; mState = TimerState::WAITING; state = TimerState::WAITING; } else if (mState == TimerState::WAITING && } else if (state == TimerState::WAITING && (triggerTime - std::chrono::steady_clock::now()) <= zero) { (triggerTime - std::chrono::steady_clock::now()) <= zero) { triggerTimeout = true; triggerTimeout = true; mState = TimerState::IDLE; state = TimerState::IDLE; } } } } } if (triggerTimeout && mTimeoutCallback) { if (triggerTimeout && mTimeoutCallback) { mTimeoutCallback(); mTimeoutCallback(); } } } } } } void OneShotTimer::reset() { OneShotTimer::TimerState OneShotTimer::checkForResetAndStop(TimerState state) { { // Stop takes precedence of the reset. std::lock_guard<std::mutex> lock(mMutex); if (mStopTriggered.exchange(false)) { mState = TimerState::RESET; return TimerState::STOPPED; } } mCondition.notify_all(); // If the state was stopped, the thread was joined, and we cannot reset // the timer anymore. if (state != TimerState::STOPPED && mResetTriggered.exchange(false)) { return TimerState::RESET; } return state; } void OneShotTimer::reset() { mResetTriggered = true; sem_post(&mSemaphore); } } std::string OneShotTimer::dump() const { std::string OneShotTimer::dump() const { Loading
services/surfaceflinger/Scheduler/OneShotTimer.h +13 −8 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ #pragma once #pragma once #include <semaphore.h> #include <chrono> #include <chrono> #include <condition_variable> #include <condition_variable> #include <thread> #include <thread> Loading Loading @@ -70,17 +71,15 @@ private: // Function that loops until the condition for stopping is met. // Function that loops until the condition for stopping is met. void loop(); void loop(); // Checks whether mResetTriggered and mStopTriggered were set and updates // mState if so. TimerState checkForResetAndStop(TimerState state); // Thread waiting for timer to expire. // Thread waiting for timer to expire. std::thread mThread; std::thread mThread; // Condition used to notify mThread. // Semaphore to keep mThread synchronized. std::condition_variable_any mCondition; sem_t mSemaphore; // Lock used for synchronizing the waiting thread with the application thread. std::mutex mMutex; // Current timer state TimerState mState GUARDED_BY(mMutex) = TimerState::RESET; // Interval after which timer expires. // Interval after which timer expires. const Interval mInterval; const Interval mInterval; Loading @@ -90,6 +89,12 @@ private: // Callback that happens when timer expires. // Callback that happens when timer expires. const TimeoutCallback mTimeoutCallback; const TimeoutCallback mTimeoutCallback; // After removing lock guarding mState, the state can be now accessed at // any time. Keep a bool if the reset or stop were requested, and occasionally // check in the main loop if they were. std::atomic<bool> mResetTriggered = false; std::atomic<bool> mStopTriggered = false; }; }; } // namespace scheduler } // namespace scheduler Loading