Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable the RTC alarm on shutdown #6211

Merged
merged 3 commits into from
May 31, 2020
Merged

Disable the RTC alarm on shutdown #6211

merged 3 commits into from
May 31, 2020

Conversation

NiLuJe
Copy link
Member

@NiLuJe NiLuJe commented May 30, 2020

Depends on koreader/koreader-base#1106


This change is Reviewable

@NiLuJe
Copy link
Member Author

NiLuJe commented May 30, 2020

@Frenzie: Shouldn't

if WakeupMgr:isWakeupAlarmScheduled() and WakeupMgr:validateWakeupAlarmByProximity() then
logger.info("Kobo suspend: scheduled wakeup.")
local res = WakeupMgr:wakeupAction()

be using

self.wakeup_mgr instead of WakeupMgr?

EDIT: Or Kobo.wakeup_mgr or something? (I just noticed it was inside an anonymous function).

self.wakeup_mgr = WakeupMgr:new()

@NiLuJe
Copy link
Member Author

NiLuJe commented May 30, 2020

That's a random 2AM PR after strace'ing Nickel during a sane shutdown, now that we can reliably trigger those via NickelMenu ;).

EDIT: Experiments which answered another question I had: no, Nickel doesn't have a SIGTERM handler :(. There's no nice graceful closing of the dbs on SIGTERM :/.

@NiLuJe
Copy link
Member Author

NiLuJe commented May 30, 2020

@Frenzie: Also, rtcwake disables the alarm interrupt as soon as it wakes up.

I guess this wouldn't make much sense for us, since we re-set a new alarm at that point, right?

@NiLuJe
Copy link
Member Author

NiLuJe commented May 30, 2020

Also, nothing ever uses WakeupMgr's dev_rtc: ffi/rtc.lua hardcodes /dev/rtc0 everywhere ;).

Copy link
Contributor

@poire-z poire-z left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not much code to review :)
(I don't really know much about that RTC stuff, but I guess that if something more is done just before poweroff, it can't hurt :)

@poire-z
Copy link
Contributor

poire-z commented May 30, 2020

Wasn't there some issue recently, here or on mobileread, where someone had his device auto waking up during the night?


I have the following code in my private patch, no issue noticed. But just to be sure, does it look sane to you, and compatible with the core code using /dev/rtc0 , while I use /sys/class/rtc/rtc0/wakealarm (don't remember why :) ?

if self.auto_suspend_sec > 0 then
    -- On programme une sortie du standby pour l'autosuspend
    -- Note : s'assurer que auto_standby_timeout_seconds < auto_suspend_timeout_seconds
    -- On retranche self.auto_standby_sec car il s'est passé ce temps depuis last action
    local wake_in_sec = self.auto_suspend_sec - self.auto_standby_sec
    self:mylog("  programming wakealarm for in ".. wake_in_sec)
    -- os.execute('echo 0 > /sys/class/rtc/rtc0/wakealarm ; echo +'.. wake_in_sec ..' > /sys/class/rtc/rtc0/wakealarm')
    -- Refait en pur lua pour éviter problème avec fork si memory leak :
    self:_writeToSysFile("/sys/class/rtc/rtc0/wakealarm", "0")
    self:_writeToSysFile("/sys/class/rtc/rtc0/wakealarm", "+"..wake_in_sec)
end
-- os.execute('echo standby > /sys/power/state ; echo 0 > /sys/class/rtc/rtc0/wakealarm')
-- Refait en pur lua pour éviter problème avec fork si memory leak :
self:_writeToSysFile("/sys/power/state", "standby")
-- (là, on est sorti du standby)
self:_writeToSysFile("/sys/class/rtc/rtc0/wakealarm", "0")

(that "autostandby" of mine is some state I trigger after 20s of inactivity where I just let the touchscreen enabled - so a touch wakes it up)

@NiLuJe
Copy link
Member Author

NiLuJe commented May 30, 2020

On the laptop right now, but /sys/class/rtc/rtc0 should essentially be a symlink to /dev/rtc0;).

(The sysfs interface is not available on Mk <= 5, hence the ioctl's, which work everywhere ;)).

@Frenzie
Copy link
Member

Frenzie commented May 30, 2020

@NiLuJe Possibly, I didn't test it in too much detail besides the basics (i.e., standby for a bit and then auto wake up a little while later to shut down) although I believe scheduling further wakeups and such was working as intended.

Copy link
Member

@pazos pazos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@Frenzie Frenzie added this to the 2020.06 milestone May 31, 2020
@NiLuJe NiLuJe merged commit 95567d8 into koreader:master May 31, 2020
@poire-z
Copy link
Contributor

poire-z commented Apr 28, 2021

@NiLuJe : regarding my snippet above #6211 (comment), actually some more complex bits of code than that, that seem to no longer work fine since you shuffle around all the clocks :)
Can you tell me what happens to CLOCK_MONOTONIC after a standby, when it's resumed?
I guess it starts with the same time as at standby time - so, anything scheduled previously and that should have run in the standby duration will not run immediately, right ? I had to exec some action just after resume for it to get a change to run or reschedule itself precisely.

Also, does the UIManager/linux monotonic time adjusts itself so it can quickly catch the duration spent sleeping?
Because soon after a resume, self:discardEvents(true) does not work as expected. In if TimeVal:now() < self._discard_events_till then, I got TimeVal:now().secs=5272 and self._discard_events_till.secs=5267 (5 secs difference! while all this happened in the time of a tap).
If not, may be there's some issue in using self._now in self._discard_events_till = self._now + delay ?

Bit confused about all that at this time - but if you have any idea, thanks :)

@NiLuJe
Copy link
Member Author

NiLuJe commented Apr 28, 2021

@poire-z: Yep, on Linux, MONOTONIC doesn't tick during suspend. So your understanding is correct, anything scheduled before suspend will run on resume in the same conditions as if a suspend had never happened.

(i.e., whereas before, most likely every scheduled tasks would have expired and run on resume).

You'd have to get me more context around where your patch actually applies to tell you what might be happening ;).


As for discardEvents, I couldn't find an easy test-case for it, so it's quite possible that there's a case where using the cached value doesn't make sense. What are you doing to reproduce that, exactly?

@poire-z
Copy link
Contributor

poire-z commented Apr 28, 2021

that there's a case where using the cached value doesn't make sense. What are you doing to reproduce that, exactly?

Well, I'm very edge-casey with all this.
It's some old code from 2017 that I have just updated to keep it working - it could really use a full rewrite.
Anyway, the context is that:

  • autostandby after 10s of inactivity, programming a RTC wakeup up before an autosuspend of 300s
  • waking up after 290s, it sets the frontlight off and schedule a real suspend in 30s - so I can notice the frontlight off if I was dreaming and tap the screen to not go into suspend, which then sets the frontlight on. This is what my discardEvent(true) is needed for, so that tap does not go one page forward.

The issue is that I catch the tap in :handleInput() : you set self._now at top, then you Input:waitEvent() - then I tap and I catch the event in my self:_resetAutoSuspendTimer() before the regular :handleInputEvent() - so, possibly after the delay spent in waitEvent(), which might be 5 seconds.
It's not a natural use case, so I'm fine with the hacks I found.

My original ugly code for context:

@@ -1616,10 +1651,14 @@

     -- delegate each input event to handler
     if input_events then
+        self:_resetAutoStandbyTimer() -- me
+        self:_resetAutoSuspendTimer() -- me kept back 20170628
         -- Handle the full batch of events
         for __, ev in ipairs(input_events) do
             self:handleInputEvent(ev)
         end
+        self:_resetAutoStandbyTimer() -- me: do it also after, if handling took time
+        self:_resetAutoSuspendTimer() -- me kept back 20170628
     end

     if self.looper then
@@ -1794,5 +1833,710 @@
-- "me kept back 20170628" : remis logique AutoSuspend supprimee,
-- et externalisee dans plugin par Hzj-jie 20170623 :
-- https://github.com/koreader/koreader/commit/7d2ed4c3d0f61330a92ecf5a944eeedbd91c45be

-- me kept back 20170628
-- Kobo does not have an auto suspend function, so we implement it ourselves.
function UIManager:_initAutoSuspend()
    local function isAutoSuspendEnabled()
        return Device:isKobo() and self.auto_suspend_sec > 0
    end

    local sec = G_reader_settings:readSetting("auto_suspend_timeout_seconds")
    if sec then
        self.auto_suspend_sec = sec
    else
        -- default setting is 60 minutes
        self.auto_suspend_sec = 60 * 60
    end

    if isAutoSuspendEnabled() then
        self.auto_suspend_action = function()
            self:mylog("in auto_suspend_action") -- me
            -- me : par sécurité : logiquement, on ne devrait pas avoir programmé
            -- plusieurs fois auto_suspend_action, mais bon, sait-on jamais
            self:unschedule(self.auto_suspend_action)
            local now = os.time()
            -- Do not repeat auto suspend procedure after suspend.
            if self.last_action_sec + self.auto_suspend_sec <= now then
                -- self:suspend()
                -- me: on éteind juste l'éclairage, et on délai le vrai suspend de 30 secondes,
                -- qu'on puisse le canceller si on s'était mis à rêver
                self:mylog("  suspending pre-stage")
                -- On fait comme KoboPowerD:beforeSuspend() :
                Device.powerd.fl:setBrightness(0) -- is_fl_on & fl_intensity conservent leurs valeurs
                self:_stopAutoStandby() -- pour ne pas passer en standby d'ici là
                self:mylog("  scheduling real_suspend_action in 30")
                self:scheduleIn(30, self.real_suspend_action)
                self.real_suspend_scheduled = true
            else
                self:mylog("  scheduling auto_suspend_action in " .. (self.last_action_sec + self.auto_suspend_sec - now))
                self:scheduleIn(
                    self.last_action_sec + self.auto_suspend_sec - now,
                    self.auto_suspend_action)
            end
        end

        self.real_suspend_scheduled = false -- me
        self.real_suspend_action = function()
            self:mylog("  in real_suspend_action")
            self.real_suspend_scheduled = false
            -- Au choix : vrai suspend avec screensaver :
            -- self:suspend()
            -- Ou bien : standby avec lock :
            self:standbyAndLockInput()
        end

        function UIManager:_startAutoSuspend()
            self:mylog("in _startAutoSuspend") -- me
            self.last_action_sec = os.time()
            self:nextTick(self.auto_suspend_action)
        end
        dbg:guard(UIManager, '_startAutoSuspend',
            function()
                assert(isAutoSuspendEnabled())
            end)

        function UIManager:_stopAutoSuspend()
            self:mylog("in _stopAutoSuspend") -- me
            self:unschedule(self.auto_suspend_action)
        end

        function UIManager:_resetAutoSuspendTimer()
            -- self:mylog("in _resetAutoSuspendTimer") -- me
            self.last_action_sec = os.time()
            -- me : on nettoie si action alors qu'on a programmé le vrai suspend
            if self.real_suspend_scheduled then
                self:unschedule(self.real_suspend_action)
                self.real_suspend_scheduled = false
                -- On remet la lumière si elle était là avant
                -- On fait comme KoboPowerD:afterResume() :
                if Device.powerd.is_fl_on then
                    Device.powerd.fl:setBrightness(Device.powerd.fl_intensity)
                end
                self:_startAutoSuspend() -- et on remet l'autosuspend
                self:_startAutoStandby() -- et on remet l'autostandby
                -- pour que le handler discard le touch qu'on vient de faire
                -- pour rallumer (on discard LES events (touch, release))
                self:discardEvents(true)
            end
        end

        self:_startAutoSuspend()
    else
        self._startAutoSuspend = noop
        self._stopAutoSuspend = noop
    end
end

UIManager._resetAutoSuspendTimer = noop -- me kept back 20170628


-- me : pour écrire dans fichier système plutôt que os.execute("echo 1 > /sys/toto")
-- qui est sujet à problème si memory leak et fork impossible
function UIManager:_writeToSysFile(sysfile, value)
    local f, re, err_msg, err_code
    -- logger.dbg("got to write '"..value.."' to "..sysfile)
    f = io.open(sysfile, "w")
    if not f then
        logger.err("Cannot open ", sysfile, " for writing", value)
        return false
    end
    re, err_msg, err_code = f:write(value.."\n")
    -- logger.dbg("Wrote", value, "to", sysfile, "returned", re)
    if not re then
        logger.err("Write error to", sysfile, " for writing", value, ":", err_msg, err_code)
        io.close(f)
        return false
    end
    io.close(f)
    return true
end

-- me : autoStandby (ça délaie le changement de pages de ~500ms à 1.5s,
-- mais activable / désactivable via setting auto_standby_timeout_seconds=0)
function UIManager:_initAutoStandby()
    local function isAutoStandbyEnabled()
        return Device:isKobo() and self.auto_standby_sec > 0
    end

    local sec = G_reader_settings:readSetting("auto_standby_timeout_seconds")
    if sec then
        self.auto_standby_sec = sec
    else
        self.auto_standby_sec = 0   -- default = 0 => not enabled
    end

    if isAutoStandbyEnabled() then
        self.auto_standby_action = function()
            self:mylog("in auto_standby_action")
            -- me : par sécurité : logiquement, on ne devrait pas avoir programmé
            -- plusieurs fois auto_suspend_action, mais bon, sait-on jamais
            self:unschedule(self.auto_standby_action)
            local now = ffiUtil.gettime()
            if self.last_wakeaction_sec + self.auto_standby_sec <= now then
                -- Si wifi enabled, on ne fait juste pas les opérations systèmes,
                -- c'est plus simple que de trouver où mettre des stop/startAutoStandby
                -- pour le désactiver pendant le WIFI
                if not self:isWifiEnabled() then
                    if self.auto_suspend_sec > 0 then
                        -- On programme une sortie du standby pour l'autosuspend
                        -- Note : s'assurer que auto_standby_timeout_seconds < auto_suspend_timeout_seconds
                        -- On retranche self.auto_standby_sec car il s'est passé ce temps depuis last action
                        local wake_in_sec = self.auto_suspend_sec - self.auto_standby_sec
                        self:mylog("  programming wakealarm for in ".. wake_in_sec)
                        -- os.execute('echo 0 > /sys/class/rtc/rtc0/wakealarm ; echo +'.. wake_in_sec ..' > /sys/class/rtc/rtc0/wakealarm')
                        -- Refait en pur lua pour éviter problème avec fork si memory leak :
                        self:_writeToSysFile("/sys/class/rtc/rtc0/wakealarm", "0")
                        self:_writeToSysFile("/sys/class/rtc/rtc0/wakealarm", "+"..wake_in_sec)
                    end
                    self:updateStateStats("autostandby")
                    -- Pas la peine de logguer les autostandby, y'en a trop, on saure avec StateStats
                    -- os.execute('echo standby > /sys/power/state ; echo 0 > /sys/class/rtc/rtc0/wakealarm')
                    -- Refait en pur lua pour éviter problème avec fork si memory leak :
                    self:_writeToSysFile("/sys/power/state", "standby")
                    -- (là, on est sorti du standby)
                    self:_writeToSysFile("/sys/class/rtc/rtc0/wakealarm", "0")
                    self:updateStateStats("active")
                end
                self:_startAutoStandby() -- on redémarre l'auto standby
            else
                self:mylog("  scheduling auto_standby_action in " .. (self.last_wakeaction_sec + self.auto_standby_sec - now))
                self:scheduleIn(
                    self.last_wakeaction_sec + self.auto_standby_sec - now,
                    self.auto_standby_action)
            end
        end

        function UIManager:_startAutoStandby()
            self:mylog("in _startAutoStandby")
            self.last_wakeaction_sec = ffiUtil.gettime()
            self:nextTick(self.auto_standby_action)
        end
        dbg:guard(UIManager, '_startAutoStandby',
            function()
                assert(isAutoStandbyEnabled())
            end)

        function UIManager:_stopAutoStandby()
            self:mylog("in _stopAutoStandby")
            self:unschedule(self.auto_standby_action)
        end

        function UIManager:_resetAutoStandbyTimer()
            -- self:mylog("in _resetAutoStandbyTimer")
            self.last_wakeaction_sec = ffiUtil.gettime()
        end

        self:_startAutoStandby()
        logger.info("auto-standby enabled ("..self.auto_standby_sec.."s)")
    else
        logger.info("auto-standby disabled")
        self._startAutoStandby = noop
        self._stopAutoStandby = noop
    end
end

UIManager._resetAutoStandbyTimer = noop

I fixed my issues with these additional hacks:

@@ -1917,6 +1917,7 @@
                 self:_startAutoStandby() -- et on remet l'autostandby
                 -- pour que le handler discard le touch qu'on vient de faire
                 -- pour rallumer (on discard LES events (touch, release))
+                self._now = TimeVal:now()
                 self:discardEvents(true)
             end
         end
@@ -1997,8 +1998,13 @@
                     -- (la, on est sorti du standby)
                     self:_writeToSysFile("/sys/class/rtc/rtc0/wakealarm", "0")
                     self:updateStateStats("active")
+                    self:_startAutoStandby() -- on redemarre l'auto standby
+                    if self.auto_suspend_sec > 0 then
+                        self.auto_suspend_action() -- ça peut stoper l'auto standby
+                    end
+                else
+                    self:_startAutoStandby() -- on redemarre l'auto standby
                 end
-                self:_startAutoStandby() -- on redemarre l'auto standby
             else
                 self:mylog("  scheduling auto_standby_action in " .. (self.last_wakeaction_sec + self.auto_standby_sec - now))
                 self:scheduleIn(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants