Loading services/surfaceflinger/Scheduler/Timer.cpp +23 −7 Original line number Diff line number Diff line Loading @@ -51,13 +51,25 @@ Timer::~Timer() { } void Timer::reset() { std::function<void()> cb; { std::lock_guard lock(mMutex); if (mExpectingCallback && mCallback) { cb = mCallback; } cleanup(); mTimerFd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); mEpollFd = epoll_create1(EPOLL_CLOEXEC); if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) { ALOGE("could not create TimerDispatch mPipes"); return; }; } } if (cb) { setDebugState(DebugState::InCallback); cb(); setDebugState(DebugState::Running); } setDebugState(DebugState::Reset); } Loading @@ -81,6 +93,8 @@ void Timer::cleanup() { close(mPipes[kWritePipe]); mPipes[kWritePipe] = -1; } mExpectingCallback = false; mCallback = {}; } void Timer::endDispatch() { Loading @@ -99,6 +113,7 @@ void Timer::alarmAt(std::function<void()> const& cb, nsecs_t time) { std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); mCallback = cb; mExpectingCallback = true; struct itimerspec old_timer; struct itimerspec new_timer { Loading Loading @@ -198,6 +213,7 @@ bool Timer::dispatch() { { std::lock_guard lock(mMutex); cb = mCallback; mExpectingCallback = false; } if (cb) { setDebugState(DebugState::InCallback); Loading services/surfaceflinger/Scheduler/Timer.h +8 −3 Original line number Diff line number Diff line Loading @@ -36,6 +36,10 @@ public: void alarmCancel() final; void dump(std::string& result) const final; protected: // For unit testing int mEpollFd = -1; private: enum class DebugState { Reset, Loading @@ -48,12 +52,12 @@ private: ftl_last = Terminated }; void reset(); void cleanup(); void reset() EXCLUDES(mMutex); void cleanup() REQUIRES(mMutex); void setDebugState(DebugState state) EXCLUDES(mMutex); int mTimerFd = -1; int mEpollFd = -1; std::array<int, 2> mPipes = {-1, -1}; std::thread mDispatchThread; Loading @@ -63,6 +67,7 @@ private: mutable std::mutex mMutex; std::function<void()> mCallback GUARDED_BY(mMutex); bool mExpectingCallback GUARDED_BY(mMutex) = false; DebugState mDebugState GUARDED_BY(mMutex); }; Loading services/surfaceflinger/tests/unittests/TimerTest.cpp +19 −1 Original line number Diff line number Diff line Loading @@ -26,11 +26,19 @@ using namespace std::literals; namespace android::scheduler { struct TestableTimer : public Timer { public: void makeEpollError() { // close the epoll file descriptor to cause an epoll error close(mEpollFd); } }; struct TimerTest : testing::Test { static constexpr int mIterations = 20; AsyncCallRecorder<void (*)()> mCallbackRecorder; Timer mTimer; TestableTimer mTimer; void timerCallback() { mCallbackRecorder.recordCall(); } }; Loading @@ -42,4 +50,14 @@ TEST_F(TimerTest, callsCallbackIfScheduledInPast) { EXPECT_FALSE(mCallbackRecorder.waitForUnexpectedCall().has_value()); } } TEST_F(TimerTest, recoversAfterEpollError) { for (int i = 0; i < mIterations; i++) { mTimer.makeEpollError(); mTimer.alarmAt(std::bind(&TimerTest::timerCallback, this), systemTime() - 10'000'00); EXPECT_TRUE(mCallbackRecorder.waitForCall().has_value()); EXPECT_FALSE(mCallbackRecorder.waitForUnexpectedCall().has_value()); } } } // namespace android::scheduler Loading
services/surfaceflinger/Scheduler/Timer.cpp +23 −7 Original line number Diff line number Diff line Loading @@ -51,13 +51,25 @@ Timer::~Timer() { } void Timer::reset() { std::function<void()> cb; { std::lock_guard lock(mMutex); if (mExpectingCallback && mCallback) { cb = mCallback; } cleanup(); mTimerFd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); mEpollFd = epoll_create1(EPOLL_CLOEXEC); if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) { ALOGE("could not create TimerDispatch mPipes"); return; }; } } if (cb) { setDebugState(DebugState::InCallback); cb(); setDebugState(DebugState::Running); } setDebugState(DebugState::Reset); } Loading @@ -81,6 +93,8 @@ void Timer::cleanup() { close(mPipes[kWritePipe]); mPipes[kWritePipe] = -1; } mExpectingCallback = false; mCallback = {}; } void Timer::endDispatch() { Loading @@ -99,6 +113,7 @@ void Timer::alarmAt(std::function<void()> const& cb, nsecs_t time) { std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); mCallback = cb; mExpectingCallback = true; struct itimerspec old_timer; struct itimerspec new_timer { Loading Loading @@ -198,6 +213,7 @@ bool Timer::dispatch() { { std::lock_guard lock(mMutex); cb = mCallback; mExpectingCallback = false; } if (cb) { setDebugState(DebugState::InCallback); Loading
services/surfaceflinger/Scheduler/Timer.h +8 −3 Original line number Diff line number Diff line Loading @@ -36,6 +36,10 @@ public: void alarmCancel() final; void dump(std::string& result) const final; protected: // For unit testing int mEpollFd = -1; private: enum class DebugState { Reset, Loading @@ -48,12 +52,12 @@ private: ftl_last = Terminated }; void reset(); void cleanup(); void reset() EXCLUDES(mMutex); void cleanup() REQUIRES(mMutex); void setDebugState(DebugState state) EXCLUDES(mMutex); int mTimerFd = -1; int mEpollFd = -1; std::array<int, 2> mPipes = {-1, -1}; std::thread mDispatchThread; Loading @@ -63,6 +67,7 @@ private: mutable std::mutex mMutex; std::function<void()> mCallback GUARDED_BY(mMutex); bool mExpectingCallback GUARDED_BY(mMutex) = false; DebugState mDebugState GUARDED_BY(mMutex); }; Loading
services/surfaceflinger/tests/unittests/TimerTest.cpp +19 −1 Original line number Diff line number Diff line Loading @@ -26,11 +26,19 @@ using namespace std::literals; namespace android::scheduler { struct TestableTimer : public Timer { public: void makeEpollError() { // close the epoll file descriptor to cause an epoll error close(mEpollFd); } }; struct TimerTest : testing::Test { static constexpr int mIterations = 20; AsyncCallRecorder<void (*)()> mCallbackRecorder; Timer mTimer; TestableTimer mTimer; void timerCallback() { mCallbackRecorder.recordCall(); } }; Loading @@ -42,4 +50,14 @@ TEST_F(TimerTest, callsCallbackIfScheduledInPast) { EXPECT_FALSE(mCallbackRecorder.waitForUnexpectedCall().has_value()); } } TEST_F(TimerTest, recoversAfterEpollError) { for (int i = 0; i < mIterations; i++) { mTimer.makeEpollError(); mTimer.alarmAt(std::bind(&TimerTest::timerCallback, this), systemTime() - 10'000'00); EXPECT_TRUE(mCallbackRecorder.waitForCall().has_value()); EXPECT_FALSE(mCallbackRecorder.waitForUnexpectedCall().has_value()); } } } // namespace android::scheduler