|
1 | | -# Module sched -- a generally useful event scheduler class |
2 | | - |
3 | | -# Each instance of this class manages its own queue. |
4 | | -# No multi-threading is implied; you are supposed to hack that |
5 | | -# yourself, or use a single instance per application. |
6 | | -# |
7 | | -# Each instance is parametrized with two functions, one that is |
8 | | -# supposed to return the current time, one that is supposed to |
9 | | -# implement a delay. You can implement real-time scheduling by |
10 | | -# substituting time and sleep from built-in module time, or you can |
11 | | -# implement simulated time by writing your own functions. This can |
12 | | -# also be used to integrate scheduling with STDWIN events; the delay |
13 | | -# function is allowed to modify the queue. Time can be expressed as |
14 | | -# integers or floating point numbers, as long as it is consistent. |
15 | | - |
16 | | -# Events are specified by tuples (time, priority, action, argument). |
17 | | -# As in UNIX, lower priority numbers mean higher priority; in this |
18 | | -# way the queue can be maintained fully sorted. Execution of the |
19 | | -# event means calling the action function, passing it the argument. |
20 | | -# Remember that in Python, multiple function arguments can be packed |
21 | | -# in a tuple. The action function may be an instance method so it |
22 | | -# has another way to reference private data (besides global variables). |
23 | | -# Parameterless functions or methods cannot be used, however. |
| 1 | +"""Module sched -- a generally useful event scheduler class |
| 2 | +
|
| 3 | +Each instance of this class manages its own queue. |
| 4 | +No multi-threading is implied; you are supposed to hack that |
| 5 | +yourself, or use a single instance per application. |
| 6 | +
|
| 7 | +Each instance is parametrized with two functions, one that is |
| 8 | +supposed to return the current time, one that is supposed to |
| 9 | +implement a delay. You can implement real-time scheduling by |
| 10 | +substituting time and sleep from built-in module time, or you can |
| 11 | +implement simulated time by writing your own functions. This can |
| 12 | +also be used to integrate scheduling with STDWIN events; the delay |
| 13 | +function is allowed to modify the queue. Time can be expressed as |
| 14 | +integers or floating point numbers, as long as it is consistent. |
| 15 | +
|
| 16 | +Events are specified by tuples (time, priority, action, argument). |
| 17 | +As in UNIX, lower priority numbers mean higher priority; in this |
| 18 | +way the queue can be maintained fully sorted. Execution of the |
| 19 | +event means calling the action function, passing it the argument. |
| 20 | +Remember that in Python, multiple function arguments can be packed |
| 21 | +in a tuple. The action function may be an instance method so it |
| 22 | +has another way to reference private data (besides global variables). |
| 23 | +Parameterless functions or methods cannot be used, however. |
| 24 | +""" |
24 | 25 |
|
25 | 26 | # XXX The timefunc and delayfunc should have been defined as methods |
26 | 27 | # XXX so you can define new kinds of schedulers using subclassing |
27 | 28 | # XXX instead of having to define a module or class just to hold |
28 | | -# XXX the global state of your particular time and delay functtions. |
| 29 | +# XXX the global state of your particular time and delay functions. |
29 | 30 |
|
30 | 31 | import bisect |
31 | 32 |
|
32 | 33 | class scheduler: |
33 | | - # |
34 | | - # Initialize a new instance, passing the time and delay functions |
35 | | - # |
36 | | - def __init__(self, timefunc, delayfunc): |
37 | | - self.queue = [] |
38 | | - self.timefunc = timefunc |
39 | | - self.delayfunc = delayfunc |
40 | | - # |
41 | | - # Enter a new event in the queue at an absolute time. |
42 | | - # Returns an ID for the event which can be used |
43 | | - # to remove it, if necessary. |
44 | | - # |
45 | | - def enterabs(self, time, priority, action, argument): |
46 | | - event = time, priority, action, argument |
47 | | - bisect.insort(self.queue, event) |
48 | | - return event # The ID |
49 | | - # |
50 | | - # A variant that specifies the time as a relative time. |
51 | | - # This is actually the more commonly used interface. |
52 | | - # |
53 | | - def enter(self, delay, priority, action, argument): |
54 | | - time = self.timefunc() + delay |
55 | | - return self.enterabs(time, priority, action, argument) |
56 | | - # |
57 | | - # Remove an event from the queue. |
58 | | - # This must be presented the ID as returned by enter(). |
59 | | - # If the event is not in the queue, this raises RuntimeError. |
60 | | - # |
61 | | - def cancel(self, event): |
62 | | - self.queue.remove(event) |
63 | | - # |
64 | | - # Check whether the queue is empty. |
65 | | - # |
66 | | - def empty(self): |
67 | | - return len(self.queue) == 0 |
68 | | - # |
69 | | - # Run: execute events until the queue is empty. |
70 | | - # |
71 | | - # When there is a positive delay until the first event, the |
72 | | - # delay function is called and the event is left in the queue; |
73 | | - # otherwise, the event is removed from the queue and executed |
74 | | - # (its action function is called, passing it the argument). |
75 | | - # If the delay function returns prematurely, it is simply |
76 | | - # restarted. |
77 | | - # |
78 | | - # It is legal for both the delay function and the action |
79 | | - # function to to modify the queue or to raise an exception; |
80 | | - # exceptions are not caught but the scheduler's state |
81 | | - # remains well-defined so run() may be called again. |
82 | | - # |
83 | | - # A questionably hack is added to allow other threads to run: |
84 | | - # just after an event is executed, a delay of 0 is executed, |
85 | | - # to avoid monopolizing the CPU when other threads are also |
86 | | - # runnable. |
87 | | - # |
88 | | - def run(self): |
89 | | - q = self.queue |
90 | | - while q: |
91 | | - time, priority, action, argument = q[0] |
92 | | - now = self.timefunc() |
93 | | - if now < time: |
94 | | - self.delayfunc(time - now) |
95 | | - else: |
96 | | - del q[0] |
97 | | - void = apply(action, argument) |
98 | | - self.delayfunc(0) # Let other threads run |
99 | | - # |
| 34 | + def __init__(self, timefunc, delayfunc): |
| 35 | + """Initialize a new instance, passing the time and delay |
| 36 | + functions""" |
| 37 | + self.queue = [] |
| 38 | + self.timefunc = timefunc |
| 39 | + self.delayfunc = delayfunc |
| 40 | + |
| 41 | + def enterabs(self, time, priority, action, argument): |
| 42 | + """Enter a new event in the queue at an absolute time. |
| 43 | +
|
| 44 | + Returns an ID for the event which can be used to remove it, |
| 45 | + if necessary. |
| 46 | +
|
| 47 | + """ |
| 48 | + event = time, priority, action, argument |
| 49 | + bisect.insort(self.queue, event) |
| 50 | + return event # The ID |
| 51 | + |
| 52 | + def enter(self, delay, priority, action, argument): |
| 53 | + """A variant that specifies the time as a relative time. |
| 54 | +
|
| 55 | + This is actually the more commonly used interface. |
| 56 | +
|
| 57 | + """ |
| 58 | + time = self.timefunc() + delay |
| 59 | + return self.enterabs(time, priority, action, argument) |
| 60 | + |
| 61 | + def cancel(self, event): |
| 62 | + """Remove an event from the queue. |
| 63 | +
|
| 64 | + This must be presented the ID as returned by enter(). |
| 65 | + If the event is not in the queue, this raises RuntimeError. |
| 66 | +
|
| 67 | + """ |
| 68 | + self.queue.remove(event) |
| 69 | + |
| 70 | + def empty(self): |
| 71 | + """Check whether the queue is empty.""" |
| 72 | + return len(self.queue) == 0 |
| 73 | + |
| 74 | + def run(self): |
| 75 | + """Execute events until the queue is empty. |
| 76 | + |
| 77 | + When there is a positive delay until the first event, the |
| 78 | + delay function is called and the event is left in the queue; |
| 79 | + otherwise, the event is removed from the queue and executed |
| 80 | + (its action function is called, passing it the argument). If |
| 81 | + the delay function returns prematurely, it is simply |
| 82 | + restarted. |
| 83 | +
|
| 84 | + It is legal for both the delay function and the action |
| 85 | + function to to modify the queue or to raise an exception; |
| 86 | + exceptions are not caught but the scheduler's state remains |
| 87 | + well-defined so run() may be called again. |
| 88 | +
|
| 89 | + A questionably hack is added to allow other threads to run: |
| 90 | + just after an event is executed, a delay of 0 is executed, to |
| 91 | + avoid monopolizing the CPU when other threads are also |
| 92 | + runnable. |
| 93 | +
|
| 94 | + """ |
| 95 | + q = self.queue |
| 96 | + while q: |
| 97 | + time, priority, action, argument = q[0] |
| 98 | + now = self.timefunc() |
| 99 | + if now < time: |
| 100 | + self.delayfunc(time - now) |
| 101 | + else: |
| 102 | + del q[0] |
| 103 | + void = apply(action, argument) |
| 104 | + self.delayfunc(0) # Let other threads run |
0 commit comments