16 self.prev=None |
16 self.prev=None |
17 self.name=name |
17 self.name=name |
18 self.cond=cond |
18 self.cond=cond |
19 |
19 |
20 def __lt__(self, other): |
20 def __lt__(self, other): |
21 return False |
21 raise NotImplementedError |
22 |
|
23 def __gt__(self, other): |
|
24 return True |
|
25 |
22 |
26 def insert_after(self, ev): |
23 def insert_after(self, ev): |
27 if not self.next: |
24 if not self.next: |
28 ev.prev=self |
25 ev.prev=self |
29 self.next=ev |
26 self.next=ev |
30 ev.next=None |
27 ev.next=None |
31 elif self.next>ev: |
28 elif ev<self.next: |
32 self.insert_immediately_after(ev) |
29 self.insert_immediately_after(ev) |
33 else: |
30 else: |
34 self.next.insert_after(ev) |
31 self.next.insert_after(ev) |
35 |
32 |
36 def insert_immediately_after(self, ev): |
33 def insert_immediately_after(self, ev): |
48 if p: |
45 if p: |
49 p.next=n |
46 p.next=n |
50 |
47 |
51 class ScheduledEvent(QueuedEvent): |
48 class ScheduledEvent(QueuedEvent): |
52 def __init__(self, when, cond, name=None): |
49 def __init__(self, when, cond, name=None): |
53 super().__init__(cond, name) |
50 super().__init__(cond, name=name) |
54 self.when=when |
51 self.when=when |
55 |
52 |
56 def __lt__(self, other): |
53 def __lt__(self, other): |
57 return self.when < other.when |
54 return self.when < other.when |
58 |
|
59 def __gt__(self, other): |
|
60 return self.when > other.when |
|
61 |
55 |
62 class TerminableThread(Thread): |
56 class TerminableThread(Thread): |
63 def __init__(self, *args, **kwargs): |
57 def __init__(self, *args, **kwargs): |
64 super().__init__(*args, **kwargs) |
58 super().__init__(*args, **kwargs) |
65 self._terminate=False |
59 self._terminate=False |
70 _terminate=True |
64 _terminate=True |
71 self._cond.notify() |
65 self._cond.notify() |
72 |
66 |
73 class QueueThread(TerminableThread): |
67 class QueueThread(TerminableThread): |
74 def __init__(self, *args, **kwargs): |
68 def __init__(self, *args, **kwargs): |
|
69 super().__init__(*args, **kwargs) |
|
70 self.daemon = True |
75 self._list = None |
71 self._list = None |
76 super().__init__(*args, **kwargs) |
|
77 |
72 |
78 def _insert(self, ev): |
73 def _insert(self, ev): |
79 if not self._list: |
74 if not self._list: |
80 self._list=ev |
75 self._list=ev |
81 elif self._list > ev: |
76 elif ev<self._list: |
82 ev.insert_immediately_after(self._list) |
77 ev.insert_immediately_after(self._list) |
83 self._list=ev |
78 self._list=ev |
84 else: |
79 else: |
85 self._list.insert_immediately_after(ev) |
80 self._list.insert_immediately_after(ev) |
86 |
81 |
87 self._cond.notify() |
82 self._cond.notify() |
88 |
83 |
89 def _wait(self, ev): |
84 def _unlink(self, ev): |
90 with self._cond: |
85 if ev==self._list: |
91 self._insert(ev) |
86 self._list=ev.next |
92 # This will release the lock on cond, allowing queue manager (scheduler) |
|
93 # thread to notify us if we are already to be released |
|
94 ev.cond.wait() |
|
95 |
|
96 # If we were woken up by some other event, not the scheduler, |
|
97 # ensure the event is removed |
|
98 with self._cond: |
|
99 if ev==self._list: |
|
100 self._list=ev.next |
|
101 ev.unlink() |
|
102 |
|
103 def _pop(self): |
|
104 ev=self._list |
|
105 logger.info("%s: Found event %s" % self.name(), str(ev.name)) |
|
106 self._list=ev.next |
|
107 ev.unlink() |
87 ev.unlink() |
108 ev.cond.acquire() |
|
109 ev.cond.notifyAll() |
|
110 ev.cond.release() |
|
111 |
88 |
112 |
89 |
113 class Scheduler(QueueThread): |
90 class Scheduler(QueueThread): |
114 # Default to precision of 60 seconds: the scheduler thread will never |
91 # Default to precision of 60 seconds: the scheduler thread will never |
115 # sleep longer than that, to get quickly back on track with the schedule |
92 # sleep longer than that, to get quickly back on track with the schedule |
116 # when the computer wakes up from sleep |
93 # when the computer wakes up from sleep |
117 def __init__(self, precision=60): |
94 def __init__(self, precision=60): |
118 self.precision = precision |
95 self.precision = precision |
119 self._next_event_time = None |
96 self._next_event_time = None |
120 self._list = None |
97 super().__init__(target = self._scheduler_thread, name = 'Scheduler') |
121 super().__init__(target = self.__scheduler_thread, name = 'Scheduler') |
|
122 self.daemon=True |
|
123 |
98 |
124 def __scheduler_thread(self): |
99 def _scheduler_thread(self): |
125 with self._cond: |
100 with self._cond: |
126 while not self._terminate: |
101 while not self._terminate: |
127 now = time.monotonic() |
102 now = time.monotonic() |
128 if not self._list: |
103 if not self._list: |
129 timeout = None |
104 timeout = None |
135 if not timeout or timeout>0: |
110 if not timeout or timeout>0: |
136 self._cond.wait(timeout) |
111 self._cond.wait(timeout) |
137 now = time.monotonic() |
112 now = time.monotonic() |
138 |
113 |
139 while self._list and self._list.when <= now: |
114 while self._list and self._list.when <= now: |
140 self._pop() |
115 ev=self._list |
|
116 logger.info("%s: Scheduling event %s" % self.name(), str(ev.name)) |
|
117 # We are only allowed to remove ev from list when ev.cond allows |
|
118 with ev.cond: |
|
119 self._list=ev.next |
|
120 ev.unlink() |
|
121 ev.cond.notifyAll() |
|
122 |
|
123 def _wait(self, ev): |
|
124 with self._cond: |
|
125 self._insert(ev) |
|
126 |
|
127 # This will release the lock on cond, allowing queue manager (scheduler) |
|
128 # thread to notify us if we are already to be released |
|
129 ev.cond.wait() |
|
130 |
|
131 # If we were woken up by some other event, not the scheduler, |
|
132 # ensure the event is removed |
|
133 with self._cond: |
|
134 self._unlink(ev) |
141 |
135 |
142 # cond has to be acquired on entry! |
136 # cond has to be acquired on entry! |
143 def wait_until(self, when, cond, name=None): |
137 def wait_until(self, when, cond, name=None): |
144 self._wait(ScheduledEvent(when, cond, name)) |
138 self._wait(ScheduledEvent(when, cond, name)) |
145 |
139 |