Skip to content

Commit 4bb31a1

Browse files
colesburynohlson
authored andcommitted
pythongh-120974: Use common freelist code in asyncio (python#122132)
This refactors asyncio to use the common freelist helper functions and macros. As a side effect, the freelist for _asyncio.Future is now re-enabled in the free-threaded build.
1 parent 423e358 commit 4bb31a1

File tree

3 files changed

+10
-70
lines changed

3 files changed

+10
-70
lines changed

Include/internal/pycore_freelist_state.h

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ extern "C" {
2020
# define Py_contexts_MAXFREELIST 255
2121
# define Py_async_gens_MAXFREELIST 80
2222
# define Py_async_gen_asends_MAXFREELIST 80
23+
# define Py_futureiters_MAXFREELIST 255
2324
# define Py_object_stack_chunks_MAXFREELIST 4
2425
#else
2526
# define PyTuple_MAXSAVESIZE 0
@@ -47,6 +48,7 @@ struct _Py_freelists {
4748
struct _Py_freelist contexts;
4849
struct _Py_freelist async_gens;
4950
struct _Py_freelist async_gen_asends;
51+
struct _Py_freelist futureiters;
5052
struct _Py_freelist object_stack_chunks;
5153
#else
5254
char _unused; // Empty structs are not allowed.

Modules/_asynciomodule.c

+4-69
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "Python.h"
66
#include "pycore_dict.h" // _PyDict_GetItem_KnownHash()
7+
#include "pycore_freelist.h" // _Py_FREELIST_POP()
78
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
89
#include "pycore_moduleobject.h" // _PyModule_GetState()
910
#include "pycore_object.h" // _Py_SetImmortalUntracked
@@ -75,8 +76,6 @@ typedef struct {
7576
#define Future_Check(state, obj) PyObject_TypeCheck(obj, state->FutureType)
7677
#define Task_Check(state, obj) PyObject_TypeCheck(obj, state->TaskType)
7778

78-
#define FI_FREELIST_MAXLEN 255
79-
8079
#ifdef Py_GIL_DISABLED
8180
# define ASYNCIO_STATE_LOCK(state) PyMutex_Lock(&state->mutex)
8281
# define ASYNCIO_STATE_UNLOCK(state) PyMutex_Unlock(&state->mutex)
@@ -138,11 +137,6 @@ typedef struct {
138137
/* Counter for autogenerated Task names */
139138
uint64_t task_name_counter;
140139

141-
#ifndef Py_GIL_DISABLED
142-
futureiterobject *fi_freelist;
143-
Py_ssize_t fi_freelist_len;
144-
#endif
145-
146140
/* Linked-list of all tasks which are instances of asyncio.Task or subclasses
147141
of it. Third party tasks implementations which don't inherit from
148142
asyncio.Task are tracked separately using the 'non_asyncio_tasks' WeakSet.
@@ -1584,24 +1578,7 @@ FutureIter_dealloc(futureiterobject *it)
15841578
PyObject_GC_UnTrack(it);
15851579
tp->tp_clear((PyObject *)it);
15861580

1587-
#ifndef Py_GIL_DISABLED
1588-
// GH-115874: We can't use PyType_GetModuleByDef here as the type might have
1589-
// already been cleared, which is also why we must check if ht_module != NULL.
1590-
PyObject *module = ((PyHeapTypeObject*)tp)->ht_module;
1591-
asyncio_state *state = NULL;
1592-
if (module && _PyModule_GetDef(module) == &_asynciomodule) {
1593-
state = get_asyncio_state(module);
1594-
}
1595-
1596-
// TODO GH-121621: This should be moved to thread state as well.
1597-
if (state && state->fi_freelist_len < FI_FREELIST_MAXLEN) {
1598-
state->fi_freelist_len++;
1599-
it->future = (FutureObj*) state->fi_freelist;
1600-
state->fi_freelist = it;
1601-
}
1602-
else
1603-
#endif
1604-
{
1581+
if (!_Py_FREELIST_PUSH(futureiters, it, Py_futureiters_MAXFREELIST)) {
16051582
PyObject_GC_Del(it);
16061583
Py_DECREF(tp);
16071584
}
@@ -1805,17 +1782,8 @@ future_new_iter(PyObject *fut)
18051782
asyncio_state *state = get_asyncio_state_by_def((PyObject *)fut);
18061783
ENSURE_FUTURE_ALIVE(state, fut)
18071784

1808-
#ifndef Py_GIL_DISABLED
1809-
if (state->fi_freelist_len) {
1810-
state->fi_freelist_len--;
1811-
it = state->fi_freelist;
1812-
state->fi_freelist = (futureiterobject*) it->future;
1813-
it->future = NULL;
1814-
_Py_NewReference((PyObject*) it);
1815-
}
1816-
else
1817-
#endif
1818-
{
1785+
it = _Py_FREELIST_POP(futureiterobject, futureiters);
1786+
if (it == NULL) {
18191787
it = PyObject_GC_New(futureiterobject, state->FutureIterType);
18201788
if (it == NULL) {
18211789
return NULL;
@@ -3678,27 +3646,6 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop)
36783646
return tasks;
36793647
}
36803648

3681-
static void
3682-
module_free_freelists(asyncio_state *state)
3683-
{
3684-
#ifndef Py_GIL_DISABLED
3685-
PyObject *next;
3686-
PyObject *current;
3687-
3688-
next = (PyObject*) state->fi_freelist;
3689-
while (next != NULL) {
3690-
assert(state->fi_freelist_len > 0);
3691-
state->fi_freelist_len--;
3692-
3693-
current = next;
3694-
next = (PyObject*) ((futureiterobject*) current)->future;
3695-
PyObject_GC_Del(current);
3696-
}
3697-
assert(state->fi_freelist_len == 0);
3698-
state->fi_freelist = NULL;
3699-
#endif
3700-
}
3701-
37023649
static int
37033650
module_traverse(PyObject *mod, visitproc visit, void *arg)
37043651
{
@@ -3727,16 +3674,6 @@ module_traverse(PyObject *mod, visitproc visit, void *arg)
37273674

37283675
Py_VISIT(state->context_kwname);
37293676

3730-
#ifndef Py_GIL_DISABLED
3731-
// Visit freelist.
3732-
PyObject *next = (PyObject*) state->fi_freelist;
3733-
while (next != NULL) {
3734-
PyObject *current = next;
3735-
Py_VISIT(current);
3736-
next = (PyObject*) ((futureiterobject*) current)->future;
3737-
}
3738-
#endif
3739-
37403677
return 0;
37413678
}
37423679

@@ -3768,8 +3705,6 @@ module_clear(PyObject *mod)
37683705

37693706
Py_CLEAR(state->context_kwname);
37703707

3771-
module_free_freelists(state);
3772-
37733708
return 0;
37743709
}
37753710

Objects/object.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,9 @@ static void
829829
free_object(void *obj)
830830
{
831831
PyObject *op = (PyObject *)obj;
832-
Py_TYPE(op)->tp_free(op);
832+
PyTypeObject *tp = Py_TYPE(op);
833+
tp->tp_free(op);
834+
Py_DECREF(tp);
833835
}
834836

835837
#endif
@@ -851,6 +853,7 @@ _PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization)
851853
clear_freelist(&freelists->contexts, is_finalization, free_object);
852854
clear_freelist(&freelists->async_gens, is_finalization, free_object);
853855
clear_freelist(&freelists->async_gen_asends, is_finalization, free_object);
856+
clear_freelist(&freelists->futureiters, is_finalization, free_object);
854857
if (is_finalization) {
855858
// Only clear object stack chunks during finalization. We use object
856859
// stacks during GC, so emptying the free-list is counterproductive.

0 commit comments

Comments
 (0)