From 687343300fdfba0dd77fb8ccda4291fc284b97f3 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Sat, 4 Jan 2025 11:43:55 +0000 Subject: [PATCH 1/4] stop the world in all tasks --- Include/internal/pycore_pystate.h | 4 ++-- Modules/_asynciomodule.c | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 1e73e541ef8de0..3812d6def6b6d0 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -182,8 +182,8 @@ extern void _PyEval_StartTheWorldAll(_PyRuntimeState *runtime); // Perform a stop-the-world pause for threads in the specified interpreter. // // NOTE: This is a no-op outside of Py_GIL_DISABLED builds. -extern void _PyEval_StopTheWorld(PyInterpreterState *interp); -extern void _PyEval_StartTheWorld(PyInterpreterState *interp); +extern PyAPI_FUNC(void) _PyEval_StopTheWorld(PyInterpreterState *interp); +extern PyAPI_FUNC(void) _PyEval_StartTheWorld(PyInterpreterState *interp); static inline void diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index b8b184af04a7cb..8763e7592924ba 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3767,9 +3767,11 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) return NULL; } int err = 0; - ASYNCIO_STATE_LOCK(state); - struct llist_node *node; + PyInterpreterState *interp = PyInterpreterState_Get(); + _PyEval_StopTheWorld(interp); + + struct llist_node *node; llist_for_each_safe(node, &state->asyncio_tasks_head) { TaskObj *task = llist_data(node, TaskObj, task_node); if (PyList_Append(tasks, (PyObject *)task) < 0) { @@ -3779,7 +3781,8 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) break; } } - ASYNCIO_STATE_UNLOCK(state); + + _PyEval_StartTheWorld(interp); if (err) { return NULL; } From 078f2d423fd5260d665cfcf071e370a10407f101 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 6 Jan 2025 11:47:30 +0000 Subject: [PATCH 2/4] add comment --- Modules/_asynciomodule.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 8763e7592924ba..7ba7cf9123dab0 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3768,6 +3768,13 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) } int err = 0; + // The linked list holds borrowed references to the tasks + // so before reading from it, all other threads + // are stopped using stop the world event so that + // no task could be concurrently deallocated while being + // added to the list. + // The state critical section need not to be held as + // all other threads are paused. PyInterpreterState *interp = PyInterpreterState_Get(); _PyEval_StopTheWorld(interp); From 58ea4ee1dd6f71d52a764437e4addc0ad462b077 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Tue, 7 Jan 2025 07:06:53 +0000 Subject: [PATCH 3/4] use _Py_TryIncref to protect against concurrent deallocations --- Include/internal/pycore_object.h | 2 +- Include/internal/pycore_pystate.h | 4 ++-- Modules/_asynciomodule.c | 35 +++++++++++++++---------------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index d7d68f938a9f0a..e26cb7673f939c 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -120,7 +120,7 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalRefcountErrorFunc( PyAPI_DATA(Py_ssize_t) _Py_RefTotal; extern void _Py_AddRefTotal(PyThreadState *, Py_ssize_t); -extern void _Py_IncRefTotal(PyThreadState *); +extern PyAPI_FUNC(void) _Py_IncRefTotal(PyThreadState *); extern void _Py_DecRefTotal(PyThreadState *); # define _Py_DEC_REFTOTAL(interp) \ diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 3812d6def6b6d0..1e73e541ef8de0 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -182,8 +182,8 @@ extern void _PyEval_StartTheWorldAll(_PyRuntimeState *runtime); // Perform a stop-the-world pause for threads in the specified interpreter. // // NOTE: This is a no-op outside of Py_GIL_DISABLED builds. -extern PyAPI_FUNC(void) _PyEval_StopTheWorld(PyInterpreterState *interp); -extern PyAPI_FUNC(void) _PyEval_StartTheWorld(PyInterpreterState *interp); +extern void _PyEval_StopTheWorld(PyInterpreterState *interp); +extern void _PyEval_StartTheWorld(PyInterpreterState *interp); static inline void diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 7ba7cf9123dab0..11dab87c63a7bb 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3767,29 +3767,28 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) return NULL; } int err = 0; - - // The linked list holds borrowed references to the tasks - // so before reading from it, all other threads - // are stopped using stop the world event so that - // no task could be concurrently deallocated while being - // added to the list. - // The state critical section need not to be held as - // all other threads are paused. - PyInterpreterState *interp = PyInterpreterState_Get(); - _PyEval_StopTheWorld(interp); - + ASYNCIO_STATE_LOCK(state); struct llist_node *node; + llist_for_each_safe(node, &state->asyncio_tasks_head) { TaskObj *task = llist_data(node, TaskObj, task_node); - if (PyList_Append(tasks, (PyObject *)task) < 0) { - Py_DECREF(tasks); - Py_DECREF(loop); - err = 1; - break; + // The linked list holds borrowed references to task + // as such it is possible that it can concurrently + // deallocated while added to this list. + // To protect against concurrent deallocation, + // we first try to incref the task which would fail + // if it is concurrently getting deallocated in another thread, + // otherwise it gets added to the list. + if (_Py_TryIncref((PyObject *)task)) { + if (_PyList_AppendTakeRef((PyListObject *)tasks, (PyObject *)task) < 0) { + Py_DECREF(tasks); + Py_DECREF(loop); + err = 1; + break; + } } } - - _PyEval_StartTheWorld(interp); + ASYNCIO_STATE_UNLOCK(state); if (err) { return NULL; } From 0b6922e44907eaec778396cf8c1cbf07c0c7f0d1 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Tue, 7 Jan 2025 07:08:27 +0000 Subject: [PATCH 4/4] fix comment --- Modules/_asynciomodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 11dab87c63a7bb..48f0ef95934fa4 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3773,9 +3773,9 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) llist_for_each_safe(node, &state->asyncio_tasks_head) { TaskObj *task = llist_data(node, TaskObj, task_node); // The linked list holds borrowed references to task - // as such it is possible that it can concurrently + // as such it is possible that the task is concurrently // deallocated while added to this list. - // To protect against concurrent deallocation, + // To protect against concurrent deallocations, // we first try to incref the task which would fail // if it is concurrently getting deallocated in another thread, // otherwise it gets added to the list. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy