Skip to content

Commit 0b5ee86

Browse files
pablogsalmiss-islington
authored andcommitted
gh-158574: Stabilize TLBC cache refresh tests (GH-158801)
* gh-158574: Stabilize TLBC cache refresh tests * Simplify synchronization in TLBC cache refresh tests (cherry picked from commit 0e75a05) Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
1 parent 9e401cf commit 0b5ee86

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
@@ -2363,42 +2363,50 @@ def test_tlbc_cache_refresh_after_growth(self):
23632363
script = textwrap.dedent("""\
23642364
import os, threading
23652365
from _remote_debugging import RemoteUnwinder
2366+
from _queue import SimpleQueue
23662367
from test import support
23672368
2368-
go = threading.Event()
2369-
stop = threading.Event()
2369+
go = threading.Lock()
2370+
stop = threading.Lock()
2371+
go.acquire()
2372+
stop.acquire()
2373+
ready = SimpleQueue()
23702374
23712375
def leaf():
2372-
stop.wait()
2373-
2374-
def wait_for_leaf_frames(u, expected_count):
2375-
for _ in support.sleeping_retry(
2376-
support.SHORT_TIMEOUT,
2377-
f"Expected {expected_count} leaf frames",
2378-
):
2379-
try:
2380-
traces = u.get_stack_trace()
2381-
except RuntimeError as exc:
2382-
if str(exc) != "Failed to parse initial frame in chain":
2383-
raise
2384-
continue
2385-
count = sum(
2386-
f.funcname == "leaf"
2387-
for i in traces
2388-
for t in i.threads for f in t.frame_info
2389-
)
2390-
if count == expected_count:
2391-
return
2376+
ready.put(None)
2377+
stop.acquire()
2378+
2379+
def start_leaf():
2380+
ready.put(None)
2381+
go.acquire()
2382+
leaf()
2383+
2384+
def park():
2385+
ready.put(None)
2386+
stop.acquire()
2387+
2388+
def leaf_count(u):
2389+
return sum(
2390+
f.funcname == "leaf"
2391+
for i in u.get_stack_trace()
2392+
for t in i.threads for f in t.frame_info
2393+
)
23922394
2395+
# SimpleQueue.put() and Lock.acquire() do not push Python frames.
2396+
# Once notified, the worker's stack stays stable until go is released.
23932397
threading.Thread(target=leaf, daemon=True).start()
2398+
ready.get(timeout=support.SHORT_TIMEOUT)
23942399
for _ in range(16):
2395-
threading.Thread(target=stop.wait, daemon=True).start()
2396-
threading.Thread(target=lambda: (go.wait(), leaf()), daemon=True).start()
2400+
threading.Thread(target=park, daemon=True).start()
2401+
ready.get(timeout=support.SHORT_TIMEOUT)
2402+
threading.Thread(target=start_leaf, daemon=True).start()
2403+
ready.get(timeout=support.SHORT_TIMEOUT)
23972404
23982405
u = RemoteUnwinder(os.getpid(), all_threads=True, cache_frames=False)
2399-
wait_for_leaf_frames(u, 1)
2400-
go.set()
2401-
wait_for_leaf_frames(u, 2)
2406+
assert leaf_count(u) == 1
2407+
go.release()
2408+
ready.get(timeout=support.SHORT_TIMEOUT)
2409+
assert leaf_count(u) == 2
24022410
""")
24032411
result = subprocess.run(
24042412
[sys.executable, "-X", "gil=0", "-X", "tlbc=1", "-c", script],
@@ -2422,45 +2430,49 @@ def test_tlbc_cache_refresh_after_slot_fill(self):
24222430
script = textwrap.dedent("""\
24232431
import os, threading
24242432
from _remote_debugging import RemoteUnwinder
2433+
from _queue import SimpleQueue
24252434
2426-
go = threading.Event()
2427-
stop = threading.Event()
2435+
go = threading.Lock()
2436+
stop = threading.Lock()
2437+
go.acquire()
2438+
stop.acquire()
2439+
ready = SimpleQueue()
24282440
24292441
def leaf():
2430-
stop.wait()
2442+
ready.put(None)
2443+
stop.acquire()
2444+
2445+
def start_leaf():
2446+
ready.put(None)
2447+
go.acquire()
2448+
leaf()
24312449
24322450
from test import support
24332451
2434-
def lines(u, expected_count):
2435-
for _ in support.sleeping_retry(
2436-
support.SHORT_TIMEOUT,
2437-
f"Expected {expected_count} leaf frames",
2438-
):
2439-
try:
2440-
traces = u.get_stack_trace()
2441-
except RuntimeError as exc:
2442-
if str(exc) != "Failed to parse initial frame in chain":
2443-
raise
2444-
continue
2445-
result = sorted(
2446-
f.location.lineno
2447-
for i in traces
2448-
for t in i.threads for f in t.frame_info
2449-
if f.funcname == "leaf"
2450-
)
2451-
# A new frame can still point at the function definition.
2452-
if (len(result) == expected_count and
2453-
leaf.__code__.co_firstlineno not in result):
2454-
return result
2452+
def lines(u):
2453+
return sorted(
2454+
f.location.lineno
2455+
for i in u.get_stack_trace()
2456+
for t in i.threads for f in t.frame_info
2457+
if f.funcname == "leaf"
2458+
)
24552459
2460+
# SimpleQueue.put() and Lock.acquire() do not push Python frames.
2461+
# Once notified, the worker's stack stays stable until go is released.
24562462
threading.Thread(target=leaf, daemon=True).start()
2457-
threading.Thread(target=lambda: (go.wait(), leaf()), daemon=True).start()
2463+
ready.get(timeout=support.SHORT_TIMEOUT)
2464+
threading.Thread(target=start_leaf, daemon=True).start()
2465+
ready.get(timeout=support.SHORT_TIMEOUT)
24582466
u = RemoteUnwinder(os.getpid(), all_threads=True, cache_frames=False)
2459-
before = lines(u, 1)
2460-
assert before == [8], before
2461-
go.set()
2462-
cached = lines(u, 2)
2463-
assert cached == [8, 8], cached
2467+
before = lines(u)
2468+
# The notification can be observed before put() returns, so either
2469+
# line in leaf() is a valid sample.
2470+
assert before in ([12], [13]), before
2471+
go.release()
2472+
ready.get(timeout=support.SHORT_TIMEOUT)
2473+
cached = lines(u)
2474+
assert len(cached) == 2, cached
2475+
assert all(line in (12, 13) for line in cached), cached
24642476
""")
24652477
result = subprocess.run(
24662478
[sys.executable, "-X", "gil=0", "-X", "tlbc=1", "-c", script],

0 commit comments

Comments
 (0)