@@ -423,10 +423,16 @@ def _get_task_id_map(self, stack_trace):
423423 def _get_awaited_by_relationships (self , stack_trace ):
424424 """Extract task name to awaited_by set mapping."""
425425 id_to_task = self ._get_task_id_map (stack_trace )
426+
427+ def task_name (task_id ):
428+ task = id_to_task .get (task_id )
429+ if task is None :
430+ return f"<unknown object at { task_id :#x} >"
431+ return task .task_name
432+
426433 return {
427434 task .task_name : set (
428- id_to_task [awaited .task_name ].task_name
429- for awaited in task .awaited_by
435+ task_name (awaited .task_name ) for awaited in task .awaited_by
430436 )
431437 for task in stack_trace [0 ].awaited_by
432438 }
@@ -1476,33 +1482,34 @@ def matches_awaited_by_pattern(task):
14761482 sys .platform == "linux" and not PROCESS_VM_READV_SUPPORTED ,
14771483 "Test only runs on Linux with process_vm_readv support" ,
14781484 )
1479- def test_async_global_awaited_by_skips_set_tombstones (self ):
1485+ def test_async_awaited_by_skips_set_tombstones (self ):
14801486 script_body = """\
14811487 import asyncio
14821488
1483- class HashedTask(asyncio.Task):
1484- def __init__(self, coro, task_hash, **kwargs):
1485- self.task_hash = task_hash
1486- super().__init__(coro, **kwargs)
1489+ class RemovedTask(asyncio.Task):
1490+ def __hash__(self):
1491+ return 0
14871492
1493+ class RemainingTask(asyncio.Task):
14881494 def __hash__(self):
1489- return self.task_hash
1495+ return 1
14901496
14911497 async def main():
1492- victim = asyncio.create_task(
1493- asyncio.sleep(10_000), name="victim"
1498+ victim = asyncio.current_task()
1499+ victim.set_name("victim")
1500+ removed = RemovedTask(
1501+ asyncio.sleep(10_000), name="removed"
14941502 )
1495- removed = HashedTask(
1496- asyncio.sleep(10_000), 0, name="removed"
1497- )
1498- remaining = HashedTask(
1499- asyncio.sleep(10_000), 1, name="remaining"
1503+ remaining = RemainingTask(
1504+ asyncio.sleep(10_000), name="remaining"
15001505 )
15011506
15021507 asyncio.future_add_to_awaited_by(victim, removed)
15031508 asyncio.future_add_to_awaited_by(victim, remaining)
15041509
1505- # Removing hash 0 leaves a dummy before the live hash-1 entry.
1510+ # Removing hash 0 leaves a dummy in slot 0 before the only
1511+ # active entry in slot 1. It must not count toward the set's
1512+ # used entries.
15061513 asyncio.future_discard_from_awaited_by(victim, removed)
15071514
15081515 sock.sendall(b"ready")
@@ -1511,20 +1518,28 @@ async def main():
15111518 asyncio.run(main())
15121519 """
15131520
1514- with self ._target_process (script_body ) as (p , client_socket , _ ):
1521+ with self ._target_process (script_body ) as (
1522+ _ ,
1523+ client_socket ,
1524+ make_unwinder ,
1525+ ):
15151526 _wait_for_signal (client_socket , b"ready" )
15161527
1517- all_awaited_by = get_all_awaited_by (p .pid )
1518- tasks_by_name = {
1519- task .task_name : task
1520- for task in self ._get_task_id_map (all_awaited_by ).values ()
1521- }
1522- victim = tasks_by_name ["victim" ]
1523- remaining = tasks_by_name ["remaining" ]
1524- self .assertEqual (
1525- [waiter .task_name for waiter in victim .awaited_by ],
1526- [remaining .task_id ],
1527- )
1528+ for method_name in (
1529+ "get_async_stack_trace" ,
1530+ "get_all_awaited_by" ,
1531+ ):
1532+ with self .subTest (method = method_name ):
1533+ unwinder = make_unwinder (cache_frames = False )
1534+ stack_trace = getattr (unwinder , method_name )()
1535+ relationships = self ._get_awaited_by_relationships (
1536+ stack_trace
1537+ )
1538+ self .assertEqual (
1539+ relationships ["victim" ],
1540+ {"remaining" },
1541+ )
1542+
15281543 client_socket .sendall (b"done" )
15291544
15301545 @skip_if_not_supported
0 commit comments