mirror of https://github.com/python/cpython
bpo-19270: Fixed sched.scheduler.cancel to cancel correct event (GH-22729)
This commit is contained in:
parent
155938907c
commit
5368c2b6e2
20
Lib/sched.py
20
Lib/sched.py
|
@ -26,23 +26,19 @@ has another way to reference private data (besides global variables).
|
||||||
import time
|
import time
|
||||||
import heapq
|
import heapq
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
from itertools import count
|
||||||
import threading
|
import threading
|
||||||
from time import monotonic as _time
|
from time import monotonic as _time
|
||||||
|
|
||||||
__all__ = ["scheduler"]
|
__all__ = ["scheduler"]
|
||||||
|
|
||||||
class Event(namedtuple('Event', 'time, priority, action, argument, kwargs')):
|
Event = namedtuple('Event', 'time, priority, sequence, action, argument, kwargs')
|
||||||
__slots__ = []
|
|
||||||
def __eq__(s, o): return (s.time, s.priority) == (o.time, o.priority)
|
|
||||||
def __lt__(s, o): return (s.time, s.priority) < (o.time, o.priority)
|
|
||||||
def __le__(s, o): return (s.time, s.priority) <= (o.time, o.priority)
|
|
||||||
def __gt__(s, o): return (s.time, s.priority) > (o.time, o.priority)
|
|
||||||
def __ge__(s, o): return (s.time, s.priority) >= (o.time, o.priority)
|
|
||||||
|
|
||||||
Event.time.__doc__ = ('''Numeric type compatible with the return value of the
|
Event.time.__doc__ = ('''Numeric type compatible with the return value of the
|
||||||
timefunc function passed to the constructor.''')
|
timefunc function passed to the constructor.''')
|
||||||
Event.priority.__doc__ = ('''Events scheduled for the same time will be executed
|
Event.priority.__doc__ = ('''Events scheduled for the same time will be executed
|
||||||
in the order of their priority.''')
|
in the order of their priority.''')
|
||||||
|
Event.sequence.__doc__ = ('''A continually increasing sequence number that
|
||||||
|
separates events if time and priority are equal.''')
|
||||||
Event.action.__doc__ = ('''Executing the event means executing
|
Event.action.__doc__ = ('''Executing the event means executing
|
||||||
action(*argument, **kwargs)''')
|
action(*argument, **kwargs)''')
|
||||||
Event.argument.__doc__ = ('''argument is a sequence holding the positional
|
Event.argument.__doc__ = ('''argument is a sequence holding the positional
|
||||||
|
@ -61,6 +57,7 @@ class scheduler:
|
||||||
self._lock = threading.RLock()
|
self._lock = threading.RLock()
|
||||||
self.timefunc = timefunc
|
self.timefunc = timefunc
|
||||||
self.delayfunc = delayfunc
|
self.delayfunc = delayfunc
|
||||||
|
self._sequence_generator = count()
|
||||||
|
|
||||||
def enterabs(self, time, priority, action, argument=(), kwargs=_sentinel):
|
def enterabs(self, time, priority, action, argument=(), kwargs=_sentinel):
|
||||||
"""Enter a new event in the queue at an absolute time.
|
"""Enter a new event in the queue at an absolute time.
|
||||||
|
@ -71,8 +68,10 @@ class scheduler:
|
||||||
"""
|
"""
|
||||||
if kwargs is _sentinel:
|
if kwargs is _sentinel:
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
event = Event(time, priority, action, argument, kwargs)
|
|
||||||
with self._lock:
|
with self._lock:
|
||||||
|
event = Event(time, priority, next(self._sequence_generator),
|
||||||
|
action, argument, kwargs)
|
||||||
heapq.heappush(self._queue, event)
|
heapq.heappush(self._queue, event)
|
||||||
return event # The ID
|
return event # The ID
|
||||||
|
|
||||||
|
@ -136,7 +135,8 @@ class scheduler:
|
||||||
with lock:
|
with lock:
|
||||||
if not q:
|
if not q:
|
||||||
break
|
break
|
||||||
time, priority, action, argument, kwargs = q[0]
|
(time, priority, sequence, action,
|
||||||
|
argument, kwargs) = q[0]
|
||||||
now = timefunc()
|
now = timefunc()
|
||||||
if time > now:
|
if time > now:
|
||||||
delay = True
|
delay = True
|
||||||
|
|
|
@ -142,6 +142,17 @@ class TestCase(unittest.TestCase):
|
||||||
self.assertTrue(q.empty())
|
self.assertTrue(q.empty())
|
||||||
self.assertEqual(timer.time(), 4)
|
self.assertEqual(timer.time(), 4)
|
||||||
|
|
||||||
|
def test_cancel_correct_event(self):
|
||||||
|
# bpo-19270
|
||||||
|
events = []
|
||||||
|
scheduler = sched.scheduler()
|
||||||
|
scheduler.enterabs(1, 1, events.append, ("a",))
|
||||||
|
b = scheduler.enterabs(1, 1, events.append, ("b",))
|
||||||
|
scheduler.enterabs(1, 1, events.append, ("c",))
|
||||||
|
scheduler.cancel(b)
|
||||||
|
scheduler.run()
|
||||||
|
self.assertEqual(events, ["a", "c"])
|
||||||
|
|
||||||
def test_empty(self):
|
def test_empty(self):
|
||||||
l = []
|
l = []
|
||||||
fun = lambda x: l.append(x)
|
fun = lambda x: l.append(x)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
:meth:`sched.scheduler.cancel()` will now cancel the correct event, if two
|
||||||
|
events with same priority are scheduled for the same time. Patch by Bar Harel.
|
Loading…
Reference in New Issue