mirror of https://github.com/python/cpython
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:
parent
ca0fb3423c
commit
af9f6de6ea
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue