https://github.com/python/cpython/commit/5c33a3b689f2bade4cf17e0941ab8b3f072633a6
commit: 5c33a3b689f2bade4cf17e0941ab8b3f072633a6
branch: 3.14
author: Pablo Galindo Salgado <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-10-05T01:57:30Z
summary:

[3.14] gh-158574: Stabilize TLBC cache refresh tests (GH-158801) (#158818)

[3.14] gh-158574: Stabilize TLBC cache refresh tests (GH-158801)

Wait for workers to reach stable Python frames before sampling their stacks.
Adapt the backport to the 3.14 RemoteUnwinder API.

(cherry picked from commit 0e75a0555731b9d7713d473e65b991f77316838c)

files:
M Lib/test/test_external_inspection.py

diff --git a/Lib/test/test_external_inspection.py 
b/Lib/test/test_external_inspection.py
index a4fed09034cc23..cf0289f7f4de24 100644
--- a/Lib/test/test_external_inspection.py
+++ b/Lib/test/test_external_inspection.py
@@ -1329,41 +1329,49 @@ def test_tlbc_cache_refresh_after_growth(self):
         script = textwrap.dedent("""\
             import os, threading
             from _remote_debugging import RemoteUnwinder
+            from _queue import SimpleQueue
             from test import support
 
-            go = threading.Event()
-            stop = threading.Event()
+            go = threading.Lock()
+            stop = threading.Lock()
+            go.acquire()
+            stop.acquire()
+            ready = SimpleQueue()
 
             def leaf():
-                stop.wait()
-
-            def wait_for_leaf_frames(u, expected_count):
-                for _ in support.sleeping_retry(
-                    support.SHORT_TIMEOUT,
-                    f"Expected {expected_count} leaf frames",
-                ):
-                    try:
-                        traces = u.get_stack_trace()
-                    except RuntimeError as exc:
-                        if str(exc) != "Failed to parse initial frame in 
chain":
-                            raise
-                        continue
-                    count = sum(
-                        f.funcname == "leaf"
-                        for t in traces for f in t.frame_info
-                    )
-                    if count == expected_count:
-                        return
+                ready.put(None)
+                stop.acquire()
+
+            def start_leaf():
+                ready.put(None)
+                go.acquire()
+                leaf()
+
+            def park():
+                ready.put(None)
+                stop.acquire()
+
+            def leaf_count(u):
+                return sum(
+                    f.funcname == "leaf"
+                    for t in u.get_stack_trace() for f in t.frame_info
+                )
 
+            # SimpleQueue.put() and Lock.acquire() do not push Python frames.
+            # Once notified, the worker's stack stays stable until go is 
released.
             threading.Thread(target=leaf, daemon=True).start()
+            ready.get(timeout=support.SHORT_TIMEOUT)
             for _ in range(16):
-                threading.Thread(target=stop.wait, daemon=True).start()
-            threading.Thread(target=lambda: (go.wait(), leaf()), 
daemon=True).start()
+                threading.Thread(target=park, daemon=True).start()
+                ready.get(timeout=support.SHORT_TIMEOUT)
+            threading.Thread(target=start_leaf, daemon=True).start()
+            ready.get(timeout=support.SHORT_TIMEOUT)
 
             u = RemoteUnwinder(os.getpid(), all_threads=True)
-            wait_for_leaf_frames(u, 1)
-            go.set()
-            wait_for_leaf_frames(u, 2)
+            assert leaf_count(u) == 1
+            go.release()
+            ready.get(timeout=support.SHORT_TIMEOUT)
+            assert leaf_count(u) == 2
             """)
         result = subprocess.run(
             [sys.executable, "-X", "gil=0", "-X", "tlbc=1", "-c", script],
@@ -1387,44 +1395,48 @@ def test_tlbc_cache_refresh_after_slot_fill(self):
         script = textwrap.dedent("""\
             import os, threading
             from _remote_debugging import RemoteUnwinder
+            from _queue import SimpleQueue
 
-            go = threading.Event()
-            stop = threading.Event()
+            go = threading.Lock()
+            stop = threading.Lock()
+            go.acquire()
+            stop.acquire()
+            ready = SimpleQueue()
 
             def leaf():
-                stop.wait()
+                ready.put(None)
+                stop.acquire()
+
+            def start_leaf():
+                ready.put(None)
+                go.acquire()
+                leaf()
 
             from test import support
 
-            def lines(u, expected_count):
-                for _ in support.sleeping_retry(
-                    support.SHORT_TIMEOUT,
-                    f"Expected {expected_count} leaf frames",
-                ):
-                    try:
-                        traces = u.get_stack_trace()
-                    except RuntimeError as exc:
-                        if str(exc) != "Failed to parse initial frame in 
chain":
-                            raise
-                        continue
-                    result = sorted(
-                        f.lineno
-                        for t in traces for f in t.frame_info
-                        if f.funcname == "leaf"
-                    )
-                    # A new frame can still point at the function definition.
-                    if (len(result) == expected_count and
-                        leaf.__code__.co_firstlineno not in result):
-                        return result
+            def lines(u):
+                return sorted(
+                    f.lineno
+                    for t in u.get_stack_trace() for f in t.frame_info
+                    if f.funcname == "leaf"
+                )
 
+            # SimpleQueue.put() and Lock.acquire() do not push Python frames.
+            # Once notified, the worker's stack stays stable until go is 
released.
             threading.Thread(target=leaf, daemon=True).start()
-            threading.Thread(target=lambda: (go.wait(), leaf()), 
daemon=True).start()
+            ready.get(timeout=support.SHORT_TIMEOUT)
+            threading.Thread(target=start_leaf, daemon=True).start()
+            ready.get(timeout=support.SHORT_TIMEOUT)
             u = RemoteUnwinder(os.getpid(), all_threads=True)
-            before = lines(u, 1)
-            assert before == [8], before
-            go.set()
-            cached = lines(u, 2)
-            assert cached == [8, 8], cached
+            before = lines(u)
+            # The notification can be observed before put() returns, so either
+            # line in leaf() is a valid sample.
+            assert before in ([12], [13]), before
+            go.release()
+            ready.get(timeout=support.SHORT_TIMEOUT)
+            cached = lines(u)
+            assert len(cached) == 2, cached
+            assert all(line in (12, 13) for line in cached), cached
             """)
         result = subprocess.run(
             [sys.executable, "-X", "gil=0", "-X", "tlbc=1", "-c", script],

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to