Skip to content

Commit

Permalink
pythongh-107851: Fix spurious failures in fcntl eintr tests (python#1…
Browse files Browse the repository at this point in the history
…21556)

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.
  • Loading branch information
colesbury authored and estyxx committed Jul 17, 2024
1 parent 68bf878 commit d5d0072
Showing 1 changed file with 20 additions and 17 deletions.
37 changes: 20 additions & 17 deletions Lib/test/_test_eintr.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import socket
import subprocess
import sys
import textwrap
import time
import unittest

Expand Down Expand Up @@ -492,29 +493,31 @@ def test_devpoll(self):
self.check_elapsed_time(dt)


class FNTLEINTRTest(EINTRBaseTest):
class FCNTLEINTRTest(EINTRBaseTest):
def _lock(self, lock_func, lock_name):
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
code = '\n'.join((
"import fcntl, time",
"with open('%s', 'wb') as f:" % os_helper.TESTFN,
" fcntl.%s(f, fcntl.LOCK_EX)" % lock_name,
" time.sleep(%s)" % self.sleep_time))
start_time = time.monotonic()
proc = self.subprocess(code)
rd1, wr1 = os.pipe()
rd2, wr2 = os.pipe()
for fd in (rd1, wr1, rd2, wr2):
self.addCleanup(os.close, fd)
code = textwrap.dedent(f"""
import fcntl, os, time
with open('{os_helper.TESTFN}', 'wb') as f:
fcntl.{lock_name}(f, fcntl.LOCK_EX)
os.write({wr1}, b"ok")
_ = os.read({rd2}, 2) # wait for parent process
time.sleep({self.sleep_time})
""")
proc = self.subprocess(code, pass_fds=[wr1, rd2])
with kill_on_error(proc):
with open(os_helper.TESTFN, 'wb') as f:
# synchronize the subprocess
ok = os.read(rd1, 2)
self.assertEqual(ok, b"ok")

# notify the child that the parent is ready
start_time = time.monotonic()
for _ in support.sleeping_retry(support.LONG_TIMEOUT, error=False):
try:
lock_func(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
lock_func(f, fcntl.LOCK_UN)
except BlockingIOError:
break
else:
dt = time.monotonic() - start_time
raise Exception("failed to sync child in %.1f sec" % dt)
os.write(wr2, b"go")

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

0 comments on commit d5d0072

Please sign in to comment.