Skip to content

Commit 9148042

Browse files
committed
Simplify synchronization in TLBC cache refresh tests
1 parent 1d8afbb commit 9148042

1 file changed

Lines changed: 56 additions & 82 deletions

File tree

‎Lib/test/test_external_inspection.py‎

Lines changed: 56 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -2336,63 +2336,52 @@ def get_trace_with_opcodes(pid):
23362336
def test_tlbc_cache_refresh_after_growth(self):
23372337
# Reproducer from gh-157660.
23382338
script = textwrap.dedent("""\
2339-
import os, sys, threading
2339+
import os, threading
23402340
from _remote_debugging import RemoteUnwinder
2341+
from _queue import SimpleQueue
23412342
from test import support
23422343
23432344
go = threading.Lock()
23442345
stop = threading.Lock()
23452346
go.acquire()
23462347
stop.acquire()
2348+
ready = SimpleQueue()
23472349
23482350
def leaf():
2351+
ready.put(None)
23492352
stop.acquire()
23502353
2351-
def wait_for_leaf_frames(u, expected_count):
2352-
for _ in support.sleeping_retry(
2353-
support.SHORT_TIMEOUT,
2354-
f"Expected {expected_count} leaf frames",
2355-
):
2356-
# Wait until the workers have entered their native lock
2357-
# calls, so sampling cannot race with Python frame changes.
2358-
frames = sys._current_frames()
2359-
frames.pop(threading.get_ident())
2360-
if len(frames) != 18:
2361-
continue
2362-
leaves = [f for f in frames.values()
2363-
if f.f_code is leaf.__code__]
2364-
if (len(leaves) != expected_count or
2365-
any(f.f_lineno != leaf.__code__.co_firstlineno + 1
2366-
for f in leaves)):
2367-
continue
2368-
if any(f.f_code not in (leaf.__code__, start_leaf.__code__,
2369-
threading.Thread.run.__code__)
2370-
for f in frames.values()):
2371-
continue
2372-
try:
2373-
traces = u.get_stack_trace()
2374-
except RuntimeError as exc:
2375-
if str(exc) != "Failed to parse initial frame in chain":
2376-
raise
2377-
continue
2378-
count = sum(
2379-
f.funcname == "leaf"
2380-
for i in traces
2381-
for t in i.threads for f in t.frame_info
2382-
)
2383-
if count == expected_count:
2384-
return
2354+
def start_leaf():
2355+
ready.put(None)
2356+
go.acquire()
2357+
leaf()
23852358
2359+
def park():
2360+
ready.put(None)
2361+
stop.acquire()
2362+
2363+
def leaf_count(u):
2364+
return sum(
2365+
f.funcname == "leaf"
2366+
for i in u.get_stack_trace()
2367+
for t in i.threads for f in t.frame_info
2368+
)
2369+
2370+
# SimpleQueue.put() and Lock.acquire() do not push Python frames.
2371+
# Once notified, the worker's stack stays stable until go is released.
23862372
threading.Thread(target=leaf, daemon=True).start()
2373+
ready.get(timeout=support.SHORT_TIMEOUT)
23872374
for _ in range(16):
2388-
threading.Thread(target=stop.acquire, daemon=True).start()
2389-
start_leaf = lambda: (go.acquire(), leaf())
2375+
threading.Thread(target=park, daemon=True).start()
2376+
ready.get(timeout=support.SHORT_TIMEOUT)
23902377
threading.Thread(target=start_leaf, daemon=True).start()
2378+
ready.get(timeout=support.SHORT_TIMEOUT)
23912379
23922380
u = RemoteUnwinder(os.getpid(), all_threads=True, cache_frames=False)
2393-
wait_for_leaf_frames(u, 1)
2381+
assert leaf_count(u) == 1
23942382
go.release()
2395-
wait_for_leaf_frames(u, 2)
2383+
ready.get(timeout=support.SHORT_TIMEOUT)
2384+
assert leaf_count(u) == 2
23962385
""")
23972386
result = subprocess.run(
23982387
[sys.executable, "-X", "gil=0", "-X", "tlbc=1", "-c", script],
@@ -2414,66 +2403,51 @@ def wait_for_leaf_frames(u, expected_count):
24142403
def test_tlbc_cache_refresh_after_slot_fill(self):
24152404
# Reproducer from gh-157660.
24162405
script = textwrap.dedent("""\
2417-
import os, sys, threading
2406+
import os, threading
24182407
from _remote_debugging import RemoteUnwinder
2408+
from _queue import SimpleQueue
24192409
24202410
go = threading.Lock()
24212411
stop = threading.Lock()
24222412
go.acquire()
24232413
stop.acquire()
2414+
ready = SimpleQueue()
24242415
24252416
def leaf():
2417+
ready.put(None)
24262418
stop.acquire()
24272419
2420+
def start_leaf():
2421+
ready.put(None)
2422+
go.acquire()
2423+
leaf()
2424+
24282425
from test import support
24292426
2430-
def lines(u, expected_count):
2431-
for _ in support.sleeping_retry(
2432-
support.SHORT_TIMEOUT,
2433-
f"Expected {expected_count} leaf frames",
2434-
):
2435-
# Wait until the workers have entered their native lock
2436-
# calls, so sampling cannot race with Python frame changes.
2437-
frames = sys._current_frames()
2438-
frames.pop(threading.get_ident())
2439-
if len(frames) != 2:
2440-
continue
2441-
leaves = [f for f in frames.values()
2442-
if f.f_code is leaf.__code__]
2443-
if (len(leaves) != expected_count or
2444-
any(f.f_lineno != leaf.__code__.co_firstlineno + 1
2445-
for f in leaves)):
2446-
continue
2447-
if any(f.f_code not in (leaf.__code__, start_leaf.__code__,
2448-
threading.Thread.run.__code__)
2449-
for f in frames.values()):
2450-
continue
2451-
try:
2452-
traces = u.get_stack_trace()
2453-
except RuntimeError as exc:
2454-
if str(exc) != "Failed to parse initial frame in chain":
2455-
raise
2456-
continue
2457-
result = sorted(
2458-
f.location.lineno
2459-
for i in traces
2460-
for t in i.threads for f in t.frame_info
2461-
if f.funcname == "leaf"
2462-
)
2463-
# A new frame can still point at the function definition.
2464-
if (len(result) == expected_count and
2465-
leaf.__code__.co_firstlineno not in result):
2466-
return result
2427+
def lines(u):
2428+
return sorted(
2429+
f.location.lineno
2430+
for i in u.get_stack_trace()
2431+
for t in i.threads for f in t.frame_info
2432+
if f.funcname == "leaf"
2433+
)
24672434
2435+
# SimpleQueue.put() and Lock.acquire() do not push Python frames.
2436+
# Once notified, the worker's stack stays stable until go is released.
24682437
threading.Thread(target=leaf, daemon=True).start()
2469-
start_leaf = lambda: (go.acquire(), leaf())
2438+
ready.get(timeout=support.SHORT_TIMEOUT)
24702439
threading.Thread(target=start_leaf, daemon=True).start()
2440+
ready.get(timeout=support.SHORT_TIMEOUT)
24712441
u = RemoteUnwinder(os.getpid(), all_threads=True, cache_frames=False)
2472-
before = lines(u, 1)
2473-
assert before == [10], before
2442+
before = lines(u)
2443+
# The notification can be observed before put() returns, so either
2444+
# line in leaf() is a valid sample.
2445+
assert before in ([12], [13]), before
24742446
go.release()
2475-
cached = lines(u, 2)
2476-
assert cached == [10, 10], cached
2447+
ready.get(timeout=support.SHORT_TIMEOUT)
2448+
cached = lines(u)
2449+
assert len(cached) == 2, cached
2450+
assert all(line in (12, 13) for line in cached), cached
24772451
""")
24782452
result = subprocess.run(
24792453
[sys.executable, "-X", "gil=0", "-X", "tlbc=1", "-c", script],

0 commit comments

Comments
 (0)