From fd31bcc5fdf23896e973bfe77b0a5f94335d76b7 Mon Sep 17 00:00:00 2001 From: ezdiy Date: Sun, 30 Aug 2020 15:01:56 +0200 Subject: [PATCH] Make UIManager track prevent/allowStandby state. (#6558) Conversely, Trapper and plugins report standby interruptibility. --- frontend/device/generic/device.lua | 5 +++ frontend/ui/trapper.lua | 2 + frontend/ui/uimanager.lua | 39 ++++++++++++++++++- .../commandrunner.lua | 3 ++ .../coverbrowser.koplugin/bookinfomanager.lua | 5 +++ 5 files changed, 52 insertions(+), 2 deletions(-) diff --git a/frontend/device/generic/device.lua b/frontend/device/generic/device.lua index 346788bc4..4a4f3c76c 100644 --- a/frontend/device/generic/device.lua +++ b/frontend/device/generic/device.lua @@ -306,6 +306,11 @@ function Device:info() return self.model end +-- Hardware specific method for UI to signal allowed/disallowed standby. +-- The device is allowed to enter standby only from within waitForEvents, +-- and only if allowed state is true at the time of waitForEvents() invocation. +function Device:setAutoStandby(isAllowed) end + -- Hardware specific method to handle usb plug in event function Device:usbPlugIn() end diff --git a/frontend/ui/trapper.lua b/frontend/ui/trapper.lua index a5d8ef304..71291450c 100644 --- a/frontend/ui/trapper.lua +++ b/frontend/ui/trapper.lua @@ -43,8 +43,10 @@ function Trapper:wrap(func) -- Catch and log any error happening in func (an error happening -- in a coroutine just aborts silently the coroutine) local pcalled_func = function() + UIManager:preventStandby() -- we use xpcall as it can give a whole stacktrace, unlike pcall local ok, err = xpcall(func, debug.traceback) + UIManager:allowStandby() if not ok then logger.warn("error in wrapped function:", err) return false diff --git a/frontend/ui/uimanager.lua b/frontend/ui/uimanager.lua index 4ae614c33..7ff6ec7dc 100644 --- a/frontend/ui/uimanager.lua +++ b/frontend/ui/uimanager.lua @@ -37,6 +37,8 @@ local UIManager = { _refresh_func_stack = {}, _entered_poweroff_stage = false, _exit_code = nil, + _prevent_standby_count = 0, + _prev_prevent_standby_count = 0, event_hook = require("ui/hook_container"):new() } @@ -80,12 +82,11 @@ function UIManager:init() end) end if Device:isPocketBook() then + -- Only fg/bg state plugin notifiers, not real power event. self.event_handlers["Suspend"] = function() self:_beforeSuspend() - Device:onPowerEvent("Power") end self.event_handlers["Resume"] = function() - Device:onPowerEvent("Power") self:_afterResume() end end @@ -1198,6 +1199,11 @@ function UIManager:handleInput() wait_us = math.min(wait_us or math.huge, self.ZMQ_TIMEOUT) end + -- If allowed, entering standby (from which we can wake by input) must trigger in response to event + -- 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() + -- wait for next event local input_event = Input:waitEvent(wait_us) @@ -1301,6 +1307,35 @@ function UIManager:resume() end end +-- Release standby lock once. We're done with whatever we were doing in the background. +-- Standby is re-enabled only after all issued prevents are paired with allowStandby for each one. +function UIManager:allowStandby() + assert(self._prevent_standby_count > 0, "allowing standby that isn't prevented; you have an allow/prevent mismatch somewhere") + self._prevent_standby_count = self._prevent_standby_count - 1 +end + +-- Prevent standby, ie something is happening in background, yet UI may tick. +function UIManager:preventStandby() + self._prevent_standby_count = self._prevent_standby_count + 1 +end + +-- Allow/prevent calls above can interminently allow standbys, but we're not interested until +-- the state change crosses UI tick boundary, which is what self._prev_prevent_standby_count is tracking. +function UIManager:_standbyTransition() + if self._prevent_standby_count == 0 and self._prev_prevent_standby_count > 0 then + -- edge prevent->allow + logger.dbg("allow standby") + Device:setAutoStandby(true) + self:broadcastEvent(Event:new("AllowStandby")) + elseif self._prevent_standby_count > 0 and self._prev_prevent_standby_count == 0 then + -- edge allow->prevent + logger.dbg("prevent standby") + Device:setAutoStandby(false) + self:broadcastEvent(Event:new("PreventStandby")) + end + self._prev_prevent_standby_count = self._prevent_standby_count +end + function UIManager:flushSettings() self:broadcastEvent(Event:new("FlushSettings")) end diff --git a/plugins/backgroundrunner.koplugin/commandrunner.lua b/plugins/backgroundrunner.koplugin/commandrunner.lua index 60d9892d4..56a2a134d 100644 --- a/plugins/backgroundrunner.koplugin/commandrunner.lua +++ b/plugins/backgroundrunner.koplugin/commandrunner.lua @@ -1,4 +1,5 @@ local logger = require("logger") +local UIManager = require("ui/uimanager") local CommandRunner = { pio = nil, @@ -41,6 +42,7 @@ function CommandRunner:start(job) "sh plugins/backgroundrunner.koplugin/luawrapper.sh " .. "\"" .. self.job.executable .. "\"" logger.dbg("CommandRunner: Will execute command " .. command) + UIManager:preventStandby() self.pio = io.popen(command) end @@ -71,6 +73,7 @@ function CommandRunner:poll() self.job.result = 222 end end + UIManager:allowStandby() self.pio:close() self.pio = nil self.job.end_sec = os.time() diff --git a/plugins/coverbrowser.koplugin/bookinfomanager.lua b/plugins/coverbrowser.koplugin/bookinfomanager.lua index 09b998ca9..ae63dc7f0 100644 --- a/plugins/coverbrowser.koplugin/bookinfomanager.lua +++ b/plugins/coverbrowser.koplugin/bookinfomanager.lua @@ -544,6 +544,8 @@ function BookInfoManager:collectSubprocesses() local pid = self.subprocesses_pids[i] if util.isSubProcessDone(pid) then table.remove(self.subprocesses_pids, i) + -- Prevent has been issued for each bg task spawn, we must allow for each death too. + UIManager:allowStandby() else i = i + 1 end @@ -621,6 +623,9 @@ function BookInfoManager:extractInBackground(files) logger.warn("Failed lauching background extraction sub-process (fork failed)") return false -- let caller know it failed end + -- No straight control flow exists for background task completion here, so we bump prevent + -- counter on each task, and undo that inside collectSubprocesses() zombie reaper. + UIManager:preventStandby() table.insert(self.subprocesses_pids, task_pid) self.subprocesses_last_added_ts = util.gettime()