From 73b2b389543e7b9e4e5f52d1b9f8b89b3fa653bf Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Tue, 7 Jun 2022 00:22:22 +0200 Subject: [PATCH] AutoSuspend: Make sure we send a LeaveStandby event ASAP (#9173) Even in corner cases where we're woken up without user input (e.g., rtc alarm). (Followup to #9124) --- .../reader/modules/readercoptlistener.lua | 3 --- frontend/apps/reader/modules/readerfooter.lua | 9 ++++--- frontend/ui/uimanager.lua | 12 +++++++++ plugins/autosuspend.koplugin/main.lua | 25 +++++++++++-------- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/frontend/apps/reader/modules/readercoptlistener.lua b/frontend/apps/reader/modules/readercoptlistener.lua index 2d2103572..1213ee673 100644 --- a/frontend/apps/reader/modules/readercoptlistener.lua +++ b/frontend/apps/reader/modules/readercoptlistener.lua @@ -145,12 +145,10 @@ function ReaderCoptListener:onResume() end self:headerRefresh() - self:rescheduleHeaderRefreshIfNeeded() end function ReaderCoptListener:onLeaveStandby() self:headerRefresh() - self:rescheduleHeaderRefreshIfNeeded() end function ReaderCoptListener:onOutOfScreenSaver() @@ -160,7 +158,6 @@ function ReaderCoptListener:onOutOfScreenSaver() self._delayed_screensaver = nil self:headerRefresh() - self:rescheduleHeaderRefreshIfNeeded() end -- Unschedule on these events diff --git a/frontend/apps/reader/modules/readerfooter.lua b/frontend/apps/reader/modules/readerfooter.lua index 561694957..b1d88da5c 100644 --- a/frontend/apps/reader/modules/readerfooter.lua +++ b/frontend/apps/reader/modules/readerfooter.lua @@ -766,8 +766,9 @@ end function ReaderFooter:unscheduleFooterAutoRefresh() if not self.autoRefreshFooter then return end -- not yet set up + -- Slightly different wording than in rescheduleFooterAutoRefreshIfNeeded because it might not actually be scheduled at all + logger.dbg("ReaderFooter: unschedule autoRefreshFooter") UIManager:unschedule(self.autoRefreshFooter) - logger.dbg("ReaderFooter.autoRefreshFooter unscheduled") end function ReaderFooter:rescheduleFooterAutoRefreshIfNeeded() @@ -808,12 +809,12 @@ function ReaderFooter:rescheduleFooterAutoRefreshIfNeeded() if schedule then UIManager:scheduleIn(61 - tonumber(os.date("%S")), self.autoRefreshFooter) if not unscheduled then - logger.dbg("ReaderFooter.autoRefreshFooter scheduled") + logger.dbg("ReaderFooter: scheduled autoRefreshFooter") else - logger.dbg("ReaderFooter.autoRefreshFooter rescheduled") + logger.dbg("ReaderFooter: rescheduled autoRefreshFooter") end elseif unscheduled then - logger.dbg("ReaderFooter.autoRefreshFooter unscheduled") + logger.dbg("ReaderFooter: unscheduled autoRefreshFooter") end end diff --git a/frontend/ui/uimanager.lua b/frontend/ui/uimanager.lua index dbe617835..a7953cded 100644 --- a/frontend/ui/uimanager.lua +++ b/frontend/ui/uimanager.lua @@ -1659,6 +1659,12 @@ function UIManager:handleInput() -- this function emits (plugin), or within waitEvent() right after (hardware). -- Anywhere else breaks preventStandby/allowStandby invariants used by background jobs while UI is left running. self:_standbyTransition() + if self.PM_INPUT_TIMEOUT then + -- If the PM state transition requires an early return from input polling, honor that. + -- c.f., UIManager:setPMInputTimeout (and AutoSuspend:AllowStandbyHandler). + deadline = now + time.s(self.PM_INPUT_TIMEOUT) + self.PM_INPUT_TIMEOUT = nil + end -- wait for next batch of events local input_events = Input:waitEvent(now, deadline) @@ -1841,6 +1847,12 @@ function UIManager:_standbyTransition() self._prev_prevent_standby_count = self._prevent_standby_count end +-- Used by a PM transition event handler to request an early return from input polling (value in s). +-- NOTE: We can't re-use setInputTimeout to avoid interactions with ZMQ... +function UIManager:setPMInputTimeout(timeout) + self.PM_INPUT_TIMEOUT = timeout +end + --- Broadcasts a `FlushSettings` Event to *all* widgets. function UIManager:flushSettings() self:broadcastEvent(Event:new("FlushSettings")) diff --git a/plugins/autosuspend.koplugin/main.lua b/plugins/autosuspend.koplugin/main.lua index 6002a863f..122446cf4 100644 --- a/plugins/autosuspend.koplugin/main.lua +++ b/plugins/autosuspend.koplugin/main.lua @@ -204,12 +204,6 @@ function AutoSuspend:_unschedule_standby() end -- Make sure we don't trigger a ghost LeaveStandby event... - if self.wrapped_leave_standby_task then - logger.dbg("AutoSuspend: unschedule leave standby task wrapper") - UIManager:unschedule(self.wrapped_leave_standby_task) - self.wrapped_leave_standby_task = nil - end - if self.leave_standby_task then logger.dbg("AutoSuspend: unschedule leave standby task") UIManager:unschedule(self.leave_standby_task) @@ -338,8 +332,6 @@ end function AutoSuspend:onLeaveStandby() logger.dbg("AutoSuspend: onLeaveStandby") - -- If the Event got through, tickAfterNext did its thing, clear the reference to the initial nextTick wrapper... - self.wrapped_leave_standby_task = nil -- Unschedule suspend and shutdown, as the realtime clock has ticked self:_unschedule() -- Reschedule suspend and shutdown (we'll recompute the delay based on the last user input, *not* the current time). @@ -599,8 +591,21 @@ function AutoSuspend:AllowStandbyHandler() -- to make sure UIManager will consume the input events that woke us up first -- (in case we were woken up by user input, as opposed to an rtc wake alarm)! -- (This ensures we'll use an up to date last_action_time, and that it only ever gets updated from *user* input). - -- NOTE: UIManager consumes scheduled tasks before input events, which is why we can't use nextTick. - self.wrapped_leave_standby_task = UIManager:tickAfterNext(self.leave_standby_task) + -- NOTE: While UIManager consumes scheduled tasks before input events, we do *NOT* have to rely on tickAfterNext, + -- solely because of where we run inside an UI frame (via UIManager:_standbyTransition): + -- we're neither a scheduled task nor an input event, we run *between* scheduled tasks and input polling. + -- That means we go straight to input polling when returning, *without* a trip through the task queue + -- (c.f., UIManager:_checkTasks in UIManager:handleInput). + UIManager:nextTick(self.leave_standby_task) + + -- Since we go straight to input polling, and that our time spent in standby won't have affected the already computed + -- input polling deadline (because MONOTONIC doesn't tick during standby/suspend), + -- tweak said deadline to make sure poll will return immediately, so we get a chance to run through the task queue ASAP. + -- This ensures we get a LeaveStandby event in a timely fashion, + -- even when there isn't actually any user input happening (e.g., woken up by the rtc alarm). + -- This shouldn't prevent us from actually consuming any pending input events first, + -- because if we were woken up by user input, those events should already be in the evdev queue... + UIManager:setPMInputTimeout(0) end end