43 p=self.prev |
44 p=self.prev |
44 if n: |
45 if n: |
45 n.prev=p |
46 n.prev=p |
46 if p: |
47 if p: |
47 p.next=n |
48 p.next=n |
|
49 self.next=None |
|
50 self.prev=None |
48 |
51 |
49 class ScheduledEvent(QueuedEvent): |
52 class ScheduledEvent(QueuedEvent): |
|
53 #@accepts(ScheduledEvent, sleep.Time, threading.Cond, str) |
50 def __init__(self, when, cond, name=None): |
54 def __init__(self, when, cond, name=None): |
51 super().__init__(cond, name=name) |
55 super().__init__(cond, name=name) |
52 self.when=when |
56 self.when=when |
53 |
57 |
54 def __lt__(self, other): |
58 def __lt__(self, other): |
88 def _unlink(self, ev): |
92 def _unlink(self, ev): |
89 if ev==self._list: |
93 if ev==self._list: |
90 self._list=ev.next |
94 self._list=ev.next |
91 ev.unlink() |
95 ev.unlink() |
92 |
96 |
|
97 def _resort(self): |
|
98 oldlist=self._list |
|
99 self._list=None |
|
100 while oldlist: |
|
101 ev=oldlist |
|
102 oldlist=oldlist.next |
|
103 ev.unlink() |
|
104 self._insert(ev) |
|
105 |
|
106 |
93 |
107 |
94 class Scheduler(QueueThread): |
108 class Scheduler(QueueThread): |
95 # Default to precision of 60 seconds: the scheduler thread will never |
109 # Default to precision of 60 seconds: the scheduler thread will never |
96 # sleep longer than that, to get quickly back on track with the schedule |
110 # sleep longer than that, to get quickly back on track with the schedule |
97 # when the computer wakes up from sleep |
111 # when the computer wakes up from sleep |
98 def __init__(self, precision=60): |
112 def __init__(self, precision=60): |
99 self.precision = precision |
113 self.precision = precision |
100 self._next_event_time = None |
114 self._next_event_time = None |
101 super().__init__(target = self._scheduler_thread, name = 'Scheduler') |
115 super().__init__(target = self._scheduler_thread, name = 'Scheduler') |
|
116 sleep.add_callback(self, self._wakeup_callback) |
102 |
117 |
103 def _scheduler_thread(self): |
118 def _scheduler_thread(self): |
104 logger.debug("Scheduler thread started") |
119 logger.debug("Scheduler thread started") |
105 with self._cond: |
120 with self._cond: |
106 while not self._terminate: |
121 while not self._terminate: |
108 if not self._list: |
123 if not self._list: |
109 timeout = None |
124 timeout = None |
110 else: |
125 else: |
111 # Wait at most precision seconds, or until next event if it |
126 # Wait at most precision seconds, or until next event if it |
112 # comes earlier |
127 # comes earlier |
113 timeout=min(self.precision, self._list.when-now) |
128 timeout=min(self.precision, self._list.when.realtime()-now) |
114 |
129 |
115 if not timeout or timeout>0: |
130 if not timeout or timeout>0: |
116 logger.debug("Scheduler waiting %d seconds" % (timeout or (-1))) |
131 logger.debug("Scheduler waiting %d seconds" % (timeout or (-1))) |
117 self._cond.wait(timeout) |
132 self._cond.wait(timeout) |
118 now = time.monotonic() |
133 now = time.monotonic() |
119 |
134 |
120 logger.debug("Scheduler timed out") |
135 logger.debug("Scheduler timed out") |
121 |
136 |
122 while self._list and self._list.when <= now: |
137 while self._list and self._list.when.monotonic() <= now: |
123 ev=self._list |
138 ev=self._list |
124 logger.debug("Scheduler activating %s" % (ev.name or "(unknown)")) |
139 logger.debug("Scheduler activating %s" % (ev.name or "(unknown)")) |
125 # We are only allowed to remove ev from list when ev.cond allows |
140 # We are only allowed to remove ev from list when ev.cond allows |
126 with ev.cond: |
141 with ev.cond: |
127 self._list=ev.next |
142 self._list=ev.next |
128 ev.unlink() |
143 ev.unlink() |
129 ev.cond.notifyAll() |
144 ev.cond.notifyAll() |
|
145 |
|
146 def _wakeup_callback(self): |
|
147 logger.debug("Rescheduling events after wakeup") |
|
148 with self._cond: |
|
149 self._resort() |
130 |
150 |
131 def _wait(self, ev): |
151 def _wait(self, ev): |
132 with self._cond: |
152 with self._cond: |
133 self._insert(ev) |
153 self._insert(ev) |
134 |
154 |
141 with self._cond: |
161 with self._cond: |
142 self._unlink(ev) |
162 self._unlink(ev) |
143 |
163 |
144 # cond has to be acquired on entry! |
164 # cond has to be acquired on entry! |
145 def wait_until(self, when, cond, name=None): |
165 def wait_until(self, when, cond, name=None): |
146 logger.debug("Scheduling '%s' at %d" % (name, when)) |
166 logger.debug("Scheduling '%s' in %s seconds [%s]" % |
|
167 (name, when.seconds_to(), when.__class__.__name__)) |
147 self._wait(ScheduledEvent(when, cond, name)) |
168 self._wait(ScheduledEvent(when, cond, name)) |
148 |
169 |