mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
PM: Optimize task queue handling around standby (#10203)
Instead of firing on(Enter|Leave)Standby Events, and having every other piece of code that might care about that handle re-scheduling their stuff themselves; simply make the standby implementation (i.e., AutoSuspend's) shift the whole task queue by the amount of time spent in standby to re-sync everything automatically. (This is necessary in the first place because Linux, as the task queue ticks in CLOCK_MONOTONIC, which does *not* tick during suspend/standby; while we expect most of the tasks scheduled to actually reflect real world clock delays).
This commit is contained in:
@@ -165,10 +165,6 @@ function ReaderCoptListener:onResume()
|
||||
self:headerRefresh()
|
||||
end
|
||||
|
||||
function ReaderCoptListener:onLeaveStandby()
|
||||
self:headerRefresh()
|
||||
end
|
||||
|
||||
function ReaderCoptListener:onOutOfScreenSaver()
|
||||
if not self._delayed_screensaver then
|
||||
return
|
||||
@@ -181,7 +177,6 @@ end
|
||||
-- Unschedule on these events
|
||||
ReaderCoptListener.onCloseDocument = ReaderCoptListener.unscheduleHeaderRefresh
|
||||
ReaderCoptListener.onSuspend = ReaderCoptListener.unscheduleHeaderRefresh
|
||||
ReaderCoptListener.onEnterStandby = ReaderCoptListener.unscheduleHeaderRefresh
|
||||
|
||||
function ReaderCoptListener:setAndSave(setting, property, value)
|
||||
self.ui.document._document:setIntProperty(property, value)
|
||||
|
||||
@@ -2471,21 +2471,12 @@ function ReaderFooter:onOutOfScreenSaver()
|
||||
self:rescheduleFooterAutoRefreshIfNeeded()
|
||||
end
|
||||
|
||||
function ReaderFooter:onLeaveStandby()
|
||||
self:maybeUpdateFooter()
|
||||
self:rescheduleFooterAutoRefreshIfNeeded()
|
||||
end
|
||||
|
||||
function ReaderFooter:onSuspend()
|
||||
self:unscheduleFooterAutoRefresh()
|
||||
-- Reset the initial marker
|
||||
self.progress_bar.inital_percentage = nil
|
||||
end
|
||||
|
||||
function ReaderFooter:onEnterStandby()
|
||||
self:unscheduleFooterAutoRefresh()
|
||||
end
|
||||
|
||||
function ReaderFooter:onCloseDocument()
|
||||
self:unscheduleFooterAutoRefresh()
|
||||
end
|
||||
|
||||
@@ -243,6 +243,16 @@ function UIManager:close(widget, refreshtype, refreshregion, refreshdither)
|
||||
end
|
||||
end
|
||||
|
||||
--- Shift the execution times of all scheduled tasks.
|
||||
-- UIManager uses CLOCK_MONOTONIC (which doesn't tick during standby), so shifting the execution
|
||||
-- time by a negative value will lead to an execution at the expected time.
|
||||
-- @param time if positive execute the tasks later, if negative they should be executed earlier
|
||||
function UIManager:shiftScheduledTasksBy(shift_time)
|
||||
for i, v in ipairs(self._task_queue) do
|
||||
v.time = v.time + shift_time
|
||||
end
|
||||
end
|
||||
|
||||
-- Schedule an execution task; task queue is in descending order
|
||||
function UIManager:schedule(sched_time, action, ...)
|
||||
local lo, hi = 1, #self._task_queue
|
||||
|
||||
@@ -212,44 +212,14 @@ function AutoDim:_onResume()
|
||||
self:_schedule_autodim_task()
|
||||
end
|
||||
|
||||
function AutoDim:_onEnterStandby()
|
||||
self:_unschedule_autodim_task()
|
||||
-- don't unschedule ramp task, as this is done in onLeaveStandby if necessary
|
||||
end
|
||||
|
||||
function AutoDim:_onLeaveStandby()
|
||||
if self.isCurrentlyDimming then
|
||||
if self.last_ramp_scheduling_time then
|
||||
-- we are during the ramp down
|
||||
local now = UIManager:getElapsedTimeSinceBoot()
|
||||
local next_ramp_time_s = self.last_ramp_scheduling_time + time.s(self.autodim_step_time_s) - now
|
||||
self:_unschedule_ramp_task() -- self.last_ramp_scheduling_time gets deleted with this call
|
||||
self.isCurrentlyDimming = true -- as this gets deleted by `_unschedule_ramp_task()`
|
||||
if next_ramp_time_s <= 0 then -- only happens, when standby is ended by a scheduled ramp_task()
|
||||
self:ramp_task()
|
||||
else
|
||||
self:_schedule_ramp_task(time.to_s(next_ramp_time_s))
|
||||
end
|
||||
else
|
||||
self:_unschedule_ramp_task()
|
||||
end
|
||||
else
|
||||
self:autodim_task() -- check times and reschedule autodim_task if necessary
|
||||
end
|
||||
end
|
||||
|
||||
function AutoDim:setEventHandlers()
|
||||
self.onResume = self._onResume
|
||||
self.onSuspend = self._onSuspend
|
||||
self.onEnterStandby = self._onEnterStandby
|
||||
self.onLeaveStandby = self._onLeaveStandby
|
||||
end
|
||||
|
||||
function AutoDim:clearEventHandlers()
|
||||
self.onResume = nil
|
||||
self.onSuspend = nil
|
||||
self.onEnterStandby = nil
|
||||
self.onLeaveStandby = nil
|
||||
end
|
||||
|
||||
function AutoDim:onFrontlightTurnedOff()
|
||||
|
||||
@@ -5,7 +5,6 @@ if not Device:canSuspend() then
|
||||
return { disabled = true, }
|
||||
end
|
||||
|
||||
local Event = require("ui/event")
|
||||
local Math = require("optmath")
|
||||
local NetworkMgr = require("ui/network/manager")
|
||||
local PluginShare = require("pluginshare")
|
||||
@@ -34,8 +33,6 @@ local AutoSuspend = WidgetContainer:extend{
|
||||
task = nil,
|
||||
kindle_task = nil,
|
||||
standby_task = nil,
|
||||
leave_standby_task = nil,
|
||||
wrapped_leave_standby_task = nil,
|
||||
going_to_suspend = nil,
|
||||
}
|
||||
|
||||
@@ -109,10 +106,10 @@ function AutoSuspend:_start()
|
||||
end
|
||||
end
|
||||
|
||||
function AutoSuspend:_start_standby()
|
||||
function AutoSuspend:_start_standby(sleep_in)
|
||||
if self:_enabledStandby() then
|
||||
logger.dbg("AutoSuspend: start standby timer at", time.format_time(self.last_action_time))
|
||||
self:_schedule_standby()
|
||||
self:_schedule_standby(sleep_in)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -201,14 +198,6 @@ function AutoSuspend:init()
|
||||
self.standby_task = function()
|
||||
self:_schedule_standby()
|
||||
end
|
||||
self.leave_standby_task = function()
|
||||
-- Only if we're not already entering suspend...
|
||||
if self.going_to_suspend then
|
||||
return
|
||||
end
|
||||
|
||||
UIManager:broadcastEvent(Event:new("LeaveStandby"))
|
||||
end
|
||||
|
||||
-- Make sure we only have an AllowStandby handler when we actually want one...
|
||||
self:toggleStandbyHandler(self:_enabledStandby())
|
||||
@@ -235,7 +224,6 @@ function AutoSuspend:onCloseWidget()
|
||||
|
||||
self:_unschedule_standby()
|
||||
self.standby_task = nil
|
||||
self.leave_standby_task = nil
|
||||
end
|
||||
|
||||
function AutoSuspend:onInputEvent()
|
||||
@@ -252,17 +240,13 @@ function AutoSuspend:_unschedule_standby()
|
||||
|
||||
self.is_standby_scheduled = false
|
||||
end
|
||||
|
||||
-- Make sure we don't trigger a ghost LeaveStandby event...
|
||||
if self.leave_standby_task then
|
||||
logger.dbg("AutoSuspend: unschedule leave standby task")
|
||||
UIManager:unschedule(self.leave_standby_task)
|
||||
end
|
||||
end
|
||||
|
||||
function AutoSuspend:_schedule_standby()
|
||||
function AutoSuspend:_schedule_standby(sleep_in)
|
||||
sleep_in = sleep_in or self.auto_standby_timeout_seconds
|
||||
|
||||
-- Start the long list of conditions in which we do *NOT* want to go into standby ;).
|
||||
if not Device:canStandby() then
|
||||
if not Device:canStandby() or self.going_to_suspend then
|
||||
return
|
||||
end
|
||||
|
||||
@@ -292,7 +276,7 @@ function AutoSuspend:_schedule_standby()
|
||||
standby_delay_seconds = self.auto_standby_timeout_seconds
|
||||
else
|
||||
local now = UIManager:getElapsedTimeSinceBoot()
|
||||
standby_delay_seconds = self.auto_standby_timeout_seconds - time.to_number(now - self.last_action_time)
|
||||
standby_delay_seconds = sleep_in - time.to_number(now - self.last_action_time)
|
||||
|
||||
-- If we blow past the deadline on the first call of a scheduling cycle,
|
||||
-- make sure we don't go straight to allowStandby, as we haven't called preventStandby yet...
|
||||
@@ -302,7 +286,7 @@ function AutoSuspend:_schedule_standby()
|
||||
-- or if the only input events we consumed did not trigger an InputEvent event (woken up by gyro events),
|
||||
-- meaning self.last_action_time is further in the past than it ought to.
|
||||
-- Delay by the full amount to avoid further bad scheduling interactions.
|
||||
standby_delay_seconds = self.auto_standby_timeout_seconds
|
||||
standby_delay_seconds = sleep_in
|
||||
end
|
||||
end
|
||||
|
||||
@@ -360,8 +344,7 @@ function AutoSuspend:onSuspend()
|
||||
UIManager:preventStandby()
|
||||
end
|
||||
|
||||
-- And make sure onLeaveStandby, which will come *after* us if we suspended *during* standby,
|
||||
-- won't re-schedule stuff right before entering suspend...
|
||||
-- Make sure that we don't re-schedule standby *after* us if we suspended *during* standby,
|
||||
self.going_to_suspend = true
|
||||
end
|
||||
|
||||
@@ -386,20 +369,6 @@ function AutoSuspend:onResume()
|
||||
self:_start_standby()
|
||||
end
|
||||
|
||||
function AutoSuspend:onLeaveStandby()
|
||||
logger.dbg("AutoSuspend: onLeaveStandby")
|
||||
-- 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).
|
||||
-- i.e., the goal is to behave as if we'd never unscheduled it, making sure we do *NOT* reset the delay to the full timeout.
|
||||
self:_start()
|
||||
-- Assuming _start didn't send us straight to onSuspend (i.e., we were woken from standby by the scheduled suspend task!)...
|
||||
if not self.going_to_suspend then
|
||||
-- Reschedule standby, too (we're guaranteed that no standby task is currently scheduled, hence the lack of unscheduling).
|
||||
self:_start_standby()
|
||||
end
|
||||
end
|
||||
|
||||
function AutoSuspend:onUnexpectedWakeupLimit()
|
||||
logger.dbg("AutoSuspend: onUnexpectedWakeupLimit")
|
||||
-- Should be unnecessary, because we should *always* follow onSuspend, which already does this...
|
||||
@@ -649,7 +618,6 @@ function AutoSuspend:AllowStandbyHandler()
|
||||
end
|
||||
|
||||
if wake_in >= 3 then -- don't go into standby, if scheduled wakeup is in less than 3 secs
|
||||
UIManager:broadcastEvent(Event:new("EnterStandby"))
|
||||
logger.dbg("AutoSuspend: entering standby with a wakeup alarm in", wake_in, "s")
|
||||
|
||||
-- This obviously needs a matching implementation in Device, the canonical one being Kobo.
|
||||
@@ -657,29 +625,28 @@ function AutoSuspend:AllowStandbyHandler()
|
||||
|
||||
logger.dbg("AutoSuspend: left standby after", time.format_time(Device.last_standby_time), "s")
|
||||
|
||||
-- We delay the LeaveStandby event (our onLeaveStandby handler is responsible for rescheduling everything properly),
|
||||
-- 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: While UIManager consumes scheduled tasks before input events, we do *NOT* have to rely on tickAfterNext,
|
||||
-- NOTE: UIManager consumes scheduled tasks before input events,
|
||||
-- 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)
|
||||
|
||||
UIManager:shiftScheduledTasksBy( - Device.last_standby_time) -- correct scheduled times by last_standby_time
|
||||
|
||||
-- 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:consumeInputEarlyAfterPM(true)
|
||||
|
||||
-- When we exit this method, we are sure that the input polling deadline is zero (consumeInputEarly).
|
||||
-- UIManager will check newly scheduled tasks before going to input polling again (with a new deadline).
|
||||
self:_start_standby() -- Schedule the next standby check in the future.
|
||||
else
|
||||
if not self.going_to_suspend then
|
||||
self:_start_standby()
|
||||
end
|
||||
-- When we exit this method, we are sure that the input polling deadline is approximately `wake_in`.
|
||||
-- So it is safe to schedule another task a bit later.
|
||||
self:_start_standby(wake_in + 0.1) -- Schedule the next standby check 0.1 seconds after the next calculated wakeup time.
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
local Device = require("device")
|
||||
local Event = require("ui/event")
|
||||
local PluginShare = require("pluginshare")
|
||||
local UIManager = require("ui/uimanager")
|
||||
@@ -29,14 +28,14 @@ function AutoTurn:_schedule()
|
||||
return
|
||||
end
|
||||
|
||||
local delay = self.last_action_time + time.s(self.autoturn_sec) - UIManager:getTime()
|
||||
local delay = self.last_action_time + time.s(self.autoturn_sec) - UIManager:getElapsedTimeSinceBoot()
|
||||
|
||||
if delay <= 0 then
|
||||
local top_wg = UIManager:getTopmostVisibleWidget() or {}
|
||||
if top_wg.name == "ReaderUI" then
|
||||
logger.dbg("AutoTurn: go to next page")
|
||||
self.ui:handleEvent(Event:new("GotoViewRel", self.autoturn_distance))
|
||||
self.last_action_time = UIManager:getTime()
|
||||
self.last_action_time = UIManager:getElapsedTimeSinceBoot()
|
||||
end
|
||||
logger.dbg("AutoTurn: schedule in", self.autoturn_sec)
|
||||
UIManager:scheduleIn(self.autoturn_sec, self.task)
|
||||
@@ -60,10 +59,10 @@ end
|
||||
|
||||
function AutoTurn:_start()
|
||||
if self:_enabled() then
|
||||
local now = UIManager:getTime()
|
||||
logger.dbg("AutoTurn: start at", time.format_time(now))
|
||||
local time_since_boot = UIManager:getElapsedTimeSinceBoot()
|
||||
logger.dbg("AutoTurn: start at", time.format_time(time_since_boot))
|
||||
PluginShare.pause_auto_suspend = true
|
||||
self.last_action_time = now
|
||||
self.last_action_time = time_since_boot
|
||||
self:_schedule()
|
||||
|
||||
local text
|
||||
@@ -109,11 +108,7 @@ end
|
||||
|
||||
function AutoTurn:onInputEvent()
|
||||
logger.dbg("AutoTurn: onInputEvent")
|
||||
self.last_action_time = UIManager:getTime()
|
||||
end
|
||||
|
||||
function AutoTurn:onEnterStandby()
|
||||
self:_unschedule()
|
||||
self.last_action_time = UIManager:getElapsedTimeSinceBoot()
|
||||
end
|
||||
|
||||
-- We do not want autoturn to turn pages during the suspend process.
|
||||
@@ -123,16 +118,6 @@ function AutoTurn:onSuspend()
|
||||
self:_unschedule()
|
||||
end
|
||||
|
||||
function AutoTurn:_onLeaveStandby()
|
||||
self.last_action_time = self.last_action_time - Device.last_standby_time
|
||||
|
||||
-- We messed with last_action_time, so a complete reschedule has to be done.
|
||||
if self:_enabled() then
|
||||
self:_unschedule()
|
||||
self:_schedule()
|
||||
end
|
||||
end
|
||||
|
||||
function AutoTurn:_onResume()
|
||||
logger.dbg("AutoTurn: onResume")
|
||||
self:_start()
|
||||
@@ -168,7 +153,6 @@ function AutoTurn:addToMainMenu(menu_items)
|
||||
self:_unschedule()
|
||||
menu:updateItems()
|
||||
self.onResume = nil
|
||||
self.onLeaveStandby = nil
|
||||
end,
|
||||
ok_always_enabled = true,
|
||||
callback = function(t)
|
||||
@@ -180,7 +164,6 @@ function AutoTurn:addToMainMenu(menu_items)
|
||||
self:_start()
|
||||
menu:updateItems()
|
||||
self.onResume = self._onResume
|
||||
self.onLeaveStandby = self._onLeaveStandby
|
||||
end,
|
||||
}
|
||||
UIManager:show(autoturn_spin)
|
||||
|
||||
@@ -137,7 +137,9 @@ function AutoWarmth:onAutoWarmthMode()
|
||||
self:scheduleMidnightUpdate()
|
||||
end
|
||||
|
||||
function AutoWarmth:leavePowerSavingState(from_resume)
|
||||
function AutoWarmth:_onResume()
|
||||
logger.dbg("AutoWarmth: onResume")
|
||||
|
||||
local resume_date = os.date("*t")
|
||||
|
||||
-- check if resume and suspend are done on the same day
|
||||
@@ -146,26 +148,16 @@ function AutoWarmth:leavePowerSavingState(from_resume)
|
||||
|
||||
local now_s = SunTime:getTimeInSec(resume_date)
|
||||
self.sched_warmth_index = self.sched_warmth_index - 1 -- scheduleNextWarmth will check this
|
||||
self:scheduleNextWarmthChange(from_resume)
|
||||
self:scheduleNextWarmthChange(true)
|
||||
self:scheduleToggleFrontlight(now_s) -- reset user toggles at sun set or sun rise
|
||||
self:toggleFrontlight(now_s)
|
||||
-- Reschedule 1sec after midnight
|
||||
UIManager:scheduleIn(24*3600 + 1 - now_s, self.scheduleMidnightUpdate, self)
|
||||
else
|
||||
self:scheduleMidnightUpdate(from_resume) -- resume is on the other day, do all calcs again
|
||||
self:scheduleMidnightUpdate(true) -- resume is on the other day, do all calcs again
|
||||
end
|
||||
end
|
||||
|
||||
function AutoWarmth:_onResume()
|
||||
logger.dbg("AutoWarmth: onResume")
|
||||
self:leavePowerSavingState(true)
|
||||
end
|
||||
|
||||
function AutoWarmth:_onLeaveStandby()
|
||||
logger.dbg("AutoWarmth: onLeaveStandby")
|
||||
self:leavePowerSavingState(false)
|
||||
end
|
||||
|
||||
function AutoWarmth:_onSuspend()
|
||||
logger.dbg("AutoWarmth: onSuspend")
|
||||
UIManager:unschedule(self.scheduleMidnightUpdate)
|
||||
@@ -173,8 +165,6 @@ function AutoWarmth:_onSuspend()
|
||||
UIManager:unschedule(self.setFrontlight)
|
||||
end
|
||||
|
||||
AutoWarmth._onEnterStandby = AutoWarmth._onSuspend
|
||||
|
||||
function AutoWarmth:_onToggleNightMode()
|
||||
logger.dbg("AutoWarmth: onToggleNightMode")
|
||||
if not self.hide_nightmode_warning then
|
||||
@@ -220,8 +210,6 @@ end
|
||||
function AutoWarmth:setEventHandlers()
|
||||
self.onResume = self._onResume
|
||||
self.onSuspend = self._onSuspend
|
||||
self.onEnterStandby = self._onEnterStandby
|
||||
self.onLeaveStandby = self._onLeaveStandby
|
||||
if self.control_nightmode then
|
||||
self.onToggleNightMode = self._onToggleNightMode
|
||||
self.onSetNightMode = self._onToggleNightMode
|
||||
@@ -234,8 +222,6 @@ end
|
||||
function AutoWarmth:clearEventHandlers()
|
||||
self.onResume = nil
|
||||
self.onSuspend = nil
|
||||
self.onEnterStandby = nil
|
||||
self.onLeaveStandby = nil
|
||||
self.onToggleNightMode = nil
|
||||
self.onSetNightMode = nil
|
||||
self.onToggleFrontlight = nil
|
||||
|
||||
Reference in New Issue
Block a user