diff --git a/Lib/sched.py b/Lib/sched.py index 60b0a1bacfd..147977cfe9b 100644 --- a/Lib/sched.py +++ b/Lib/sched.py @@ -1,99 +1,104 @@ -# Module sched -- a generally useful event scheduler class +"""Module sched -- a generally useful event scheduler class -# Each instance of this class manages its own queue. -# No multi-threading is implied; you are supposed to hack that -# yourself, or use a single instance per application. -# -# Each instance is parametrized with two functions, one that is -# supposed to return the current time, one that is supposed to -# implement a delay. You can implement real-time scheduling by -# substituting time and sleep from built-in module time, or you can -# implement simulated time by writing your own functions. This can -# also be used to integrate scheduling with STDWIN events; the delay -# function is allowed to modify the queue. Time can be expressed as -# integers or floating point numbers, as long as it is consistent. +Each instance of this class manages its own queue. +No multi-threading is implied; you are supposed to hack that +yourself, or use a single instance per application. -# Events are specified by tuples (time, priority, action, argument). -# As in UNIX, lower priority numbers mean higher priority; in this -# way the queue can be maintained fully sorted. Execution of the -# event means calling the action function, passing it the argument. -# Remember that in Python, multiple function arguments can be packed -# in a tuple. The action function may be an instance method so it -# has another way to reference private data (besides global variables). -# Parameterless functions or methods cannot be used, however. +Each instance is parametrized with two functions, one that is +supposed to return the current time, one that is supposed to +implement a delay. You can implement real-time scheduling by +substituting time and sleep from built-in module time, or you can +implement simulated time by writing your own functions. This can +also be used to integrate scheduling with STDWIN events; the delay +function is allowed to modify the queue. Time can be expressed as +integers or floating point numbers, as long as it is consistent. + +Events are specified by tuples (time, priority, action, argument). +As in UNIX, lower priority numbers mean higher priority; in this +way the queue can be maintained fully sorted. Execution of the +event means calling the action function, passing it the argument. +Remember that in Python, multiple function arguments can be packed +in a tuple. The action function may be an instance method so it +has another way to reference private data (besides global variables). +Parameterless functions or methods cannot be used, however. +""" # XXX The timefunc and delayfunc should have been defined as methods # XXX so you can define new kinds of schedulers using subclassing # XXX instead of having to define a module or class just to hold -# XXX the global state of your particular time and delay functtions. +# XXX the global state of your particular time and delay functions. import bisect class scheduler: - # - # Initialize a new instance, passing the time and delay functions - # - def __init__(self, timefunc, delayfunc): - self.queue = [] - self.timefunc = timefunc - self.delayfunc = delayfunc - # - # Enter a new event in the queue at an absolute time. - # Returns an ID for the event which can be used - # to remove it, if necessary. - # - def enterabs(self, time, priority, action, argument): - event = time, priority, action, argument - bisect.insort(self.queue, event) - return event # The ID - # - # A variant that specifies the time as a relative time. - # This is actually the more commonly used interface. - # - def enter(self, delay, priority, action, argument): - time = self.timefunc() + delay - return self.enterabs(time, priority, action, argument) - # - # Remove an event from the queue. - # This must be presented the ID as returned by enter(). - # If the event is not in the queue, this raises RuntimeError. - # - def cancel(self, event): - self.queue.remove(event) - # - # Check whether the queue is empty. - # - def empty(self): - return len(self.queue) == 0 - # - # Run: execute events until the queue is empty. - # - # When there is a positive delay until the first event, the - # delay function is called and the event is left in the queue; - # otherwise, the event is removed from the queue and executed - # (its action function is called, passing it the argument). - # If the delay function returns prematurely, it is simply - # restarted. - # - # It is legal for both the delay function and the action - # function to to modify the queue or to raise an exception; - # exceptions are not caught but the scheduler's state - # remains well-defined so run() may be called again. - # - # A questionably hack is added to allow other threads to run: - # just after an event is executed, a delay of 0 is executed, - # to avoid monopolizing the CPU when other threads are also - # runnable. - # - def run(self): - q = self.queue - while q: - time, priority, action, argument = q[0] - now = self.timefunc() - if now < time: - self.delayfunc(time - now) - else: - del q[0] - void = apply(action, argument) - self.delayfunc(0) # Let other threads run - # + def __init__(self, timefunc, delayfunc): + """Initialize a new instance, passing the time and delay + functions""" + self.queue = [] + self.timefunc = timefunc + self.delayfunc = delayfunc + + def enterabs(self, time, priority, action, argument): + """Enter a new event in the queue at an absolute time. + + Returns an ID for the event which can be used to remove it, + if necessary. + + """ + event = time, priority, action, argument + bisect.insort(self.queue, event) + return event # The ID + + def enter(self, delay, priority, action, argument): + """A variant that specifies the time as a relative time. + + This is actually the more commonly used interface. + + """ + time = self.timefunc() + delay + return self.enterabs(time, priority, action, argument) + + def cancel(self, event): + """Remove an event from the queue. + + This must be presented the ID as returned by enter(). + If the event is not in the queue, this raises RuntimeError. + + """ + self.queue.remove(event) + + def empty(self): + """Check whether the queue is empty.""" + return len(self.queue) == 0 + + def run(self): + """Execute events until the queue is empty. + + When there is a positive delay until the first event, the + delay function is called and the event is left in the queue; + otherwise, the event is removed from the queue and executed + (its action function is called, passing it the argument). If + the delay function returns prematurely, it is simply + restarted. + + It is legal for both the delay function and the action + function to to modify the queue or to raise an exception; + exceptions are not caught but the scheduler's state remains + well-defined so run() may be called again. + + A questionably hack is added to allow other threads to run: + just after an event is executed, a delay of 0 is executed, to + avoid monopolizing the CPU when other threads are also + runnable. + + """ + q = self.queue + while q: + time, priority, action, argument = q[0] + now = self.timefunc() + if now < time: + self.delayfunc(time - now) + else: + del q[0] + void = apply(action, argument) + self.delayfunc(0) # Let other threads run