borgend/dreamtime.py

changeset 100
b141bed9e718
parent 97
96d5adbe0205
child 101
3068b0de12ee
equal deleted inserted replaced
99:281bab8361c8 100:b141bed9e718
17 17
18 # 18 #
19 # Support classes for dealing with different times 19 # Support classes for dealing with different times
20 # 20 #
21 21
22 # Return difference (delay) of "dreamtime" to monotonic time 22 # Time snapshotting to helps to create stable comparisons of different
23 def dreamtime_difference(): 23 # subclasses of Time.
24 if _dreamtime_monitor: 24 class Snapshot:
25 return _dreamtime_monitor.diff() 25 def __init__(self):
26 else: 26 self._monotonic=None
27 return time.monotonic() 27 self._realtime=None
28 28 self._dreamtime=None
29 # Return "dreamtime" 29
30 def dreamtime(): 30 def monotonic(self):
31 return max(0, time.monotonic()-dreamtime_difference()) 31 if self._monotonic is None:
32 32 self._monotonic=time.monotonic()
33 return self._monotonic
34
35 def realtime(self):
36 if self._realtime is None:
37 self._realtime=time.time()
38 return self._realtime
39
40 def dreamtime(self):
41 if self._dreamtime is None:
42 if _dreamtime_monitor:
43 self._dreamtime=_dreamtime_monitor.dreamtime(snapshot=self)
44 else:
45 self._dreamtime=self.monotonic()
46 return self._dreamtime
47
48 # The main Time class, for time advancing in various paces
33 class Time: 49 class Time:
34 def __init__(self, when): 50 def __init__(self, when):
35 self._value=when 51 self._value=when
36 52
37 def realtime(self): 53 def _monotonic(self, snapshot):
38 raise NotImplementedError 54 raise NotImplementedError
39 55
40 def monotonic(self): 56 def _realtime(self, snapshot):
41 raise NotImplementedError 57 raise NotImplementedError
42 58
43 @staticmethod 59 @staticmethod
44 def _now(): 60 def _now(snapshot):
45 raise NotImplementedError 61 raise NotImplementedError
46 62
47 @classmethod 63 @classmethod
48 def now(cls): 64 def now(cls):
49 return cls(cls._now()) 65 return cls(cls._now(Snapshot()))
50 66
51 @classmethod 67 @classmethod
52 def from_realtime(cls, realtime): 68 def from_realtime(cls, realtime, snapshot=Snapshot()):
53 return cls(realtime-time.time()+cls._now()) 69 return cls(realtime-snapshot.realtime()+cls._now(snapshot))
54 70
55 @classmethod 71 @classmethod
56 def from_monotonic(cls, monotonic): 72 def from_monotonic(cls, monotonic, snapshot=Snapshot()):
57 return cls(monotonic-time.monotonic()+cls._now()) 73 return cls(monotonic-snapshot.monotonic()+cls._now(snapshot))
58 74
59 @classmethod 75 @classmethod
60 def after(cls, seconds): 76 def after(cls, seconds, snapshot=Snapshot()):
61 return cls(cls._now()+seconds) 77 return cls(cls._now(snapshot)+seconds)
62 78
63 @classmethod 79 @classmethod
64 def after_other(cls, other, seconds): 80 def after_other(cls, other, seconds, snapshot=Snapshot()):
65 if isinstance(other, cls): 81 if isinstance(other, cls):
66 return cls(other._value+seconds) 82 return cls(other._value+seconds)
67 else: 83 else:
68 return cls.from_monotonic(other.monotonic()+seconds) 84 return cls.from_monotonic(other._monotonic(snapshot)+seconds,
85 snapshot)
69 86
70 def datetime(self): 87 def datetime(self):
71 return datetime.datetime.fromtimestamp(self.realtime()) 88 return datetime.datetime.fromtimestamp(self.realtime())
72 89
73 def seconds_to(self): 90 def seconds_to(self):
74 return self._value-self._now() 91 return self._value-self._now(Snapshot())
75 92
76 def isoformat(self): 93 def isoformat(self):
77 return self.datetime().isoformat() 94 return self.datetime().isoformat()
78 95
96 def realtime(self):
97 return self._realtime(Snapshot())
98
99 def monotonic(self):
100 return self._monotonic(Snapshot())
101
102 def __compare(self, other, fn):
103 if isinstance(other, self.__class__):
104 return fn(self._value, other._value)
105 else:
106 snapshot=Snapshot()
107 return fn(self._monotonic(snapshot), other._monotonic(snapshot))
108
79 def __lt__(self, other): 109 def __lt__(self, other):
80 return self.monotonic() < other.monotonic() 110 return self.__compare(other, lambda x, y: x < y)
81 111
82 def __gt__(self, other): 112 def __gt__(self, other):
83 return self.monotonic() > other.monotonic() 113 return self.__compare(other, lambda x, y: x > y)
84 114
85 def __le__(self, other): 115 def __le__(self, other):
86 return self.monotonic() <= other.monotonic() 116 return self.__compare(other, lambda x, y: x <= y)
87 117
88 def __ge__(self, other): 118 def __ge__(self, other):
89 return self.monotonic() >= other.monotonic() 119 return self.__compare(other, lambda x, y: x >= y)
90 120
91 def __eq__(self, other): 121 def __eq__(self, other):
92 return self.monotonic() == other.realtime() 122 return self.__compare(other, lambda x, y: x == y)
93 123
94 class RealTime(Time): 124 class RealTime(Time):
95 def realtime(self): 125 def _realtime(self, snapshot):
96 return self._value 126 return self._value
97 127
98 def monotonic(self): 128 def _monotonic(self, snapshot):
99 return self._value+(time.monotonic()-time.time()) 129 return self._value+(snapshot.monotonic()-snapshot.realtime())
100 130
101 @staticmethod 131 @staticmethod
102 def _now(): 132 def _now(snapshot):
103 return time.time() 133 return snapshot.realtime()
104 134
105 class MonotonicTime(Time): 135 class MonotonicTime(Time):
106 def realtime(self): 136 def _realtime(self, snapshot):
107 return self._value+(time.time()-time.monotonic()) 137 return self._value+(snapshot.realtime()-snapshot.monotonic())
108 138
109 def monotonic(self): 139 def _monotonic(self, snapshot):
110 return self._value 140 return self._value
111 141
112 @staticmethod 142 @staticmethod
113 def _now(): 143 def _now(snapshot):
114 return time.monotonic() 144 return snapshot.monotonic()
115 145
116 class DreamTime(Time): 146 class DreamTime(Time):
117 def realtime(self): 147 def _realtime(self, snapshot):
118 return self._value+(time.time()-dreamtime()) 148 return self._value+(snapshot.realtime()-snapshot.dreamtime())
119 149
120 # Important: monotonic is "static" within a wakeup period 150 # Important: monotonic is "static" within a wakeup period
121 # and does not need to call time.monotonic(), as it gets compared 151 # and does not need to call time.monotonic(), as it gets compared
122 # to a specific time.monotonic() realisation 152 # to a specific time.monotonic() realisation
123 def monotonic(self): 153 def _monotonic(self, snapshot):
124 return self._value+dreamtime_difference() 154 return self._value+(snapshot.monotonic()-snapshot.dreamtime())
125 155
126 @staticmethod 156 @staticmethod
127 def _now(): 157 def _now(snapshot):
128 return dreamtime() 158 return snapshot.dreamtime()
129 159
130 160
131 if platform.system()=='Darwin': 161 if platform.system()=='Darwin':
132 162
133 import Foundation 163 import Foundation
173 try: 203 try:
174 callback() 204 callback()
175 except Exception: 205 except Exception:
176 logger.exception("Error in wake notification callback") 206 logger.exception("Error in wake notification callback")
177 207
178 # Return difference to time.monotonic() 208 # Return dreamtime
179 def diff(self): 209 def dreamtime(self, snapshot=Snapshot()):
180 with self.__lock: 210 with self.__lock:
181 diff=self.__epoch+self.__slept 211 # macOS "sleep" signals / status are complete bollocks: at least
182 return diff 212 # when plugged in, the system is actually sometimes running all
213 # night with the lid closed and sleepNotification delivered.
214 # Therefore, we need our timers to work in a sane manner when
215 # we should be sleeping!
216 if self.__sleeptime is not None:
217 now_monotonic=self.__sleeptime
218 else:
219 now_monotonic=snapshot.monotonic()
220 now_dreamtime=max(0, now_monotonic-self.__epoch-self.__slept)
221 return now_dreamtime
183 222
184 # Weirdo (Py)ObjC naming to stop it form choking up 223 # Weirdo (Py)ObjC naming to stop it form choking up
185 def addForObj_aCallback_(self, obj, callback): 224 def addForObj_aCallback_(self, obj, callback):
186 with self.__lock: 225 with self.__lock:
187 self.__callbacks[obj]=callback 226 self.__callbacks[obj]=callback

mercurial