Sun, 28 Jan 2018 00:11:18 +0000
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()))