Skip to content

Commit 0113c56

Browse files
[3.13] gh-107851: Fix spurious failures in fcntl eintr tests (GH-121556) (#121585)
On heavily loaded machines, the subprocess may finish its sleep before the parent process manages to synchronize with it via a failed lock. This leads to errors like: Exception: failed to sync child in 300.3 sec Use pipes instead to mutually synchronize between parent and child. (cherry picked from commit af9f6de) Co-authored-by: Sam Gross <[email protected]>
1 parent 0504f45 commit 0113c56

File tree

1 file changed

+20
-17
lines changed

1 file changed

+20
-17
lines changed

Lib/test/_test_eintr.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import socket
1919
import subprocess
2020
import sys
21+
import textwrap
2122
import time
2223
import unittest
2324

@@ -492,29 +493,31 @@ def test_devpoll(self):
492493
self.check_elapsed_time(dt)
493494

494495

495-
class FNTLEINTRTest(EINTRBaseTest):
496+
class FCNTLEINTRTest(EINTRBaseTest):
496497
def _lock(self, lock_func, lock_name):
497498
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
498-
code = '\n'.join((
499-
"import fcntl, time",
500-
"with open('%s', 'wb') as f:" % os_helper.TESTFN,
501-
" fcntl.%s(f, fcntl.LOCK_EX)" % lock_name,
502-
" time.sleep(%s)" % self.sleep_time))
503-
start_time = time.monotonic()
504-
proc = self.subprocess(code)
499+
rd1, wr1 = os.pipe()
500+
rd2, wr2 = os.pipe()
501+
for fd in (rd1, wr1, rd2, wr2):
502+
self.addCleanup(os.close, fd)
503+
code = textwrap.dedent(f"""
504+
import fcntl, os, time
505+
with open('{os_helper.TESTFN}', 'wb') as f:
506+
fcntl.{lock_name}(f, fcntl.LOCK_EX)
507+
os.write({wr1}, b"ok")
508+
_ = os.read({rd2}, 2) # wait for parent process
509+
time.sleep({self.sleep_time})
510+
""")
511+
proc = self.subprocess(code, pass_fds=[wr1, rd2])
505512
with kill_on_error(proc):
506513
with open(os_helper.TESTFN, 'wb') as f:
507514
# synchronize the subprocess
515+
ok = os.read(rd1, 2)
516+
self.assertEqual(ok, b"ok")
517+
518+
# notify the child that the parent is ready
508519
start_time = time.monotonic()
509-
for _ in support.sleeping_retry(support.LONG_TIMEOUT, error=False):
510-
try:
511-
lock_func(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
512-
lock_func(f, fcntl.LOCK_UN)
513-
except BlockingIOError:
514-
break
515-
else:
516-
dt = time.monotonic() - start_time
517-
raise Exception("failed to sync child in %.1f sec" % dt)
520+
os.write(wr2, b"go")
518521

519522
# the child locked the file just a moment ago for 'sleep_time' seconds
520523
# that means that the lock below will block for 'sleep_time' minus some

0 commit comments

Comments
 (0)