Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f2b9cf4

Browse files
Issue #16165: Fix sched.scheduler.run() method was block a scheduler for
other threads.
1 parent c04957b commit f2b9cf4

3 files changed

Lines changed: 60 additions & 20 deletions

File tree

Lib/sched.py

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -128,27 +128,29 @@ def run(self, blocking=True):
128128
"""
129129
# localize variable access to minimize overhead
130130
# and to improve thread safety
131-
with self._lock:
132-
q = self._queue
133-
delayfunc = self.delayfunc
134-
timefunc = self.timefunc
135-
pop = heapq.heappop
136-
while q:
137-
time, priority, action, argument, kwargs = checked_event = q[0]
131+
lock = self._lock
132+
q = self._queue
133+
delayfunc = self.delayfunc
134+
timefunc = self.timefunc
135+
pop = heapq.heappop
136+
while True:
137+
with lock:
138+
if not q:
139+
break
140+
time, priority, action, argument, kwargs = q[0]
138141
now = timefunc()
139-
if now < time:
140-
if not blocking:
141-
return time - now
142-
delayfunc(time - now)
142+
if time > now:
143+
delay = True
143144
else:
144-
event = pop(q)
145-
# Verify that the event was not removed or altered
146-
# by another thread after we last looked at q[0].
147-
if event is checked_event:
148-
action(*argument, **kwargs)
149-
delayfunc(0) # Let other threads run
150-
else:
151-
heapq.heappush(q, event)
145+
delay = False
146+
pop(q)
147+
if delay:
148+
if not blocking:
149+
return time - now
150+
delayfunc(time - now)
151+
else:
152+
action(*argument, **kwargs)
153+
delayfunc(0) # Let other threads run
152154

153155
@property
154156
def queue(self):

Lib/test/test_sched.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
import time
55
import unittest
66
from test import support
7-
7+
try:
8+
import threading
9+
except ImportError:
10+
threading = None
811

912
class TestCase(unittest.TestCase):
1013

@@ -26,6 +29,20 @@ def test_enterabs(self):
2629
scheduler.run()
2730
self.assertEqual(l, [0.01, 0.02, 0.03, 0.04, 0.05])
2831

32+
@unittest.skipUnless(threading, 'Threading required for this test.')
33+
def test_enter_concurrent(self):
34+
l = []
35+
fun = lambda x: l.append(x)
36+
scheduler = sched.scheduler(time.time, time.sleep)
37+
scheduler.enter(0.03, 1, fun, (0.03,))
38+
t = threading.Thread(target=scheduler.run)
39+
t.start()
40+
for x in [0.05, 0.04, 0.02, 0.01]:
41+
z = scheduler.enter(x, 1, fun, (x,))
42+
scheduler.run()
43+
t.join()
44+
self.assertEqual(l, [0.01, 0.02, 0.03, 0.04, 0.05])
45+
2946
def test_priority(self):
3047
l = []
3148
fun = lambda x: l.append(x)
@@ -50,6 +67,24 @@ def test_cancel(self):
5067
scheduler.run()
5168
self.assertEqual(l, [0.02, 0.03, 0.04])
5269

70+
@unittest.skipUnless(threading, 'Threading required for this test.')
71+
def test_cancel_concurrent(self):
72+
l = []
73+
fun = lambda x: l.append(x)
74+
scheduler = sched.scheduler(time.time, time.sleep)
75+
now = time.time()
76+
event1 = scheduler.enterabs(now + 0.01, 1, fun, (0.01,))
77+
event2 = scheduler.enterabs(now + 0.02, 1, fun, (0.02,))
78+
event3 = scheduler.enterabs(now + 0.03, 1, fun, (0.03,))
79+
event4 = scheduler.enterabs(now + 0.04, 1, fun, (0.04,))
80+
event5 = scheduler.enterabs(now + 0.05, 1, fun, (0.05,))
81+
t = threading.Thread(target=scheduler.run)
82+
t.start()
83+
scheduler.cancel(event1)
84+
scheduler.cancel(event5)
85+
t.join()
86+
self.assertEqual(l, [0.02, 0.03, 0.04])
87+
5388
def test_empty(self):
5489
l = []
5590
fun = lambda x: l.append(x)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ Core and Builtins
124124
Library
125125
-------
126126

127+
- Issue #16165: Fix sched.scheduler.run() method was block a scheduler for
128+
other threads.
129+
127130
- Issue #16641: Fix default values of sched.scheduler.enter arguments were
128131
modifiable.
129132

0 commit comments

Comments
 (0)