]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-107851: Fix spurious failures in fcntl eintr tests (GH-121556) (#121586)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Wed, 10 Jul 2024 14:54:45 +0000 (16:54 +0200)
committerGitHub <noreply@github.com>
Wed, 10 Jul 2024 14:54:45 +0000 (14:54 +0000)
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 af9f6de6ea930b607f948f2c91a87fe4ca9d64db)

Co-authored-by: Sam Gross <colesbury@gmail.com>
Lib/test/_test_eintr.py

index 15586f15dfab30781818451564be8e63c165f83b..493932d6c6d441c23452c1230f7800584e6aff19 100644 (file)
@@ -18,6 +18,7 @@ import signal
 import socket
 import subprocess
 import sys
+import textwrap
 import time
 import unittest
 
@@ -492,29 +493,31 @@ class SelectEINTRTest(EINTRBaseTest):
         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