From b4210de06aeaf23ad425f224d4d27a9d7d561da9 Mon Sep 17 00:00:00 2001 From: Bar Harel Date: Sun, 20 Sep 2020 02:41:08 +0300 Subject: [PATCH 1/5] Update sched.py --- Lib/sched.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Lib/sched.py b/Lib/sched.py index ff87874a3a4b70..14613cf29874da 100644 --- a/Lib/sched.py +++ b/Lib/sched.py @@ -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 From 52335445dc875579f9b52b3e44b93be1c4d286ea Mon Sep 17 00:00:00 2001 From: Bar Harel Date: Fri, 16 Oct 2020 22:44:53 +0300 Subject: [PATCH 2/5] add test --- Lib/test/test_sched.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Lib/test/test_sched.py b/Lib/test/test_sched.py index 491d7b3a745b4d..77cb0b87431827 100644 --- a/Lib/test/test_sched.py +++ b/Lib/test/test_sched.py @@ -142,6 +142,16 @@ 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.cancel(b) + scheduler.run() + self.assertEqual(events[0], "a") + def test_empty(self): l = [] fun = lambda x: l.append(x) From 61fb80d41eef920148c7e6eaf38ebb9346a64135 Mon Sep 17 00:00:00 2001 From: Bar Harel Date: Fri, 16 Oct 2020 22:48:18 +0300 Subject: [PATCH 3/5] Added news --- .../next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst diff --git a/Misc/NEWS.d/next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst b/Misc/NEWS.d/next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst new file mode 100644 index 00000000000000..7ee71a590eb057 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst @@ -0,0 +1,2 @@ +:meth:`sched.scheduler.cancel()` will now cancel the correct event, if two +events are scheduled for the same time. Patch by Bar Harel. From d0458b997e654868724ec3662b9aa632df8508d2 Mon Sep 17 00:00:00 2001 From: Bar Harel Date: Mon, 19 Oct 2020 02:56:36 +0300 Subject: [PATCH 4/5] Update Misc/NEWS.d/next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst Co-authored-by: Serhiy Storchaka --- .../next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst b/Misc/NEWS.d/next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst index 7ee71a590eb057..6330a91a44c544 100644 --- a/Misc/NEWS.d/next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst +++ b/Misc/NEWS.d/next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst @@ -1,2 +1,2 @@ :meth:`sched.scheduler.cancel()` will now cancel the correct event, if two -events are scheduled for the same time. Patch by Bar Harel. +events with same priority are scheduled for the same time. Patch by Bar Harel. From ea2f8c07dd75484b25f322fffdc7745e06ad6c20 Mon Sep 17 00:00:00 2001 From: Bar Harel Date: Mon, 19 Oct 2020 03:01:03 +0300 Subject: [PATCH 5/5] Add another event to guarantee order --- Lib/test/test_sched.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_sched.py b/Lib/test/test_sched.py index 77cb0b87431827..7ae7baae85e2c9 100644 --- a/Lib/test/test_sched.py +++ b/Lib/test/test_sched.py @@ -148,9 +148,10 @@ def test_cancel_correct_event(self): 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[0], "a") + self.assertEqual(events, ["a", "c"]) def test_empty(self): l = []