bpo-19270: Fixed sched.scheduler.cancel to cancel correct event (GH-22729)

This commit is contained in:
Bar Harel 2020-10-19 10:33:43 +03:00 committed by GitHub
parent 155938907c
commit 5368c2b6e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 23 additions and 10 deletions

View File

@ -26,23 +26,19 @@
import time
import heapq
from collections import namedtuple
from itertools import count
import threading
from time import monotonic as _time
__all__ = ["scheduler"]
class Event(namedtuple('Event', 'time, priority, 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 = namedtuple('Event', 'time, priority, sequence, action, argument, kwargs')
Event.time.__doc__ = ('''Numeric type compatible with the return value of the
timefunc function passed to the constructor.''')
Event.priority.__doc__ = ('''Events scheduled for the same time will be executed
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
action(*argument, **kwargs)''')
Event.argument.__doc__ = ('''argument is a sequence holding the positional
@ -61,6 +57,7 @@ def __init__(self, timefunc=_time, delayfunc=time.sleep):
self._lock = threading.RLock()
self.timefunc = timefunc
self.delayfunc = delayfunc
self._sequence_generator = count()
def enterabs(self, time, priority, action, argument=(), kwargs=_sentinel):
"""Enter a new event in the queue at an absolute time.
@ -71,8 +68,10 @@ def enterabs(self, time, priority, action, argument=(), kwargs=_sentinel):
"""
if kwargs is _sentinel:
kwargs = {}
event = Event(time, priority, action, argument, kwargs)
with self._lock:
event = Event(time, priority, next(self._sequence_generator),
action, argument, kwargs)
heapq.heappush(self._queue, event)
return event # The ID
@ -136,7 +135,8 @@ def run(self, blocking=True):
with lock:
if not q:
break
time, priority, action, argument, kwargs = q[0]
(time, priority, sequence, action,
argument, kwargs) = q[0]
now = timefunc()
if time > now:
delay = True

View File

@ -142,6 +142,17 @@ def test_cancel_concurrent(self):
self.assertTrue(q.empty())
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):
l = []
fun = lambda x: l.append(x)

View File

@ -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.