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