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]