@@ -1437,6 +1437,160 @@ def matches_awaited_by_pattern(task):
14371437 finally :
14381438 _cleanup_sockets (client_socket , server_socket )
14391439
1440+ @skip_if_not_supported
1441+ @unittest .skipIf (
1442+ sys .platform == "linux" and not PROCESS_VM_READV_SUPPORTED ,
1443+ "Test only runs on Linux with process_vm_readv support" ,
1444+ )
1445+ def test_async_global_awaited_by_from_non_main_thread (self ):
1446+ port = find_unused_port ()
1447+ script = textwrap .dedent (
1448+ f"""\
1449+ import asyncio
1450+ import socket
1451+ import threading
1452+ import time
1453+
1454+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1455+ sock.connect(('localhost', { port } ))
1456+
1457+ async def worker_main():
1458+ task = asyncio.create_task(
1459+ asyncio.sleep(10_000),
1460+ name="worker task",
1461+ )
1462+ await asyncio.sleep(0)
1463+ sock.sendall(f"ready:{{threading.get_native_id()}}\\ n".encode())
1464+ await task
1465+
1466+ def run_worker_loop():
1467+ asyncio.run(worker_main())
1468+
1469+ threading.Thread(
1470+ target=run_worker_loop,
1471+ name="async-worker",
1472+ daemon=True,
1473+ ).start()
1474+ time.sleep(10_000)
1475+ """
1476+ )
1477+
1478+ with os_helper .temp_dir () as work_dir :
1479+ script_dir = os .path .join (work_dir , "script_pkg" )
1480+ os .mkdir (script_dir )
1481+
1482+ server_socket = _create_server_socket (port )
1483+ script_name = _make_test_script (script_dir , "script" , script )
1484+ client_socket = None
1485+
1486+ try :
1487+ with _managed_subprocess ([sys .executable , script_name ]) as p :
1488+ client_socket , _ = server_socket .accept ()
1489+ server_socket .close ()
1490+ server_socket = None
1491+
1492+ response = _wait_for_signal (client_socket , b"ready:" )
1493+ worker_thread_id = int (
1494+ response .split (b"ready:" , 1 )[1 ].splitlines ()[0 ]
1495+ )
1496+
1497+ for _ in busy_retry (SHORT_TIMEOUT ):
1498+ all_awaited_by = get_all_awaited_by (p .pid )
1499+ if any (
1500+ task .task_name == "worker task"
1501+ for info in all_awaited_by
1502+ if info .thread_id == worker_thread_id
1503+ for task in info .awaited_by
1504+ ):
1505+ break
1506+ else :
1507+ self .fail (
1508+ "get_all_awaited_by() did not report "
1509+ "the asyncio task from the non-main thread"
1510+ )
1511+ finally :
1512+ _cleanup_sockets (client_socket , server_socket )
1513+
1514+ @skip_if_not_supported
1515+ @unittest .skipIf (
1516+ sys .platform == "linux" and not PROCESS_VM_READV_SUPPORTED ,
1517+ "Test only runs on Linux with process_vm_readv support" ,
1518+ )
1519+ def test_async_remote_stack_trace_from_non_main_thread (self ):
1520+ port = find_unused_port ()
1521+ script = textwrap .dedent (
1522+ f"""\
1523+ import asyncio
1524+ import socket
1525+ import threading
1526+ import time
1527+
1528+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1529+ sock.connect(('localhost', { port } ))
1530+
1531+ def blocking_call():
1532+ sock.sendall(f"ready:{{threading.get_native_id()}}\\ n".encode())
1533+ time.sleep(10_000)
1534+
1535+ async def worker_task():
1536+ await asyncio.sleep(0)
1537+ blocking_call()
1538+
1539+ async def worker_main():
1540+ task = asyncio.create_task(
1541+ worker_task(),
1542+ name="worker task",
1543+ )
1544+ await task
1545+
1546+ def run_worker_loop():
1547+ asyncio.run(worker_main())
1548+
1549+ threading.Thread(
1550+ target=run_worker_loop,
1551+ name="async-worker",
1552+ daemon=True,
1553+ ).start()
1554+ time.sleep(10_000)
1555+ """
1556+ )
1557+
1558+ with os_helper .temp_dir () as work_dir :
1559+ script_dir = os .path .join (work_dir , "script_pkg" )
1560+ os .mkdir (script_dir )
1561+
1562+ server_socket = _create_server_socket (port )
1563+ script_name = _make_test_script (script_dir , "script" , script )
1564+ client_socket = None
1565+
1566+ try :
1567+ with _managed_subprocess ([sys .executable , script_name ]) as p :
1568+ client_socket , _ = server_socket .accept ()
1569+ server_socket .close ()
1570+ server_socket = None
1571+
1572+ response = _wait_for_signal (client_socket , b"ready:" )
1573+ worker_thread_id = int (
1574+ response .split (b"ready:" , 1 )[1 ].splitlines ()[0 ]
1575+ )
1576+
1577+ for _ in busy_retry (SHORT_TIMEOUT ):
1578+ stack_trace = get_async_stack_trace (p .pid )
1579+ if any (
1580+ task .task_name == "worker task"
1581+ for info in stack_trace
1582+ if info .thread_id == worker_thread_id
1583+ for task in info .awaited_by
1584+ ):
1585+ break
1586+ else :
1587+ self .fail (
1588+ "get_async_stack_trace() did not report "
1589+ "the running asyncio task from the non-main thread"
1590+ )
1591+ finally :
1592+ _cleanup_sockets (client_socket , server_socket )
1593+
14401594 @skip_if_not_supported
14411595 @unittest .skipIf (
14421596 sys .platform == "linux" and not PROCESS_VM_READV_SUPPORTED ,
0 commit comments