sleep.py

Sun, 28 Jan 2018 00:11:18 +0000

author
Tuomo Valkonen <tuomov@iki.fi>
date
Sun, 28 Jan 2018 00:11:18 +0000
changeset 76
4b08fca3ce34
parent 69
8705e296c7a0
child 77
e8773133bf79
permissions
-rw-r--r--

Dreamtime scheduling: discount system sleep periods

#
# Wake/sleep detection for scheduling adjustments
#

import Foundation
import AppKit
import platform
import time
import threading
import borgend
import weakref
import datetime

logger=borgend.logger.getChild(__name__)

_dreamtime_monitor=[None]

#
# Support classes for dealing with different times
#

def dreamtime():
    if _dreamtime_monitor[0]:
        return max(0, time.monotonic()-_dreamtime_monitor[0].diff())
    else:
        return time.monotonic()

class Time:
    def realtime(self):
        raise NotImplementedError

    def monotonic(self):
        raise NotImplementedError

    @staticmethod
    def _now():
        raise NotImplementedError

    @classmethod
    def now(cls):
        return cls(cls._now())

    @classmethod
    def from_realtime(cls, realtime):
        return cls(realtime-time.time()+cls._now())

    @classmethod
    def from_monotonic(cls, monotonic):
        return cls(monotonic-time.monotonic()+cls._now())

    @classmethod
    def after(cls, seconds):
        return cls(cls._now()+seconds)

    def datetime(self):
        return datetime.datetime.fromtimestamp(self.realtime())

    def seconds_to(self):
        return self._value-self._now()

    def __lt__(self, other):
        return self.monotonic() < other.monotonic()

    def __gt__(self, other):
        return self.monotonic() > other.monotonic()

    def __le__(self, other):
        return self.monotonic() <= other.monotonic()

    def __ge__(self, other):
        return self.monotonic() >= other.monotonic()

    def __eq__(self, other):
        return self.monotonic() == other.realtime()

class RealTime(Time):
    def __init__(self, when):
        self._value=when

    def realtime(self):
        return self._value

    def monotonic(self):
        return self._value+(time.monotonic()-time.time())

    @staticmethod
    def _now():
        return time.time()

class MonotonicTime(Time):
    def __init__(self, when):
        self._value=when

    def realtime(self):
        return self._value+(time.time()-time.monotonic())

    def monotonic(self):
        return self._value

    @staticmethod
    def _now():
        return time.monotonic()

class DreamTime(Time):
    def __init__(self, when):
        self._value=when

    def realtime(self):
        return self._value+(time.time()-dreamtime())

    def monotonic(self):
        return self._value+(time.monotonic()-dreamtime())

    @staticmethod
    def _now():
        return dreamtime()

#
# Wake up / sleep handling
#

class SleepHandler(Foundation.NSObject):
    """ Handle wake/sleep notifications """

    def init(self):
        self.__sleeptime=None
        self.__slept=0
        self.__epoch=time.monotonic()
        self._lock=threading.Lock()
        self._callbacks=weakref.WeakKeyDictionary()

        return self

    def handleSleepNotification_(self, aNotification):
        logger.info("System going to sleep")
        now=time.monotonic()
        with self._lock:
            self.__sleeptime=now

    def handleWakeNotification_(self, aNotification):
        logger.info("System waking up from sleep")
        try:
            now=time.monotonic()
            with self._lock:
                if self.__sleeptime:
                    slept=max(0, now-self.__sleeptime)
                    logger.info("Slept %f seconds" % slept)
                    self.__slept=self.__slept+slept
                    self.__sleeptime=None
                callbacks=self._callbacks.copy()
        except:
            logger.exception("Bug in wakeup handler")

        for callback in callbacks.values():
            try:
                callback()
            except Exception:
                logger.exception("Error in wake notification callback")

    # Return difference to time.monotonic()
    def diff(self):
        with self._lock:
            diff=self.__epoch+self.__slept
        return diff

    # Obj-C hates this
    # def add_callback(self, obj, callback):
    #     with self.lock:
    #         self.__callbacks[obj]=callback

# obj is to use a a key in a weak key dictionary
def add_callback(obj, callback):
    monitor=_dreamtime_monitor[0]
    if not monitor:
        raise Exception("Dreamtime monitor not started")
    else:
        #monitor.add_my_callback(obj, callback)
        with monitor._lock:
            monitor._callbacks[obj]=callback

def start_monitoring():
    if platform.system()=='Darwin':
        logger.debug("Starting to monitor system sleep")
        workspace = AppKit.NSWorkspace.sharedWorkspace()
        notification_center = workspace.notificationCenter()
        _dreamtime_monitor[0] = SleepHandler.new()

        notification_center.addObserver_selector_name_object_(
            _dreamtime_monitor[0],
            "handleSleepNotification:",
            AppKit.NSWorkspaceWillSleepNotification,
            None)

        notification_center.addObserver_selector_name_object_(
            _dreamtime_monitor[0],
            "handleWakeNotification:",
            AppKit.NSWorkspaceDidWakeNotification,
            None)
    else:
        logger.warning(("No system sleep monitor implemented for '%s'"
                       % platform.system()))

mercurial