◐ Shell
clean mode source ↗

gh-121621: Move asyncio freelist to thread state by Fidget-Spinner · Pull Request #121915 · python/cpython

Expand Up @@ -138,9 +138,6 @@ typedef struct { /* Counter for autogenerated Task names */ uint64_t task_name_counter;
futureiterobject *fi_freelist; Py_ssize_t fi_freelist_len;
/* Linked-list of all tasks which are instances of asyncio.Task or subclasses of it. Third party tasks implementations which don't inherit from asyncio.Task are tracked separately using the 'non_asyncio_tasks' WeakSet. Expand Down Expand Up @@ -217,6 +214,16 @@ get_asyncio_state_by_def(PyObject *self) #include "clinic/_asynciomodule.c.h"

#ifdef WITH_FREELISTS static struct _Py_asyncmodule_futureiter_freelist * get_futureiter_freelist(void) { struct _Py_object_freelists *freelists = _Py_object_freelists_GET(); assert(freelists != NULL); return &freelists->futureiters; } #endif
/*[clinic input] class _asyncio.Future "FutureObj *" "&Future_Type" [clinic start generated code]*/ Expand Down Expand Up @@ -1579,25 +1586,19 @@ FutureIter_dealloc(futureiterobject *it)
assert(_PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE));
PyObject *module = ((PyHeapTypeObject*)tp)->ht_module; asyncio_state *state = NULL;
PyObject_GC_UnTrack(it); tp->tp_clear((PyObject *)it);
// GH-115874: We can't use PyType_GetModuleByDef here as the type might have // already been cleared, which is also why we must check if ht_module != NULL. if (module && _PyModule_GetDef(module) == &_asynciomodule) { state = get_asyncio_state(module); }
// TODO GH-121621: This should be moved to thread state as well. if (state && state->fi_freelist_len < FI_FREELIST_MAXLEN) { state->fi_freelist_len++; it->future = (FutureObj*) state->fi_freelist; state->fi_freelist = it; #ifdef WITH_FREELISTS struct _Py_asyncmodule_futureiter_freelist* freelist = get_futureiter_freelist(); if (freelist->fi_freelist_len < FI_FREELIST_MAXLEN) { freelist->fi_freelist_len++; it->future = (FutureObj*) freelist->fi_freelist; freelist->fi_freelist = it; } else { else #endif { PyObject_GC_Del(it); Py_DECREF(tp); } Expand Down Expand Up @@ -1801,14 +1802,18 @@ future_new_iter(PyObject *fut) asyncio_state *state = get_asyncio_state_by_def((PyObject *)fut); ENSURE_FUTURE_ALIVE(state, fut)
if (state->fi_freelist_len) { state->fi_freelist_len--; it = state->fi_freelist; state->fi_freelist = (futureiterobject*) it->future; #ifdef WITH_FREELISTS struct _Py_asyncmodule_futureiter_freelist* freelist = get_futureiter_freelist(); if (freelist->fi_freelist_len) { freelist->fi_freelist_len--; it = freelist->fi_freelist; freelist->fi_freelist = (futureiterobject*) it->future; it->future = NULL; _Py_NewReference((PyObject*) it); } else { else #endif { it = PyObject_GC_New(futureiterobject, state->FutureIterType); if (it == NULL) { return NULL; Expand Down Expand Up @@ -3264,6 +3269,24 @@ task_wakeup(TaskObj *task, PyObject *o) /*********************** Functions **************************/

/*[clinic input] _asyncio._clear_freelist
Clears the asyncio freelist.
Internal CPython implementation detail. Do not depend on this or use it! This function is thread-specific.
[clinic start generated code]*/
static PyObject * _asyncio__clear_freelist_impl(PyObject *module) /*[clinic end generated code: output=8d0e295bbbe2f8b6 input=f3ef7630d66cf63a]*/ { _PyAsyncModule_ClearFreeLists(_Py_object_freelists_GET(), 0); Py_RETURN_NONE; }
/*[clinic input] _asyncio._get_running_loop
Expand Down Expand Up @@ -3676,24 +3699,6 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) return tasks; }
static void module_free_freelists(asyncio_state *state) { PyObject *next; PyObject *current;
next = (PyObject*) state->fi_freelist; while (next != NULL) { assert(state->fi_freelist_len > 0); state->fi_freelist_len--;
current = next; next = (PyObject*) ((futureiterobject*) current)->future; PyObject_GC_Del(current); } assert(state->fi_freelist_len == 0); state->fi_freelist = NULL; }
static int module_traverse(PyObject *mod, visitproc visit, void *arg) Expand Down Expand Up @@ -3723,13 +3728,6 @@ module_traverse(PyObject *mod, visitproc visit, void *arg)
Py_VISIT(state->context_kwname);
// Visit freelist. PyObject *next = (PyObject*) state->fi_freelist; while (next != NULL) { PyObject *current = next; Py_VISIT(current); next = (PyObject*) ((futureiterobject*) current)->future; } return 0; }
Expand Down Expand Up @@ -3761,7 +3759,8 @@ module_clear(PyObject *mod)
Py_CLEAR(state->context_kwname);
module_free_freelists(state); _PyAsyncModule_ClearFreeLists(_Py_object_freelists_GET(), 0);

return 0; } Expand Down Expand Up @@ -3873,6 +3872,7 @@ static PyMethodDef asyncio_methods[] = { _ASYNCIO__LEAVE_TASK_METHODDEF _ASYNCIO__SWAP_CURRENT_TASK_METHODDEF _ASYNCIO_ALL_TASKS_METHODDEF _ASYNCIO__CLEAR_FREELIST_METHODDEF {NULL, NULL} };
Expand Down