Skip to content

Commit 6fd15f0

Browse files
authored
gh-159534: Fix race condition in test_sampling_profiler (#160028)
Fix test_is_python_process_non_python_subprocess(). Wait until the forked Python process calls exec() before calling is_python_process(). Previously, is_python_build() returned True if exec() was not called yet in the forked process.
1 parent 69d0f96 commit 6fd15f0

1 file changed

Lines changed: 10 additions & 29 deletions

File tree

‎Lib/test/test_profiling/test_sampling_profiler/test_children.py‎

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
SHORT_TIMEOUT,
1717
reap_children,
1818
requires_remote_subprocess_debugging,
19+
sleeping_retry,
1920
)
2021

2122
# Guard imports that require _remote_debugging module.
@@ -84,29 +85,13 @@ def reader():
8485
return result[0]
8586

8687

87-
def _wait_for_process_ready(proc, timeout):
88-
# Wait for a subprocess to be ready using polling instead of fixed sleep.
89-
# Returns True if process is ready, False if it exited or timeout.
90-
deadline = time.time() + timeout
91-
poll_interval = 0.01
92-
93-
while time.time() < deadline:
94-
if proc.poll() is not None:
95-
return False
96-
97-
try:
98-
if sys.platform == "linux":
99-
if os.path.exists(f"/proc/{proc.pid}/exe"):
100-
return True
101-
else:
102-
return True
103-
except OSError:
104-
pass
105-
106-
time.sleep(poll_interval)
107-
poll_interval = min(poll_interval * 2, 0.1)
108-
109-
return proc.poll() is None
88+
def _wait_for_process_exec(proc, name, timeout):
89+
# Wait until a child process calls exec() to replace the forked process
90+
# executable. Raise AssertionError on timeout.
91+
for _ in sleeping_retry(timeout, max_delay=0.1):
92+
exe = os.readlink(f"/proc/{proc.pid}/exe")
93+
if os.path.basename(exe) == name:
94+
break
11095

11196

11297
@unittest.skipIf(
@@ -634,17 +619,13 @@ def test_is_python_process_non_python_subprocess(self):
634619
"""Test that a non-Python subprocess is not detected as Python."""
635620
# Start a non-Python subprocess (sleep command)
636621
proc = subprocess.Popen(
637-
["sleep", "10"],
622+
["sleep", "60"],
638623
stdout=subprocess.DEVNULL,
639624
stderr=subprocess.DEVNULL,
640625
)
641626

642627
try:
643-
# Wait for process to be ready using polling
644-
self.assertTrue(
645-
_wait_for_process_ready(proc, SHORT_TIMEOUT),
646-
f"Process {proc.pid} should be ready within {SHORT_TIMEOUT}s",
647-
)
628+
_wait_for_process_exec(proc, "sleep", SHORT_TIMEOUT)
648629

649630
self.assertFalse(
650631
is_python_process(proc.pid),

0 commit comments

Comments
 (0)