Fri, 26 Jan 2018 09:27:07 +0000
Scheduling list fix and simplifications
| 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: 
49diff
changeset | 13 | class QueuedEvent: | 
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
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: 
49diff
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: 
59diff
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: 
59diff
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: 
59diff
changeset | 35 | def insert_immediately_before(self, ev): | 
| 
8705e296c7a0
Scheduling list fix and simplifications
 Tuomo Valkonen <tuomov@iki.fi> parents: 
59diff
changeset | 36 | assert(ev.next is None and ev.prev is None) | 
| 
8705e296c7a0
Scheduling list fix and simplifications
 Tuomo Valkonen <tuomov@iki.fi> parents: 
59diff
changeset | 37 | ev.next=self | 
| 
8705e296c7a0
Scheduling list fix and simplifications
 Tuomo Valkonen <tuomov@iki.fi> parents: 
59diff
changeset | 38 | ev.prev=self.prev | 
| 
8705e296c7a0
Scheduling list fix and simplifications
 Tuomo Valkonen <tuomov@iki.fi> parents: 
59diff
changeset | 39 | self.prev=ev | 
| 
8705e296c7a0
Scheduling list fix and simplifications
 Tuomo Valkonen <tuomov@iki.fi> parents: 
59diff
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: 
49diff
changeset | 49 | class ScheduledEvent(QueuedEvent): | 
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
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: 
49diff
changeset | 52 | self.when=when | 
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 53 | |
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 54 | def __lt__(self, other): | 
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 55 | return self.when < other.when | 
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
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: 
49diff
changeset | 68 | class QueueThread(TerminableThread): | 
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
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: 
49diff
changeset | 72 | self._list = None | 
| 49 | 73 | |
| 53 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 74 | def _insert(self, ev): | 
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 75 | if not self._list: | 
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 76 | self._list=ev | 
| 54 | 77 | elif ev<self._list: | 
| 69 
8705e296c7a0
Scheduling list fix and simplifications
 Tuomo Valkonen <tuomov@iki.fi> parents: 
59diff
changeset | 78 | self._list.insert_immediately_before(ev) | 
| 53 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 79 | self._list=ev | 
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 80 | else: | 
| 59 
8d0a815022cc
Oops, accidentally calling the wrong function (+log message clarification)
 Tuomo Valkonen <tuomov@iki.fi> parents: 
55diff
changeset | 81 | self._list.insert_after(ev) | 
| 53 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 82 | |
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 83 | self._cond.notify() | 
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 84 | |
| 54 | 85 | def _unlink(self, ev): | 
| 86 | if ev==self._list: | |
| 87 | self._list=ev.next | |
| 53 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 88 | ev.unlink() | 
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 89 | |
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 90 | |
| 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 91 | class Scheduler(QueueThread): | 
| 49 | 92 | # Default to precision of 60 seconds: the scheduler thread will never | 
| 93 | # sleep longer than that, to get quickly back on track with the schedule | |
| 94 | # when the computer wakes up from sleep | |
| 95 | def __init__(self, precision=60): | |
| 96 | self.precision = precision | |
| 53 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 97 | self._next_event_time = None | 
| 54 | 98 | super().__init__(target = self._scheduler_thread, name = 'Scheduler') | 
| 49 | 99 | |
| 54 | 100 | def _scheduler_thread(self): | 
| 49 | 101 | with self._cond: | 
| 102 | while not self._terminate: | |
| 103 | now = time.monotonic() | |
| 53 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 104 | if not self._list: | 
| 49 | 105 | timeout = None | 
| 106 | else: | |
| 107 | # Wait at most precision seconds, or until next event if it | |
| 108 | # comes earlier | |
| 53 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 109 | timeout=min(self.precision, self._list.when-now) | 
| 49 | 110 | |
| 111 | if not timeout or timeout>0: | |
| 112 | self._cond.wait(timeout) | |
| 113 | now = time.monotonic() | |
| 114 | ||
| 53 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 115 | while self._list and self._list.when <= now: | 
| 54 | 116 | ev=self._list | 
| 59 
8d0a815022cc
Oops, accidentally calling the wrong function (+log message clarification)
 Tuomo Valkonen <tuomov@iki.fi> parents: 
55diff
changeset | 117 | logger.debug("Scheduler activating %s" % (ev.name or "(unknown)")) | 
| 54 | 118 | # We are only allowed to remove ev from list when ev.cond allows | 
| 119 | with ev.cond: | |
| 120 | self._list=ev.next | |
| 121 | ev.unlink() | |
| 122 | ev.cond.notifyAll() | |
| 123 | ||
| 124 | def _wait(self, ev): | |
| 125 | with self._cond: | |
| 126 | self._insert(ev) | |
| 127 | ||
| 128 | # This will release the lock on cond, allowing queue manager (scheduler) | |
| 129 | # thread to notify us if we are already to be released | |
| 130 | ev.cond.wait() | |
| 131 | ||
| 132 | # If we were woken up by some other event, not the scheduler, | |
| 133 | # ensure the event is removed | |
| 134 | with self._cond: | |
| 135 | self._unlink(ev) | |
| 49 | 136 | |
| 137 | # cond has to be acquired on entry! | |
| 138 | def wait_until(self, when, cond, name=None): | |
| 53 
442c558bd632
Generalisation of scheduler thread to general queue threads
 Tuomo Valkonen <tuomov@iki.fi> parents: 
49diff
changeset | 139 | self._wait(ScheduledEvent(when, cond, name)) | 
| 49 | 140 |