Skip to content

Commit 0e75a05

Browse files
authored
gh-158574: Stabilize TLBC cache refresh tests (#158801)
* gh-158574: Stabilize TLBC cache refresh tests * Simplify synchronization in TLBC cache refresh tests
1 parent 7d25916 commit 0e75a05

1 file changed

Lines changed: 69 additions & 57 deletions

File tree

‎Lib/test/test_external_inspection.py‎

Lines changed: 69 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2437,42 +2437,50 @@ def test_tlbc_cache_refresh_after_growth(self):
24372437
script = textwrap.dedent("""\
24382438
import os, threading
24392439
from _remote_debugging import RemoteUnwinder
2440+
from _queue import SimpleQueue
24402441
from test import support
24412442
2442-
go = threading.Event()
2443-
stop = threading.Event()
2443+
go = threading.Lock()
2444+
stop = threading.Lock()
2445+
go.acquire()
2446+
stop.acquire()
2447+
ready = SimpleQueue()
24442448
24452449
def leaf():
2446-
stop.wait()
2447-
2448-
def wait_for_leaf_frames(u, expected_count):
2449-
for _ in support.sleeping_retry(
2450-
support.SHORT_TIMEOUT,
2451-
f"Expected {expected_count} leaf frames",
2452-
):
2453-
try:
2454-
traces = u.get_stack_trace()
2455-
except RuntimeError as exc:
2456-
if str(exc) != "Failed to parse initial frame in chain":
2457-
raise
2458-
continue
2459-
count = sum(
2460-
f.funcname == "leaf"
2461-
for i in traces
2462-
for t in i.threads for f in t.frame_info
2463-
)
2464-
if count == expected_count:
2465-
return
2450+
ready.put(None)
2451+
stop.acquire()
2452+
2453+
def start_leaf():
2454+
ready.put(None)
2455+
go.acquire()
2456+
leaf()
2457+
2458+
def park():
2459+
ready.put(None)
2460+
stop.acquire()
2461+
2462+
def leaf_count(u):
2463+
return sum(
2464+
f.funcname == "leaf"
2465+
for i in u.get_stack_trace()
2466+
for t in i.threads for f in t.frame_info
2467+
)
24662468
2469+
# SimpleQueue.put() and Lock.acquire() do not push Python frames.
2470+
# Once notified, the worker's stack stays stable until go is released.
24672471
threading.Thread(target=leaf, daemon=True).start()
2472+
ready.get(timeout=support.SHORT_TIMEOUT)
24682473
for _ in range(16):
2469-
threading.Thread(target=stop.wait, daemon=True).start()
2470-
threading.Thread(target=lambda: (go.wait(), leaf()), daemon=True).start()
2474+
threading.Thread(target=park, daemon=True).start()
2475+
ready.get(timeout=support.SHORT_TIMEOUT)
2476+
threading.Thread(target=start_leaf, daemon=True).start()
2477+
ready.get(timeout=support.SHORT_TIMEOUT)
24712478
24722479
u = RemoteUnwinder(os.getpid(), all_threads=True, cache_frames=False)
2473-
wait_for_leaf_frames(u, 1)
2474-
go.set()
2475-
wait_for_leaf_frames(u, 2)
2480+
assert leaf_count(u) == 1
2481+
go.release()
2482+
ready.get(timeout=support.SHORT_TIMEOUT)
2483+
assert leaf_count(u) == 2
24762484
""")
24772485
result = subprocess.run(
24782486
[sys.executable, "-X", "gil=0", "-X", "tlbc=1", "-c", script],
@@ -2496,45 +2504,49 @@ def test_tlbc_cache_refresh_after_slot_fill(self):
24962504
script = textwrap.dedent("""\
24972505
import os, threading
24982506
from _remote_debugging import RemoteUnwinder
2507+
from _queue import SimpleQueue
24992508
2500-
go = threading.Event()
2501-
stop = threading.Event()
2509+
go = threading.Lock()
2510+
stop = threading.Lock()
2511+
go.acquire()
2512+
stop.acquire()
2513+
ready = SimpleQueue()
25022514
25032515
def leaf():
2504-
stop.wait()
2516+
ready.put(None)
2517+
stop.acquire()
2518+
2519+
def start_leaf():
2520+
ready.put(None)
2521+
go.acquire()
2522+
leaf()
25052523
25062524
from test import support
25072525
2508-
def lines(u, expected_count):
2509-
for _ in support.sleeping_retry(
2510-
support.SHORT_TIMEOUT,
2511-
f"Expected {expected_count} leaf frames",
2512-
):
2513-
try:
2514-
traces = u.get_stack_trace()
2515-
except RuntimeError as exc:
2516-
if str(exc) != "Failed to parse initial frame in chain":
2517-
raise
2518-
continue
2519-
result = sorted(
2520-
f.location.lineno
2521-
for i in traces
2522-
for t in i.threads for f in t.frame_info
2523-
if f.funcname == "leaf"
2524-
)
2525-
# A new frame can still point at the function definition.
2526-
if (len(result) == expected_count and
2527-
leaf.__code__.co_firstlineno not in result):
2528-
return result
2526+
def lines(u):
2527+
return sorted(
2528+
f.location.lineno
2529+
for i in u.get_stack_trace()
2530+
for t in i.threads for f in t.frame_info
2531+
if f.funcname == "leaf"
2532+
)
25292533
2534+
# SimpleQueue.put() and Lock.acquire() do not push Python frames.
2535+
# Once notified, the worker's stack stays stable until go is released.
25302536
threading.Thread(target=leaf, daemon=True).start()
2531-
threading.Thread(target=lambda: (go.wait(), leaf()), daemon=True).start()
2537+
ready.get(timeout=support.SHORT_TIMEOUT)
2538+
threading.Thread(target=start_leaf, daemon=True).start()
2539+
ready.get(timeout=support.SHORT_TIMEOUT)
25322540
u = RemoteUnwinder(os.getpid(), all_threads=True, cache_frames=False)
2533-
before = lines(u, 1)
2534-
assert before == [8], before
2535-
go.set()
2536-
cached = lines(u, 2)
2537-
assert cached == [8, 8], cached
2541+
before = lines(u)
2542+
# The notification can be observed before put() returns, so either
2543+
# line in leaf() is a valid sample.
2544+
assert before in ([12], [13]), before
2545+
go.release()
2546+
ready.get(timeout=support.SHORT_TIMEOUT)
2547+
cached = lines(u)
2548+
assert len(cached) == 2, cached
2549+
assert all(line in (12, 13) for line in cached), cached
25382550
""")
25392551
result = subprocess.run(
25402552
[sys.executable, "-X", "gil=0", "-X", "tlbc=1", "-c", script],

0 commit comments

Comments
 (0)