Sat, 27 Jan 2018 12:19:39 +0000
UI refresh fix; added debug messages
49 | 1 | # |
2 | # Scheduler for Borgend | |
3 | # | |
4 | # This module simply provide a way for other threads to until a given time | |
5 | # | |
6 | ||
7 | import time | |
8 | import borgend | |
9 | from threading import Condition, Lock, Thread | |
10 | ||
11 | logger=borgend.logger.getChild(__name__) | |
12 | ||
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
13 | class QueuedEvent: |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
14 | def __init__(self, cond, name=None): |
49 | 15 | self.next=None |
16 | self.prev=None | |
17 | self.name=name | |
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
18 | self.cond=cond |
49 | 19 | |
20 | def __lt__(self, other): | |
54 | 21 | raise NotImplementedError |
49 | 22 | |
23 | def insert_after(self, ev): | |
69
8705e296c7a0
Scheduling list fix and simplifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
59
diff
changeset
|
24 | if not self.next or ev<self.next: |
49 | 25 | self.insert_immediately_after(ev) |
26 | else: | |
27 | self.next.insert_after(ev) | |
28 | ||
29 | def insert_immediately_after(self, ev): | |
69
8705e296c7a0
Scheduling list fix and simplifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
59
diff
changeset
|
30 | assert(ev.next is None and ev.prev is None) |
49 | 31 | ev.prev=self |
32 | ev.next=self.next | |
33 | self.next=ev | |
34 | ||
69
8705e296c7a0
Scheduling list fix and simplifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
59
diff
changeset
|
35 | def insert_immediately_before(self, ev): |
8705e296c7a0
Scheduling list fix and simplifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
59
diff
changeset
|
36 | assert(ev.next is None and ev.prev is None) |
8705e296c7a0
Scheduling list fix and simplifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
59
diff
changeset
|
37 | ev.next=self |
8705e296c7a0
Scheduling list fix and simplifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
59
diff
changeset
|
38 | ev.prev=self.prev |
8705e296c7a0
Scheduling list fix and simplifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
59
diff
changeset
|
39 | self.prev=ev |
8705e296c7a0
Scheduling list fix and simplifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
59
diff
changeset
|
40 | |
49 | 41 | def unlink(self): |
42 | n=self.next | |
43 | p=self.prev | |
44 | if n: | |
45 | n.prev=p | |
46 | if p: | |
47 | p.next=n | |
48 | ||
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
49 | class ScheduledEvent(QueuedEvent): |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
50 | def __init__(self, when, cond, name=None): |
54 | 51 | super().__init__(cond, name=name) |
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
52 | self.when=when |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
53 | |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
54 | def __lt__(self, other): |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
55 | return self.when < other.when |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
56 | |
49 | 57 | class TerminableThread(Thread): |
58 | def __init__(self, *args, **kwargs): | |
59 | super().__init__(*args, **kwargs) | |
60 | self._terminate=False | |
61 | self._cond=Condition() | |
62 | ||
63 | def terminate(self): | |
64 | with self._cond: | |
65 | _terminate=True | |
66 | self._cond.notify() | |
67 | ||
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
68 | class QueueThread(TerminableThread): |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
69 | def __init__(self, *args, **kwargs): |
54 | 70 | super().__init__(*args, **kwargs) |
71 | self.daemon = True | |
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
72 | self._list = None |
49 | 73 | |
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
74 | def _insert(self, ev): |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
75 | if not self._list: |
75
2a44b9649212
UI refresh fix; added debug messages
Tuomo Valkonen <tuomov@iki.fi>
parents:
69
diff
changeset
|
76 | #logger.debug("Insert first") |
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
77 | self._list=ev |
54 | 78 | elif ev<self._list: |
75
2a44b9649212
UI refresh fix; added debug messages
Tuomo Valkonen <tuomov@iki.fi>
parents:
69
diff
changeset
|
79 | #logger.debug("Insert beginning") |
69
8705e296c7a0
Scheduling list fix and simplifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
59
diff
changeset
|
80 | self._list.insert_immediately_before(ev) |
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
81 | self._list=ev |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
82 | else: |
75
2a44b9649212
UI refresh fix; added debug messages
Tuomo Valkonen <tuomov@iki.fi>
parents:
69
diff
changeset
|
83 | #logger.debug("Insert after") |
59
8d0a815022cc
Oops, accidentally calling the wrong function (+log message clarification)
Tuomo Valkonen <tuomov@iki.fi>
parents:
55
diff
changeset
|
84 | self._list.insert_after(ev) |
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
85 | |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
86 | self._cond.notify() |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
87 | |
54 | 88 | def _unlink(self, ev): |
89 | if ev==self._list: | |
90 | self._list=ev.next | |
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
91 | ev.unlink() |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
92 | |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
93 | |
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
94 | class Scheduler(QueueThread): |
49 | 95 | # 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 | |
97 | # when the computer wakes up from sleep | |
98 | def __init__(self, precision=60): | |
99 | self.precision = precision | |
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
100 | self._next_event_time = None |
54 | 101 | super().__init__(target = self._scheduler_thread, name = 'Scheduler') |
49 | 102 | |
54 | 103 | def _scheduler_thread(self): |
75
2a44b9649212
UI refresh fix; added debug messages
Tuomo Valkonen <tuomov@iki.fi>
parents:
69
diff
changeset
|
104 | logger.debug("Scheduler thread started") |
49 | 105 | with self._cond: |
106 | while not self._terminate: | |
107 | now = time.monotonic() | |
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
108 | if not self._list: |
49 | 109 | timeout = None |
110 | else: | |
111 | # Wait at most precision seconds, or until next event if it | |
112 | # comes earlier | |
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
113 | timeout=min(self.precision, self._list.when-now) |
49 | 114 | |
115 | if not timeout or timeout>0: | |
75
2a44b9649212
UI refresh fix; added debug messages
Tuomo Valkonen <tuomov@iki.fi>
parents:
69
diff
changeset
|
116 | logger.debug("Scheduler waiting %d seconds" % (timeout or (-1))) |
49 | 117 | self._cond.wait(timeout) |
118 | now = time.monotonic() | |
119 | ||
75
2a44b9649212
UI refresh fix; added debug messages
Tuomo Valkonen <tuomov@iki.fi>
parents:
69
diff
changeset
|
120 | logger.debug("Scheduler timed out") |
2a44b9649212
UI refresh fix; added debug messages
Tuomo Valkonen <tuomov@iki.fi>
parents:
69
diff
changeset
|
121 | |
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
122 | while self._list and self._list.when <= now: |
54 | 123 | ev=self._list |
59
8d0a815022cc
Oops, accidentally calling the wrong function (+log message clarification)
Tuomo Valkonen <tuomov@iki.fi>
parents:
55
diff
changeset
|
124 | logger.debug("Scheduler activating %s" % (ev.name or "(unknown)")) |
54 | 125 | # We are only allowed to remove ev from list when ev.cond allows |
126 | with ev.cond: | |
127 | self._list=ev.next | |
128 | ev.unlink() | |
129 | ev.cond.notifyAll() | |
130 | ||
131 | def _wait(self, ev): | |
132 | with self._cond: | |
133 | self._insert(ev) | |
134 | ||
135 | # This will release the lock on cond, allowing queue manager (scheduler) | |
136 | # thread to notify us if we are already to be released | |
137 | ev.cond.wait() | |
138 | ||
139 | # If we were woken up by some other event, not the scheduler, | |
140 | # ensure the event is removed | |
141 | with self._cond: | |
142 | self._unlink(ev) | |
49 | 143 | |
144 | # cond has to be acquired on entry! | |
145 | def wait_until(self, when, cond, name=None): | |
75
2a44b9649212
UI refresh fix; added debug messages
Tuomo Valkonen <tuomov@iki.fi>
parents:
69
diff
changeset
|
146 | logger.debug("Scheduling '%s' at %d" % (name, when)) |
53
442c558bd632
Generalisation of scheduler thread to general queue threads
Tuomo Valkonen <tuomov@iki.fi>
parents:
49
diff
changeset
|
147 | self._wait(ScheduledEvent(when, cond, name)) |
49 | 148 |