From a556d071c1731df6103c8eb86a52503b962c33bc Mon Sep 17 00:00:00 2001 From: ramindani Date: Tue, 14 Jun 2022 23:25:11 +0000 Subject: SF: Fix deadlock while stopping idle timer scope_lock `mRefreshRateConfigsLock` is acquired during `Scheduler::setRefreshRateConfigs` to stop & clear the existing idle timer callback. And in the meantime, when the code in `OneShotTimer::loop` encounters the timeout and then timeoutcallback is requested from OneShotTimer::loop, this callback in `Scheduler::kernelIdleTimerCallback` will try to acquire the lock on `mRefreshRateConfigsLock`. The OneShotTimer::stop which is called to reset the idle timer from the `Scheduler::setRefreshRateConfigs` now join on the OneShotTimer::loop thread that is now blocked waiting on the `mRefreshRateConfigsLock`. So this call never finishes and deadlock happens. When we use the fakeGuard to lock `mRefreshRateConfigsLock` we fake the lock and stop the idle timer. This is okay to do as we only update mRefreshRateConfig on the main thread. Test: atest libsurfaceflinger_unittest go/wm-smoke BUG: 233423207 Change-Id: I146325193b57d81bc3423426355791ca0ec962f5 --- services/surfaceflinger/Scheduler/Scheduler.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 37f0fec425..c9840c7ac8 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -94,9 +95,13 @@ void Scheduler::startTimers() { } void Scheduler::setRefreshRateConfigs(std::shared_ptr configs) { + // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer. { - // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer. - std::scoped_lock lock(mRefreshRateConfigsLock); + // mRefreshRateConfigsLock is not locked here to avoid the deadlock + // as the callback can attempt to acquire the lock before stopIdleTimer can finish + // the execution. It's safe to FakeGuard as main thread is the only thread that + // writes to the mRefreshRateConfigs. + ftl::FakeGuard guard(mRefreshRateConfigsLock); if (mRefreshRateConfigs) { mRefreshRateConfigs->stopIdleTimer(); mRefreshRateConfigs->clearIdleTimerCallbacks(); -- cgit v1.2.3-59-g8ed1b