gh-107851: Fix spurious failures in fcntl eintr tests (#121556)

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.
This commit is contained in:
Sam Gross 2024-07-10 10:36:52 -04:00 committed by GitHub
parent ca0fb3423c
commit af9f6de6ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 20 additions and 17 deletions

View File

@ -18,6 +18,7 @@ import signal
import socket import socket
import subprocess import subprocess
import sys import sys
import textwrap
import time import time
import unittest import unittest
@ -492,29 +493,31 @@ class SelectEINTRTest(EINTRBaseTest):
self.check_elapsed_time(dt) self.check_elapsed_time(dt)
class FNTLEINTRTest(EINTRBaseTest): class FCNTLEINTRTest(EINTRBaseTest):
def _lock(self, lock_func, lock_name): def _lock(self, lock_func, lock_name):
self.addCleanup(os_helper.unlink, os_helper.TESTFN) self.addCleanup(os_helper.unlink, os_helper.TESTFN)
code = '\n'.join(( rd1, wr1 = os.pipe()
"import fcntl, time", rd2, wr2 = os.pipe()
"with open('%s', 'wb') as f:" % os_helper.TESTFN, for fd in (rd1, wr1, rd2, wr2):
" fcntl.%s(f, fcntl.LOCK_EX)" % lock_name, self.addCleanup(os.close, fd)
" time.sleep(%s)" % self.sleep_time)) code = textwrap.dedent(f"""
start_time = time.monotonic() import fcntl, os, time
proc = self.subprocess(code) 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 kill_on_error(proc):
with open(os_helper.TESTFN, 'wb') as f: with open(os_helper.TESTFN, 'wb') as f:
# synchronize the subprocess # 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() start_time = time.monotonic()
for _ in support.sleeping_retry(support.LONG_TIMEOUT, error=False): os.write(wr2, b"go")
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)
# the child locked the file just a moment ago for 'sleep_time' seconds # 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 # that means that the lock below will block for 'sleep_time' minus some