@@ -1490,13 +1490,21 @@ append_awaited_by_for_thread(
14901490 return -1 ;
14911491 }
14921492
1493- size_t iteration_count = 0 ;
1494- const size_t MAX_ITERATIONS = 100000 ; // Reasonable upper bound
1495- while ((uintptr_t )task_node .next != head_addr ) {
1496- if (++ iteration_count > MAX_ITERATIONS ) {
1497- PyErr_SetString (PyExc_RuntimeError , "Task list appears corrupted" );
1498- return -1 ;
1499- }
1493+ size_t iteration_count = 0 ;
1494+ const size_t MAX_ITERATIONS = 2 << 15 ; // A reasonable upper bound
1495+ while ((uintptr_t )task_node .next != head_addr ) {
1496+ if (++ iteration_count > MAX_ITERATIONS ) {
1497+ PyErr_SetString (PyExc_RuntimeError , "Task list appears corrupted" );
1498+ return -1 ;
1499+ }
1500+
1501+ if (task_node .next == NULL ) {
1502+ PyErr_SetString (
1503+ PyExc_RuntimeError ,
1504+ "Invalid linked list structure reading remote memory" );
1505+ return -1 ;
1506+ }
1507+
15001508 uintptr_t task_addr = (uintptr_t )task_node .next
15011509 - async_offsets -> asyncio_task_object .task_node ;
15021510
@@ -1698,6 +1706,11 @@ get_all_awaited_by(PyObject* self, PyObject* args)
16981706 head_addr = interpreter_state_addr
16991707 + local_async_debug .asyncio_interpreter_state .asyncio_tasks_head ;
17001708
1709+ // On top of a per-thread task lists used by default by asyncio to avoid
1710+ // contention, there is also a fallback per-interpreter list of tasks;
1711+ // any tasks still pending when a thread is destroyed will be moved to the
1712+ // per-interpreter task list. It's unlikely we'll find anything here, but
1713+ // interesting for debugging.
17011714 if (append_awaited_by (pid , 0 , head_addr , & local_debug_offsets ,
17021715 & local_async_debug , result ))
17031716 {
0 commit comments