Skip to content

Commit c0ce737

Browse files
committed
fix tombstones
1 parent 75a8d8e commit c0ce737

2 files changed

Lines changed: 61 additions & 4 deletions

File tree

Lib/test/test_external_inspection.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,6 +1471,60 @@ def matches_awaited_by_pattern(task):
14711471
finally:
14721472
_cleanup_sockets(client_socket, server_socket)
14731473

1474+
@skip_if_not_supported
1475+
@unittest.skipIf(
1476+
sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED,
1477+
"Test only runs on Linux with process_vm_readv support",
1478+
)
1479+
def test_async_global_awaited_by_skips_set_tombstones(self):
1480+
script_body = """\
1481+
import asyncio
1482+
1483+
class RemovedTask(asyncio.Task):
1484+
def __hash__(self):
1485+
return 0
1486+
1487+
class RemainingTask(asyncio.Task):
1488+
def __hash__(self):
1489+
return 1
1490+
1491+
async def wait_forever():
1492+
await asyncio.Event().wait()
1493+
1494+
async def main():
1495+
victim = asyncio.create_task(wait_forever(), name="victim")
1496+
removed = RemovedTask(wait_forever(), name="removed")
1497+
remaining = RemainingTask(wait_forever(), name="remaining")
1498+
1499+
asyncio.future_add_to_awaited_by(victim, removed)
1500+
asyncio.future_add_to_awaited_by(victim, remaining)
1501+
1502+
# Put a dummy in slot 0 before the only active entry in
1503+
# slot 1. It must not count toward the set's used entries.
1504+
asyncio.future_discard_from_awaited_by(victim, removed)
1505+
1506+
sock.sendall(b"ready")
1507+
sock.recv(16)
1508+
1509+
asyncio.run(main())
1510+
"""
1511+
1512+
with self._target_process(script_body) as (p, client_socket, _):
1513+
_wait_for_signal(client_socket, b"ready")
1514+
1515+
all_awaited_by = get_all_awaited_by(p.pid)
1516+
tasks_by_name = {
1517+
task.task_name: task
1518+
for task in self._get_task_id_map(all_awaited_by).values()
1519+
}
1520+
victim = tasks_by_name["victim"]
1521+
remaining = tasks_by_name["remaining"]
1522+
self.assertEqual(
1523+
[waiter.task_name for waiter in victim.awaited_by],
1524+
[remaining.task_id],
1525+
)
1526+
client_socket.sendall(b"done")
1527+
14741528
@skip_if_not_supported
14751529
@unittest.skipIf(
14761530
sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED,

Modules/_remote_debugging/asyncio.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,19 +145,22 @@ iterate_set_entries(
145145
Py_ssize_t i = 0;
146146
Py_ssize_t els = 0;
147147
while (i < set_len && els < num_els) {
148-
uintptr_t key_addr;
149-
if (read_py_ptr(unwinder, table_ptr, &key_addr) < 0) {
148+
setentry entry;
149+
if (_Py_RemoteDebug_PagedReadRemoteMemory(
150+
&unwinder->handle, table_ptr, sizeof(entry), &entry) < 0)
151+
{
150152
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read set entry key");
151153
return -1;
152154
}
153155

154-
if ((void*)key_addr != NULL) {
156+
uintptr_t key_addr = (uintptr_t)entry.key;
157+
if (key_addr != 0 && entry.hash != -1) {
155158
if (parse_task(unwinder, key_addr, awaited_by) < 0) {
156159
return -1;
157160
}
158161
els++;
159162
}
160-
table_ptr += sizeof(void*) * 2;
163+
table_ptr += sizeof(entry);
161164
i++;
162165
}
163166

0 commit comments

Comments
 (0)