Skip to content

Commit 0ba8027

Browse files
authored
Use support.sleeping_retry() and support.busy_retry() (#93848)
* Replace time.sleep(0.010) with sleeping_retry() to use an exponential sleep. * support.wait_process(): reuse sleeping_retry(). * _test_eintr: remove unused variables.
1 parent bddbd80 commit 0ba8027

7 files changed

+51
-58
lines changed

Lib/test/_test_eintr.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -403,11 +403,9 @@ def check_sigwait(self, wait_func):
403403
old_mask = signal.pthread_sigmask(signal.SIG_BLOCK, [signum])
404404
self.addCleanup(signal.pthread_sigmask, signal.SIG_UNBLOCK, [signum])
405405

406-
t0 = time.monotonic()
407406
proc = self.subprocess(code)
408407
with kill_on_error(proc):
409408
wait_func(signum)
410-
dt = time.monotonic() - t0
411409

412410
self.assertEqual(proc.wait(), 0)
413411

@@ -497,16 +495,18 @@ def _lock(self, lock_func, lock_name):
497495
proc = self.subprocess(code)
498496
with kill_on_error(proc):
499497
with open(os_helper.TESTFN, 'wb') as f:
500-
while True: # synchronize the subprocess
501-
dt = time.monotonic() - start_time
502-
if dt > 60.0:
503-
raise Exception("failed to sync child in %.1f sec" % dt)
498+
# synchronize the subprocess
499+
start_time = time.monotonic()
500+
for _ in support.sleeping_retry(60.0, error=False):
504501
try:
505502
lock_func(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
506503
lock_func(f, fcntl.LOCK_UN)
507-
time.sleep(0.01)
508504
except BlockingIOError:
509505
break
506+
else:
507+
dt = time.monotonic() - start_time
508+
raise Exception("failed to sync child in %.1f sec" % dt)
509+
510510
# the child locked the file just a moment ago for 'sleep_time' seconds
511511
# that means that the lock below will block for 'sleep_time' minus some
512512
# potential context switch delay

Lib/test/signalinterproctester.py

+6-7
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,15 @@ def wait_signal(self, child, signame):
2828
# (if set)
2929
child.wait()
3030

31-
timeout = support.SHORT_TIMEOUT
32-
deadline = time.monotonic() + timeout
33-
34-
while time.monotonic() < deadline:
31+
start_time = time.monotonic()
32+
for _ in support.busy_retry(support.SHORT_TIMEOUT, error=False):
3533
if self.got_signals[signame]:
3634
return
3735
signal.pause()
38-
39-
self.fail('signal %s not received after %s seconds'
40-
% (signame, timeout))
36+
else:
37+
dt = time.monotonic() - start_time
38+
self.fail('signal %s not received after %.1f seconds'
39+
% (signame, dt))
4140

4241
def subprocess_send_signal(self, pid, signame):
4342
code = 'import os, signal; os.kill(%s, signal.%s)' % (pid, signame)

Lib/test/support/__init__.py

+15-20
Original file line numberDiff line numberDiff line change
@@ -2072,31 +2072,26 @@ def wait_process(pid, *, exitcode, timeout=None):
20722072

20732073
if timeout is None:
20742074
timeout = SHORT_TIMEOUT
2075-
t0 = time.monotonic()
2076-
sleep = 0.001
2077-
max_sleep = 0.1
2078-
while True:
2075+
2076+
start_time = time.monotonic()
2077+
for _ in sleeping_retry(timeout, error=False):
20792078
pid2, status = os.waitpid(pid, os.WNOHANG)
20802079
if pid2 != 0:
20812080
break
2082-
# process is still running
2083-
2084-
dt = time.monotonic() - t0
2085-
if dt > SHORT_TIMEOUT:
2086-
try:
2087-
os.kill(pid, signal.SIGKILL)
2088-
os.waitpid(pid, 0)
2089-
except OSError:
2090-
# Ignore errors like ChildProcessError or PermissionError
2091-
pass
2092-
2093-
raise AssertionError(f"process {pid} is still running "
2094-
f"after {dt:.1f} seconds")
2081+
# rety: the process is still running
2082+
else:
2083+
try:
2084+
os.kill(pid, signal.SIGKILL)
2085+
os.waitpid(pid, 0)
2086+
except OSError:
2087+
# Ignore errors like ChildProcessError or PermissionError
2088+
pass
20952089

2096-
sleep = min(sleep * 2, max_sleep)
2097-
time.sleep(sleep)
2090+
dt = time.monotonic() - start_time
2091+
raise AssertionError(f"process {pid} is still running "
2092+
f"after {dt:.1f} seconds")
20982093
else:
2099-
# Windows implementation
2094+
# Windows implementation: don't support timeout :-(
21002095
pid2, status = os.waitpid(pid, 0)
21012096

21022097
exitcode2 = os.waitstatus_to_exitcode(status)

Lib/test/support/threading_helper.py

+8-10
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,17 @@ def wait_threads_exit(timeout=None):
8888
yield
8989
finally:
9090
start_time = time.monotonic()
91-
deadline = start_time + timeout
92-
while True:
91+
for _ in support.sleeping_retry(timeout, error=False):
92+
support.gc_collect()
9393
count = _thread._count()
9494
if count <= old_count:
9595
break
96-
if time.monotonic() > deadline:
97-
dt = time.monotonic() - start_time
98-
msg = (f"wait_threads() failed to cleanup {count - old_count} "
99-
f"threads after {dt:.1f} seconds "
100-
f"(count: {count}, old count: {old_count})")
101-
raise AssertionError(msg)
102-
time.sleep(0.010)
103-
support.gc_collect()
96+
else:
97+
dt = time.monotonic() - start_time
98+
msg = (f"wait_threads() failed to cleanup {count - old_count} "
99+
f"threads after {dt:.1f} seconds "
100+
f"(count: {count}, old count: {old_count})")
101+
raise AssertionError(msg)
104102

105103

106104
def join_thread(thread, timeout=None):

Lib/test/test_asyncio/utils.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,12 @@ async def once():
109109

110110

111111
def run_until(loop, pred, timeout=support.SHORT_TIMEOUT):
112-
deadline = time.monotonic() + timeout
113-
while not pred():
114-
if timeout is not None:
115-
timeout = deadline - time.monotonic()
116-
if timeout <= 0:
117-
raise futures.TimeoutError()
112+
for _ in support.busy_retry(timeout, error=False):
113+
if pred():
114+
break
118115
loop.run_until_complete(tasks.sleep(0.001))
116+
else:
117+
raise futures.TimeoutError()
119118

120119

121120
def run_once(loop):

Lib/test/test_asyncore.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ def capture_server(evt, buf, serv):
7676
pass
7777
else:
7878
n = 200
79-
start = time.monotonic()
80-
while n > 0 and time.monotonic() - start < 3.0:
79+
for _ in support.busy_retry(3.0, error=False):
8180
r, w, e = select.select([conn], [], [], 0.1)
8281
if r:
8382
n -= 1
@@ -86,6 +85,8 @@ def capture_server(evt, buf, serv):
8685
buf.write(data.replace(b'\n', b''))
8786
if b'\n' in data:
8887
break
88+
if n <= 0:
89+
break
8990
time.sleep(0.01)
9091

9192
conn.close()

Lib/test/test_logging.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -3602,7 +3602,6 @@ def do_queuehandler_configuration(self, qspec, lspec):
36023602
if lspec is not None:
36033603
cd['handlers']['ah']['listener'] = lspec
36043604
qh = None
3605-
delay = 0.01
36063605
try:
36073606
self.apply_config(cd)
36083607
qh = logging.getHandlerByName('ah')
@@ -3612,12 +3611,14 @@ def do_queuehandler_configuration(self, qspec, lspec):
36123611
logging.debug('foo')
36133612
logging.info('bar')
36143613
logging.warning('baz')
3614+
36153615
# Need to let the listener thread finish its work
3616-
deadline = time.monotonic() + support.LONG_TIMEOUT
3617-
while not qh.listener.queue.empty():
3618-
time.sleep(delay)
3619-
if time.monotonic() > deadline:
3620-
self.fail("queue not empty")
3616+
while support.sleeping_retry(support.LONG_TIMEOUT, error=False):
3617+
if qh.listener.queue.empty():
3618+
break
3619+
else:
3620+
self.fail("queue not empty")
3621+
36213622
with open(fn, encoding='utf-8') as f:
36223623
data = f.read().splitlines()
36233624
self.assertEqual(data, ['foo', 'bar', 'baz'])

0 commit comments

Comments
 (0)