Skip to content

Commit 58ea4ee

Browse files
use _Py_TryIncref to protect against concurrent deallocations
1 parent 078f2d4 commit 58ea4ee

File tree

3 files changed

+20
-21
lines changed

3 files changed

+20
-21
lines changed

Include/internal/pycore_object.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalRefcountErrorFunc(
120120
PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
121121

122122
extern void _Py_AddRefTotal(PyThreadState *, Py_ssize_t);
123-
extern void _Py_IncRefTotal(PyThreadState *);
123+
extern PyAPI_FUNC(void) _Py_IncRefTotal(PyThreadState *);
124124
extern void _Py_DecRefTotal(PyThreadState *);
125125

126126
# define _Py_DEC_REFTOTAL(interp) \

Include/internal/pycore_pystate.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ extern void _PyEval_StartTheWorldAll(_PyRuntimeState *runtime);
182182
// Perform a stop-the-world pause for threads in the specified interpreter.
183183
//
184184
// NOTE: This is a no-op outside of Py_GIL_DISABLED builds.
185-
extern PyAPI_FUNC(void) _PyEval_StopTheWorld(PyInterpreterState *interp);
186-
extern PyAPI_FUNC(void) _PyEval_StartTheWorld(PyInterpreterState *interp);
185+
extern void _PyEval_StopTheWorld(PyInterpreterState *interp);
186+
extern void _PyEval_StartTheWorld(PyInterpreterState *interp);
187187

188188

189189
static inline void

Modules/_asynciomodule.c

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3767,29 +3767,28 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop)
37673767
return NULL;
37683768
}
37693769
int err = 0;
3770-
3771-
// The linked list holds borrowed references to the tasks
3772-
// so before reading from it, all other threads
3773-
// are stopped using stop the world event so that
3774-
// no task could be concurrently deallocated while being
3775-
// added to the list.
3776-
// The state critical section need not to be held as
3777-
// all other threads are paused.
3778-
PyInterpreterState *interp = PyInterpreterState_Get();
3779-
_PyEval_StopTheWorld(interp);
3780-
3770+
ASYNCIO_STATE_LOCK(state);
37813771
struct llist_node *node;
3772+
37823773
llist_for_each_safe(node, &state->asyncio_tasks_head) {
37833774
TaskObj *task = llist_data(node, TaskObj, task_node);
3784-
if (PyList_Append(tasks, (PyObject *)task) < 0) {
3785-
Py_DECREF(tasks);
3786-
Py_DECREF(loop);
3787-
err = 1;
3788-
break;
3775+
// The linked list holds borrowed references to task
3776+
// as such it is possible that it can concurrently
3777+
// deallocated while added to this list.
3778+
// To protect against concurrent deallocation,
3779+
// we first try to incref the task which would fail
3780+
// if it is concurrently getting deallocated in another thread,
3781+
// otherwise it gets added to the list.
3782+
if (_Py_TryIncref((PyObject *)task)) {
3783+
if (_PyList_AppendTakeRef((PyListObject *)tasks, (PyObject *)task) < 0) {
3784+
Py_DECREF(tasks);
3785+
Py_DECREF(loop);
3786+
err = 1;
3787+
break;
3788+
}
37893789
}
37903790
}
3791-
3792-
_PyEval_StartTheWorld(interp);
3791+
ASYNCIO_STATE_UNLOCK(state);
37933792
if (err) {
37943793
return NULL;
37953794
}

0 commit comments

Comments
 (0)