From 482219028d32794fe2d727a7a6059068c63377b7 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Thu, 11 Jul 2024 20:20:01 +0800 Subject: [PATCH 01/20] Use PyMutex for writes to asyncio state --- Modules/_asynciomodule.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 87ad236cdbb39f..02a8cfa00950e7 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -81,6 +81,7 @@ typedef struct futureiterobject futureiterobject; /* State of the _asyncio module */ typedef struct { + PyMutex mutex; PyTypeObject *FutureIterType; PyTypeObject *TaskStepMethWrapper_Type; PyTypeObject *FutureType; @@ -341,8 +342,10 @@ get_running_loop(asyncio_state *state, PyObject **loop) } } + PyMutex_Lock(&state->mutex); state->cached_running_loop = rl; state->cached_running_loop_tsid = ts_id; + PyMutex_Unlock(&state->mutex); } @@ -384,8 +387,10 @@ set_running_loop(asyncio_state *state, PyObject *loop) return -1; } + PyMutex_Lock(&state->mutex); state->cached_running_loop = loop; // borrowed, kept alive by ts_dict state->cached_running_loop_tsid = PyThreadState_GetID(tstate); + PyMutex_Unlock(&state->mutex); return 0; } @@ -1668,9 +1673,11 @@ FutureIter_dealloc(futureiterobject *it) } if (state && state->fi_freelist_len < FI_FREELIST_MAXLEN) { + PyMutex_Lock(&state->mutex); state->fi_freelist_len++; it->future = (FutureObj*) state->fi_freelist; state->fi_freelist = it; + PyMutex_Unlock(&state->mutex); } else { PyObject_GC_Del(it); @@ -1877,9 +1884,11 @@ future_new_iter(PyObject *fut) ENSURE_FUTURE_ALIVE(state, fut) if (state->fi_freelist_len) { + PyMutex_Lock(&state->mutex); state->fi_freelist_len--; it = state->fi_freelist; state->fi_freelist = (futureiterobject*) it->future; + PyMutex_Unlock(&state->mutex); it->future = NULL; _Py_NewReference((PyObject*) it); } @@ -2028,8 +2037,10 @@ register_task(asyncio_state *state, TaskObj *task) assert(state->asyncio_tasks.head != NULL); task->next = state->asyncio_tasks.head; + PyMutex_Lock(&state->mutex); state->asyncio_tasks.head->prev = task; state->asyncio_tasks.head = task; + PyMutex_Unlock(&state->mutex); } static int @@ -2052,7 +2063,9 @@ unregister_task(asyncio_state *state, TaskObj *task) task->next->prev = task->prev; if (task->prev == NULL) { assert(state->asyncio_tasks.head == task); + PyMutex_Lock(&state->mutex); state->asyncio_tasks.head = task->next; + PyMutex_Unlock(&state->mutex); } else { task->prev->next = task->next; } @@ -2213,7 +2226,9 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop, // optimization: defer task name formatting // store the task counter as PyLong in the name // for deferred formatting in get_name + PyMutex_Lock(&state->mutex); name = PyLong_FromUnsignedLongLong(++state->task_name_counter); + PyMutex_Unlock(&state->mutex); } else if (!PyUnicode_CheckExact(name)) { name = PyObject_Str(name); } else { @@ -3750,6 +3765,7 @@ module_free_freelists(asyncio_state *state) PyObject *current; next = (PyObject*) state->fi_freelist; + PyMutex_Lock(&state->mutex); while (next != NULL) { assert(state->fi_freelist_len > 0); state->fi_freelist_len--; @@ -3760,6 +3776,7 @@ module_free_freelists(asyncio_state *state) } assert(state->fi_freelist_len == 0); state->fi_freelist = NULL; + PyMutex_Unlock(&state->mutex); } static int @@ -3844,6 +3861,7 @@ module_init(asyncio_state *state) { PyObject *module = NULL; + PyMutex_Lock(&state->mutex); state->asyncio_mod = PyImport_ImportModule("asyncio"); if (state->asyncio_mod == NULL) { goto fail; @@ -3913,10 +3931,13 @@ module_init(asyncio_state *state) goto fail; } + PyMutex_Unlock(&state->mutex); + Py_DECREF(module); return 0; fail: + PyMutex_Unlock(&state->mutex); Py_CLEAR(module); return -1; @@ -3947,9 +3968,12 @@ static int module_exec(PyObject *mod) { asyncio_state *state = get_asyncio_state(mod); + + PyMutex_Lock(&state->mutex); Py_SET_TYPE(&state->asyncio_tasks.tail, state->TaskType); _Py_SetImmortalUntracked((PyObject *)&state->asyncio_tasks.tail); state->asyncio_tasks.head = &state->asyncio_tasks.tail; + PyMutex_Unlock(&state->mutex); #define CREATE_TYPE(m, tp, spec, base) \ do { \ From 143747b8a011c3403bb78d0a1afcdeb8d01711da Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Fri, 12 Jul 2024 22:54:34 +0800 Subject: [PATCH 02/20] Address review --- .../internal/pycore_pyatomic_ft_wrappers.h | 2 + Modules/_asynciomodule.c | 46 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Include/internal/pycore_pyatomic_ft_wrappers.h b/Include/internal/pycore_pyatomic_ft_wrappers.h index a1bb383bcd22e9..d0426ec700fa1e 100644 --- a/Include/internal/pycore_pyatomic_ft_wrappers.h +++ b/Include/internal/pycore_pyatomic_ft_wrappers.h @@ -20,6 +20,7 @@ extern "C" { #endif #ifdef Py_GIL_DISABLED +#define FT_ATOMIC_ADD_UINT64(value, new_value) _Py_atomic_add_uint64(&value, new_value) #define FT_ATOMIC_LOAD_PTR(value) _Py_atomic_load_ptr(&value) #define FT_ATOMIC_STORE_PTR(value, new_value) _Py_atomic_store_ptr(&value, new_value) #define FT_ATOMIC_LOAD_SSIZE(value) _Py_atomic_load_ssize(&value) @@ -63,6 +64,7 @@ extern "C" { _Py_atomic_store_uint32_relaxed(&value, new_value) #else +#define FT_ATOMIC_ADD_UINT64(value, new_value) value += new_value #define FT_ATOMIC_LOAD_PTR(value) value #define FT_ATOMIC_STORE_PTR(value, new_value) value = new_value #define FT_ATOMIC_LOAD_SSIZE(value) value diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 02a8cfa00950e7..6cf36597c89dc5 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -77,11 +77,21 @@ typedef struct { #define FI_FREELIST_MAXLEN 255 +#ifdef Py_GIL_DISABLED +# define ASYNCIO_STATE_LOCK(state) PyMutex_Lock(&state->mutex) +# define ASYNCIO_STATE_UNLOCK(state) PyMutex_Unlock(&state->mutex) +#else +# define ASYNCIO_STATE_LOCK(state) (void(state)) +# define ASYNCIO_STATE_UNLOCK(state) (void(state)) +#endif + typedef struct futureiterobject futureiterobject; /* State of the _asyncio module */ typedef struct { +#ifdef Py_GIL_DISABLED PyMutex mutex; +#endif PyTypeObject *FutureIterType; PyTypeObject *TaskStepMethWrapper_Type; PyTypeObject *FutureType; @@ -342,10 +352,10 @@ get_running_loop(asyncio_state *state, PyObject **loop) } } - PyMutex_Lock(&state->mutex); + // TODO GH-121621: The should be moved to PyThreadState + // for easier and quicker access. state->cached_running_loop = rl; state->cached_running_loop_tsid = ts_id; - PyMutex_Unlock(&state->mutex); } @@ -387,11 +397,11 @@ set_running_loop(asyncio_state *state, PyObject *loop) return -1; } - PyMutex_Lock(&state->mutex); + + // TODO GH-121621: The should be moved to PyThreadState + // for easier and quicker access. state->cached_running_loop = loop; // borrowed, kept alive by ts_dict state->cached_running_loop_tsid = PyThreadState_GetID(tstate); - PyMutex_Unlock(&state->mutex); - return 0; } @@ -1672,12 +1682,11 @@ FutureIter_dealloc(futureiterobject *it) 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) { - PyMutex_Lock(&state->mutex); state->fi_freelist_len++; it->future = (FutureObj*) state->fi_freelist; state->fi_freelist = it; - PyMutex_Unlock(&state->mutex); } else { PyObject_GC_Del(it); @@ -1884,11 +1893,9 @@ future_new_iter(PyObject *fut) ENSURE_FUTURE_ALIVE(state, fut) if (state->fi_freelist_len) { - PyMutex_Lock(&state->mutex); state->fi_freelist_len--; it = state->fi_freelist; state->fi_freelist = (futureiterobject*) it->future; - PyMutex_Unlock(&state->mutex); it->future = NULL; _Py_NewReference((PyObject*) it); } @@ -2027,6 +2034,7 @@ static PyMethodDef TaskWakeupDef = { static void register_task(asyncio_state *state, TaskObj *task) { + ASYNCIO_STATE_LOCK(state); assert(Task_Check(state, task)); assert(task != &state->asyncio_tasks.tail); if (task->next != NULL) { @@ -2037,10 +2045,9 @@ register_task(asyncio_state *state, TaskObj *task) assert(state->asyncio_tasks.head != NULL); task->next = state->asyncio_tasks.head; - PyMutex_Lock(&state->mutex); state->asyncio_tasks.head->prev = task; state->asyncio_tasks.head = task; - PyMutex_Unlock(&state->mutex); + ASYNCIO_STATE_UNLOCK(state); } static int @@ -2060,15 +2067,15 @@ unregister_task(asyncio_state *state, TaskObj *task) assert(state->asyncio_tasks.head != task); return; } + ASYNCIO_STATE_LOCK(state); task->next->prev = task->prev; if (task->prev == NULL) { assert(state->asyncio_tasks.head == task); - PyMutex_Lock(&state->mutex); state->asyncio_tasks.head = task->next; - PyMutex_Unlock(&state->mutex); } else { task->prev->next = task->next; } + ASYNCIO_STATE_UNLOCK(state); task->next = NULL; task->prev = NULL; assert(state->asyncio_tasks.head != task); @@ -2226,9 +2233,8 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop, // optimization: defer task name formatting // store the task counter as PyLong in the name // for deferred formatting in get_name - PyMutex_Lock(&state->mutex); - name = PyLong_FromUnsignedLongLong(++state->task_name_counter); - PyMutex_Unlock(&state->mutex); + FT_ATOMIC_ADD_UINT64(state->task_name_counter, 1); + name = PyLong_FromUnsignedLongLong(state->task_name_counter); } else if (!PyUnicode_CheckExact(name)) { name = PyObject_Str(name); } else { @@ -3765,7 +3771,6 @@ module_free_freelists(asyncio_state *state) PyObject *current; next = (PyObject*) state->fi_freelist; - PyMutex_Lock(&state->mutex); while (next != NULL) { assert(state->fi_freelist_len > 0); state->fi_freelist_len--; @@ -3776,7 +3781,6 @@ module_free_freelists(asyncio_state *state) } assert(state->fi_freelist_len == 0); state->fi_freelist = NULL; - PyMutex_Unlock(&state->mutex); } static int @@ -3861,7 +3865,6 @@ module_init(asyncio_state *state) { PyObject *module = NULL; - PyMutex_Lock(&state->mutex); state->asyncio_mod = PyImport_ImportModule("asyncio"); if (state->asyncio_mod == NULL) { goto fail; @@ -3931,13 +3934,10 @@ module_init(asyncio_state *state) goto fail; } - PyMutex_Unlock(&state->mutex); - Py_DECREF(module); return 0; fail: - PyMutex_Unlock(&state->mutex); Py_CLEAR(module); return -1; @@ -3969,11 +3969,9 @@ module_exec(PyObject *mod) { asyncio_state *state = get_asyncio_state(mod); - PyMutex_Lock(&state->mutex); Py_SET_TYPE(&state->asyncio_tasks.tail, state->TaskType); _Py_SetImmortalUntracked((PyObject *)&state->asyncio_tasks.tail); state->asyncio_tasks.head = &state->asyncio_tasks.tail; - PyMutex_Unlock(&state->mutex); #define CREATE_TYPE(m, tp, spec, base) \ do { \ From 580304cf2db3b0066d62123901c759f85764b787 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Fri, 12 Jul 2024 22:58:03 +0800 Subject: [PATCH 03/20] fix default build --- Modules/_asynciomodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 6cf36597c89dc5..53ae02094c00de 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -81,8 +81,8 @@ typedef struct { # define ASYNCIO_STATE_LOCK(state) PyMutex_Lock(&state->mutex) # define ASYNCIO_STATE_UNLOCK(state) PyMutex_Unlock(&state->mutex) #else -# define ASYNCIO_STATE_LOCK(state) (void(state)) -# define ASYNCIO_STATE_UNLOCK(state) (void(state)) +# define ASYNCIO_STATE_LOCK(state) ((void)state)) +# define ASYNCIO_STATE_UNLOCK(state) ((void)state) #endif typedef struct futureiterobject futureiterobject; From 1faed1225ee0ef74f025d5d4150fc7cab2baf3ab Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Fri, 12 Jul 2024 23:14:52 +0800 Subject: [PATCH 04/20] Fix typo --- Modules/_asynciomodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 53ae02094c00de..6a1a3b50cd255a 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -81,7 +81,7 @@ typedef struct { # define ASYNCIO_STATE_LOCK(state) PyMutex_Lock(&state->mutex) # define ASYNCIO_STATE_UNLOCK(state) PyMutex_Unlock(&state->mutex) #else -# define ASYNCIO_STATE_LOCK(state) ((void)state)) +# define ASYNCIO_STATE_LOCK(state) ((void)state) # define ASYNCIO_STATE_UNLOCK(state) ((void)state) #endif From ba3d77174807733e3d6dd0e5f703db161e88bd0c Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 13 Jul 2024 01:47:17 +0800 Subject: [PATCH 05/20] Address review --- .../internal/pycore_pyatomic_ft_wrappers.h | 2 -- Modules/_asynciomodule.c | 24 ++++++++++++++----- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Include/internal/pycore_pyatomic_ft_wrappers.h b/Include/internal/pycore_pyatomic_ft_wrappers.h index d0426ec700fa1e..a1bb383bcd22e9 100644 --- a/Include/internal/pycore_pyatomic_ft_wrappers.h +++ b/Include/internal/pycore_pyatomic_ft_wrappers.h @@ -20,7 +20,6 @@ extern "C" { #endif #ifdef Py_GIL_DISABLED -#define FT_ATOMIC_ADD_UINT64(value, new_value) _Py_atomic_add_uint64(&value, new_value) #define FT_ATOMIC_LOAD_PTR(value) _Py_atomic_load_ptr(&value) #define FT_ATOMIC_STORE_PTR(value, new_value) _Py_atomic_store_ptr(&value, new_value) #define FT_ATOMIC_LOAD_SSIZE(value) _Py_atomic_load_ssize(&value) @@ -64,7 +63,6 @@ extern "C" { _Py_atomic_store_uint32_relaxed(&value, new_value) #else -#define FT_ATOMIC_ADD_UINT64(value, new_value) value += new_value #define FT_ATOMIC_LOAD_PTR(value) value #define FT_ATOMIC_STORE_PTR(value, new_value) value = new_value #define FT_ATOMIC_LOAD_SSIZE(value) value diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 6a1a3b50cd255a..4caf59e158c33c 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -2039,6 +2039,7 @@ register_task(asyncio_state *state, TaskObj *task) assert(task != &state->asyncio_tasks.tail); if (task->next != NULL) { // already registered + ASYNCIO_STATE_UNLOCK(state); return; } assert(task->prev == NULL); @@ -2057,7 +2058,7 @@ register_eager_task(asyncio_state *state, PyObject *task) } static void -unregister_task(asyncio_state *state, TaskObj *task) +unregister_task_no_lock(asyncio_state *state, TaskObj *task) { assert(Task_Check(state, task)); assert(task != &state->asyncio_tasks.tail); @@ -2067,7 +2068,7 @@ unregister_task(asyncio_state *state, TaskObj *task) assert(state->asyncio_tasks.head != task); return; } - ASYNCIO_STATE_LOCK(state); + task->next->prev = task->prev; if (task->prev == NULL) { assert(state->asyncio_tasks.head == task); @@ -2075,12 +2076,20 @@ unregister_task(asyncio_state *state, TaskObj *task) } else { task->prev->next = task->next; } - ASYNCIO_STATE_UNLOCK(state); + task->next = NULL; task->prev = NULL; assert(state->asyncio_tasks.head != task); } +static void +unregister_task(asyncio_state *state, TaskObj *task) +{ + ASYNCIO_STATE_LOCK(state); + unregister_task_no_lock(state, task); + ASYNCIO_STATE_UNLOCK(state); +} + static int unregister_eager_task(asyncio_state *state, PyObject *task) { @@ -2233,8 +2242,12 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop, // optimization: defer task name formatting // store the task counter as PyLong in the name // for deferred formatting in get_name - FT_ATOMIC_ADD_UINT64(state->task_name_counter, 1); - name = PyLong_FromUnsignedLongLong(state->task_name_counter); +#ifdef Py_GIL_DISABLED + unsigned long long counter = _Py_atomic_add_uint64(&state->task_name_counter, 1) + 1; +#else + unsigned long long counter = ++state->task_name_counter; +#endif + name = PyLong_FromUnsignedLongLong(counter); } else if (!PyUnicode_CheckExact(name)) { name = PyObject_Str(name); } else { @@ -3968,7 +3981,6 @@ static int module_exec(PyObject *mod) { asyncio_state *state = get_asyncio_state(mod); - Py_SET_TYPE(&state->asyncio_tasks.tail, state->TaskType); _Py_SetImmortalUntracked((PyObject *)&state->asyncio_tasks.tail); state->asyncio_tasks.head = &state->asyncio_tasks.tail; From ec2c175706f681cabf81562fe4fcc928289039ed Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 13 Jul 2024 01:48:40 +0800 Subject: [PATCH 06/20] fixup whitespace --- Modules/_asynciomodule.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 4caf59e158c33c..4214e6b1a1df14 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -402,6 +402,7 @@ set_running_loop(asyncio_state *state, PyObject *loop) // for easier and quicker access. state->cached_running_loop = loop; // borrowed, kept alive by ts_dict state->cached_running_loop_tsid = PyThreadState_GetID(tstate); + return 0; } @@ -2068,7 +2069,6 @@ unregister_task_no_lock(asyncio_state *state, TaskObj *task) assert(state->asyncio_tasks.head != task); return; } - task->next->prev = task->prev; if (task->prev == NULL) { assert(state->asyncio_tasks.head == task); @@ -2076,7 +2076,6 @@ unregister_task_no_lock(asyncio_state *state, TaskObj *task) } else { task->prev->next = task->next; } - task->next = NULL; task->prev = NULL; assert(state->asyncio_tasks.head != task); From e1b730a0a38e7bbfe691a7c5ae335fdefeb5186b Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 13 Jul 2024 02:11:15 +0800 Subject: [PATCH 07/20] minor nit --- Modules/_asynciomodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 4214e6b1a1df14..0782b0e4e529f8 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -2059,7 +2059,7 @@ register_eager_task(asyncio_state *state, PyObject *task) } static void -unregister_task_no_lock(asyncio_state *state, TaskObj *task) +unregister_task_lock_held(asyncio_state *state, TaskObj *task) { assert(Task_Check(state, task)); assert(task != &state->asyncio_tasks.tail); @@ -2085,7 +2085,7 @@ static void unregister_task(asyncio_state *state, TaskObj *task) { ASYNCIO_STATE_LOCK(state); - unregister_task_no_lock(state, task); + unregister_task_lock_held(state, task); ASYNCIO_STATE_UNLOCK(state); } From f9fb6e1eb94fa5bc3902e88d266bc25b752824bb Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 13 Jul 2024 21:19:25 +0800 Subject: [PATCH 08/20] Move asyncio freelist to per-thread --- Include/internal/pycore_freelist.h | 42 +++++++++++++ Lib/test/libregrtest/utils.py | 7 +++ Modules/_asynciomodule.c | 98 ++++++++++++++++-------------- Modules/clinic/_asynciomodule.c.h | 23 ++++++- Objects/object.c | 2 + 5 files changed, 126 insertions(+), 46 deletions(-) diff --git a/Include/internal/pycore_freelist.h b/Include/internal/pycore_freelist.h index e684e084b8bef8..3cc8b12df0b778 100644 --- a/Include/internal/pycore_freelist.h +++ b/Include/internal/pycore_freelist.h @@ -124,6 +124,14 @@ struct _Py_object_stack_freelist { Py_ssize_t numfree; }; + +struct _Py_asyncmodule_futureiter_freelist { +#ifdef WITH_FREELISTS + struct futureiterobject *fi_freelist; + Py_ssize_t fi_freelist_len; +#endif +}; + struct _Py_object_freelists { struct _Py_float_freelist floats; struct _Py_tuple_freelist tuples; @@ -135,6 +143,7 @@ struct _Py_object_freelists { struct _Py_async_gen_freelist async_gens; struct _Py_async_gen_asend_freelist async_gen_asends; struct _Py_object_stack_freelist object_stacks; + struct _Py_asyncmodule_futureiter_freelist futureiters; }; extern void _PyObject_ClearFreeLists(struct _Py_object_freelists *freelists, int is_finalization); @@ -147,6 +156,39 @@ extern void _PyAsyncGen_ClearFreeLists(struct _Py_object_freelists *freelists, i extern void _PyContext_ClearFreeList(struct _Py_object_freelists *freelists, int is_finalization); extern void _PyObjectStackChunk_ClearFreeList(struct _Py_object_freelists *freelists, int is_finalization); +// Keep in sync with _asynciomodule.c ! +typedef struct futureiterobject_dummy { + PyObject_HEAD + void *future; +} futureiterobject_dummy; + +static inline void +_PyAsyncModule_ClearFreeLists(struct _Py_object_freelists *freelists, int is_finalization) +{ +#ifdef WITH_FREELISTS + PyObject *next; + PyObject *current; + + next = (PyObject*) freelists->futureiters.fi_freelist; + while (next != NULL) { + assert(freelists->futureiters.fi_freelist_len > 0); + freelists->futureiters.fi_freelist_len--; + + current = next; + next = (PyObject*) ((futureiterobject_dummy*) current)->future; + PyObject_GC_Del(current); + } + assert(freelists->futureiters.fi_freelist_len == 0 || freelists->futureiters.fi_freelist_len == -1); + freelists->futureiters.fi_freelist = NULL; + if (is_finalization) { + freelists->futureiters.fi_freelist_len = -1; + } + else { + freelists->futureiters.fi_freelist_len = 0; + } +#endif +} + #ifdef __cplusplus } #endif diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index 2a3449016fe951..ccc4f4523866b3 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -293,6 +293,13 @@ def clear_caches(): else: importlib_metadata.FastPath.__new__.cache_clear() + try: + _asyncio = sys.modules['_asyncio'] + except KeyError: + pass + else: + _asyncio._clear_freelist() + def get_build_info(): # Get most important configure and build options as a list of strings. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 0782b0e4e529f8..a6d2c124a6946a 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -141,9 +141,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. @@ -220,6 +217,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]*/ @@ -1669,27 +1676,19 @@ FutureIter_dealloc(futureiterobject *it) // FutureIter is a heap type so any subclass must also be a heap type. 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. - // Due to this restriction, subclasses that belong to a different module - // will not be able to use the free list. - 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); } @@ -1893,14 +1892,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; @@ -3361,6 +3364,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 @@ -3776,24 +3797,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) @@ -3824,12 +3827,15 @@ module_traverse(PyObject *mod, visitproc visit, void *arg) Py_VISIT(state->context_kwname); // Visit freelist. - PyObject *next = (PyObject*) state->fi_freelist; +#ifdef WITH_FREELISTS + struct _Py_asyncmodule_futureiter_freelist* freelist = get_futureiter_freelist(); + PyObject *next = (PyObject*) freelist->fi_freelist; while (next != NULL) { PyObject *current = next; Py_VISIT(current); next = (PyObject*) ((futureiterobject*) current)->future; } +#endif return 0; } @@ -3861,7 +3867,8 @@ module_clear(PyObject *mod) Py_CLEAR(state->context_kwname); - module_free_freelists(state); + _PyAsyncModule_ClearFreeLists(_Py_object_freelists_GET(), 0); + return 0; } @@ -3973,6 +3980,7 @@ static PyMethodDef asyncio_methods[] = { _ASYNCIO__LEAVE_TASK_METHODDEF _ASYNCIO__SWAP_CURRENT_TASK_METHODDEF _ASYNCIO_ALL_TASKS_METHODDEF + _ASYNCIO__CLEAR_FREELIST_METHODDEF {NULL, NULL} }; diff --git a/Modules/clinic/_asynciomodule.c.h b/Modules/clinic/_asynciomodule.c.h index d619a124ccead5..b7de9440ade4e8 100644 --- a/Modules/clinic/_asynciomodule.c.h +++ b/Modules/clinic/_asynciomodule.c.h @@ -940,6 +940,27 @@ PyDoc_STRVAR(_asyncio_Task_set_name__doc__, #define _ASYNCIO_TASK_SET_NAME_METHODDEF \ {"set_name", (PyCFunction)_asyncio_Task_set_name, METH_O, _asyncio_Task_set_name__doc__}, +PyDoc_STRVAR(_asyncio__clear_freelist__doc__, +"_clear_freelist($module, /)\n" +"--\n" +"\n" +"Clears the asyncio freelist.\n" +"\n" +"Internal CPython implementation detail. Do not depend on this or use it!\n" +"This function is thread-specific."); + +#define _ASYNCIO__CLEAR_FREELIST_METHODDEF \ + {"_clear_freelist", (PyCFunction)_asyncio__clear_freelist, METH_NOARGS, _asyncio__clear_freelist__doc__}, + +static PyObject * +_asyncio__clear_freelist_impl(PyObject *module); + +static PyObject * +_asyncio__clear_freelist(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _asyncio__clear_freelist_impl(module); +} + PyDoc_STRVAR(_asyncio__get_running_loop__doc__, "_get_running_loop($module, /)\n" "--\n" @@ -1547,4 +1568,4 @@ _asyncio_all_tasks(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py exit: return return_value; } -/*[clinic end generated code: output=ffe9b71bc65888b3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3e7f57f7c23221c9 input=a9049054013a1b77]*/ diff --git a/Objects/object.c b/Objects/object.c index c4622359bb1035..bc5b4c127886b9 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -815,6 +815,8 @@ _PyObject_ClearFreeLists(struct _Py_object_freelists *freelists, int is_finaliza _PyDict_ClearFreeList(freelists, is_finalization); _PyContext_ClearFreeList(freelists, is_finalization); _PyAsyncGen_ClearFreeLists(freelists, is_finalization); + _PyAsyncModule_ClearFreeLists(freelists, is_finalization); + // Only be cleared if is_finalization is true. _PyObjectStackChunk_ClearFreeList(freelists, is_finalization); _PySlice_ClearFreeList(freelists, is_finalization); From c9ac6bb51982afef19ee687b978fe21262c73c64 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 13 Jul 2024 23:18:49 +0800 Subject: [PATCH 09/20] Move running loop to thread state --- Include/cpython/pystate.h | 3 +++ Modules/_asynciomodule.c | 21 +++++++-------------- Python/pystate.c | 2 ++ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index bb2af78a376d75..a8d0bc067aaf86 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -164,6 +164,9 @@ struct _ts { PyObject *async_gen_firstiter; PyObject *async_gen_finalizer; + PyObject *asyncio_cached_running_loop; // Borrowed reference + volatile uint64_t asyncio_cached_running_loop_tsid; + PyObject *context; uint64_t context_ver; diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index a6d2c124a6946a..1d2c2e03594d49 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -135,9 +135,6 @@ typedef struct { /* Imports from traceback. */ PyObject *traceback_extract_stack; - PyObject *cached_running_loop; // Borrowed reference - volatile uint64_t cached_running_loop_tsid; - /* Counter for autogenerated Task names */ uint64_t task_name_counter; @@ -336,11 +333,11 @@ get_running_loop(asyncio_state *state, PyObject **loop) PyThreadState *ts = _PyThreadState_GET(); uint64_t ts_id = PyThreadState_GetID(ts); - if (state->cached_running_loop_tsid == ts_id && - state->cached_running_loop != NULL) + if (ts->asyncio_cached_running_loop_tsid == ts_id && + ts->asyncio_cached_running_loop != NULL) { // Fast path, check the cache. - rl = state->cached_running_loop; + rl = ts->asyncio_cached_running_loop; } else { PyObject *ts_dict = _PyThreadState_GetDict(ts); // borrowed @@ -359,10 +356,8 @@ get_running_loop(asyncio_state *state, PyObject **loop) } } - // TODO GH-121621: The should be moved to PyThreadState - // for easier and quicker access. - state->cached_running_loop = rl; - state->cached_running_loop_tsid = ts_id; + ts->asyncio_cached_running_loop = rl; + ts->asyncio_cached_running_loop_tsid = ts_id; } @@ -405,10 +400,8 @@ set_running_loop(asyncio_state *state, PyObject *loop) } - // TODO GH-121621: The should be moved to PyThreadState - // for easier and quicker access. - state->cached_running_loop = loop; // borrowed, kept alive by ts_dict - state->cached_running_loop_tsid = PyThreadState_GetID(tstate); + tstate->asyncio_cached_running_loop = loop; // borrowed, kept alive by ts_dict + tstate->asyncio_cached_running_loop_tsid = PyThreadState_GetID(tstate); return 0; } diff --git a/Python/pystate.c b/Python/pystate.c index 602b13e18c71ae..a74c6ce8c045a9 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1700,6 +1700,8 @@ PyThreadState_Clear(PyThreadState *tstate) /* Don't clear tstate->pyframe: it is a borrowed reference */ + /* Don't clear asyncio_cached_running_loop, it's borrowed. */ + Py_CLEAR(tstate->dict); Py_CLEAR(tstate->async_exc); From 3d78e8a66c910e7f5ea7fef8fde30aeec9cb85ec Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 16 Jul 2024 02:32:15 +0800 Subject: [PATCH 10/20] remove freelist changes --- Include/internal/pycore_freelist.h | 42 ------------- Modules/_asynciomodule.c | 97 +++++++++++++----------------- Objects/object.c | 1 - 3 files changed, 43 insertions(+), 97 deletions(-) diff --git a/Include/internal/pycore_freelist.h b/Include/internal/pycore_freelist.h index 3cc8b12df0b778..e684e084b8bef8 100644 --- a/Include/internal/pycore_freelist.h +++ b/Include/internal/pycore_freelist.h @@ -124,14 +124,6 @@ struct _Py_object_stack_freelist { Py_ssize_t numfree; }; - -struct _Py_asyncmodule_futureiter_freelist { -#ifdef WITH_FREELISTS - struct futureiterobject *fi_freelist; - Py_ssize_t fi_freelist_len; -#endif -}; - struct _Py_object_freelists { struct _Py_float_freelist floats; struct _Py_tuple_freelist tuples; @@ -143,7 +135,6 @@ struct _Py_object_freelists { struct _Py_async_gen_freelist async_gens; struct _Py_async_gen_asend_freelist async_gen_asends; struct _Py_object_stack_freelist object_stacks; - struct _Py_asyncmodule_futureiter_freelist futureiters; }; extern void _PyObject_ClearFreeLists(struct _Py_object_freelists *freelists, int is_finalization); @@ -156,39 +147,6 @@ extern void _PyAsyncGen_ClearFreeLists(struct _Py_object_freelists *freelists, i extern void _PyContext_ClearFreeList(struct _Py_object_freelists *freelists, int is_finalization); extern void _PyObjectStackChunk_ClearFreeList(struct _Py_object_freelists *freelists, int is_finalization); -// Keep in sync with _asynciomodule.c ! -typedef struct futureiterobject_dummy { - PyObject_HEAD - void *future; -} futureiterobject_dummy; - -static inline void -_PyAsyncModule_ClearFreeLists(struct _Py_object_freelists *freelists, int is_finalization) -{ -#ifdef WITH_FREELISTS - PyObject *next; - PyObject *current; - - next = (PyObject*) freelists->futureiters.fi_freelist; - while (next != NULL) { - assert(freelists->futureiters.fi_freelist_len > 0); - freelists->futureiters.fi_freelist_len--; - - current = next; - next = (PyObject*) ((futureiterobject_dummy*) current)->future; - PyObject_GC_Del(current); - } - assert(freelists->futureiters.fi_freelist_len == 0 || freelists->futureiters.fi_freelist_len == -1); - freelists->futureiters.fi_freelist = NULL; - if (is_finalization) { - freelists->futureiters.fi_freelist_len = -1; - } - else { - freelists->futureiters.fi_freelist_len = 0; - } -#endif -} - #ifdef __cplusplus } #endif diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 60e2690f3a011c..431b3d3e707418 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -138,6 +138,9 @@ 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. @@ -214,16 +217,6 @@ 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]*/ @@ -1668,19 +1661,25 @@ 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); -#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; + // 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); } - else -#endif - { + + // 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; + } + else { PyObject_GC_Del(it); Py_DECREF(tp); } @@ -1883,19 +1882,14 @@ future_new_iter(PyObject *fut) asyncio_state *state = get_asyncio_state_by_def((PyObject *)fut); ENSURE_FUTURE_ALIVE(state, fut) - -#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; + if (state->fi_freelist_len) { + state->fi_freelist_len--; + it = state->fi_freelist; + state->fi_freelist = (futureiterobject*) it->future; it->future = NULL; _Py_NewReference((PyObject*) it); } - else -#endif - { + else { it = PyObject_GC_New(futureiterobject, state->FutureIterType); if (it == NULL) { return NULL; @@ -3351,24 +3345,6 @@ 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 @@ -3784,6 +3760,24 @@ _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) @@ -3814,15 +3808,12 @@ module_traverse(PyObject *mod, visitproc visit, void *arg) Py_VISIT(state->context_kwname); // Visit freelist. -#ifdef WITH_FREELISTS - struct _Py_asyncmodule_futureiter_freelist* freelist = get_futureiter_freelist(); - PyObject *next = (PyObject*) freelist->fi_freelist; + PyObject *next = (PyObject*) state->fi_freelist; while (next != NULL) { PyObject *current = next; Py_VISIT(current); next = (PyObject*) ((futureiterobject*) current)->future; } -#endif return 0; } @@ -3854,8 +3845,7 @@ module_clear(PyObject *mod) Py_CLEAR(state->context_kwname); - _PyAsyncModule_ClearFreeLists(_Py_object_freelists_GET(), 0); - + module_free_freelists(state); return 0; } @@ -3967,7 +3957,6 @@ static PyMethodDef asyncio_methods[] = { _ASYNCIO__LEAVE_TASK_METHODDEF _ASYNCIO__SWAP_CURRENT_TASK_METHODDEF _ASYNCIO_ALL_TASKS_METHODDEF - _ASYNCIO__CLEAR_FREELIST_METHODDEF {NULL, NULL} }; diff --git a/Objects/object.c b/Objects/object.c index bc5b4c127886b9..cc272e660fb15e 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -815,7 +815,6 @@ _PyObject_ClearFreeLists(struct _Py_object_freelists *freelists, int is_finaliza _PyDict_ClearFreeList(freelists, is_finalization); _PyContext_ClearFreeList(freelists, is_finalization); _PyAsyncGen_ClearFreeLists(freelists, is_finalization); - _PyAsyncModule_ClearFreeLists(freelists, is_finalization); // Only be cleared if is_finalization is true. _PyObjectStackChunk_ClearFreeList(freelists, is_finalization); From 4f03f714c6631eeb9086c66a97703017cbb86600 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 16 Jul 2024 03:26:31 +0800 Subject: [PATCH 11/20] Use just a single threadstate field --- Include/cpython/pystate.h | 1 - Lib/test/libregrtest/utils.py | 6 -- Lib/test/test_asyncio/test_base_events.py | 2 +- Modules/_asynciomodule.c | 92 +++-------------------- Modules/clinic/_asynciomodule.c.h | 23 +----- Python/pystate.c | 4 +- 6 files changed, 16 insertions(+), 112 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index a8d0bc067aaf86..1781adac1d4139 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -165,7 +165,6 @@ struct _ts { PyObject *async_gen_finalizer; PyObject *asyncio_cached_running_loop; // Borrowed reference - volatile uint64_t asyncio_cached_running_loop_tsid; PyObject *context; uint64_t context_ver; diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index ccc4f4523866b3..7ec19139f0b89d 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -293,12 +293,6 @@ def clear_caches(): else: importlib_metadata.FastPath.__new__.cache_clear() - try: - _asyncio = sys.modules['_asyncio'] - except KeyError: - pass - else: - _asyncio._clear_freelist() def get_build_info(): diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index c14a0bb180d79b..dc52e1bedc45c8 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -968,7 +968,7 @@ def callback(): # Confirm the loop has been cleaned up with self.assertRaises(RuntimeError): - asyncio.get_running_loop() + raise Exception(asyncio.get_running_loop()) self.assertFalse(self.loop.is_running()) # Confirm the loop actually did run, processing events 10 times, diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 431b3d3e707418..831d20dfeadece 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -319,82 +319,20 @@ get_future_loop(asyncio_state *state, PyObject *fut) } -static int +static void get_running_loop(asyncio_state *state, PyObject **loop) { - PyObject *rl; - PyThreadState *ts = _PyThreadState_GET(); - uint64_t ts_id = PyThreadState_GetID(ts); - if (ts->asyncio_cached_running_loop_tsid == ts_id && - ts->asyncio_cached_running_loop != NULL) - { - // Fast path, check the cache. - rl = ts->asyncio_cached_running_loop; - } - else { - PyObject *ts_dict = _PyThreadState_GetDict(ts); // borrowed - if (ts_dict == NULL) { - goto not_found; - } - - rl = PyDict_GetItemWithError( - ts_dict, &_Py_ID(__asyncio_running_event_loop__)); // borrowed - if (rl == NULL) { - if (PyErr_Occurred()) { - goto error; - } - else { - goto not_found; - } - } - - ts->asyncio_cached_running_loop = rl; - ts->asyncio_cached_running_loop_tsid = ts_id; - } - - - if (rl == Py_None) { - goto not_found; - } - - *loop = Py_NewRef(rl); - return 0; - -not_found: - *loop = NULL; - return 0; - -error: - *loop = NULL; - return -1; + assert(ts->asyncio_cached_running_loop != NULL); + *loop = Py_NewRef(ts->asyncio_cached_running_loop); } static int set_running_loop(asyncio_state *state, PyObject *loop) { - PyObject *ts_dict = NULL; - PyThreadState *tstate = _PyThreadState_GET(); - if (tstate != NULL) { - ts_dict = _PyThreadState_GetDict(tstate); // borrowed - } - - if (ts_dict == NULL) { - PyErr_SetString( - PyExc_RuntimeError, "thread-local storage is not available"); - return -1; - } - if (PyDict_SetItem( - ts_dict, &_Py_ID(__asyncio_running_event_loop__), loop) < 0) - { - return -1; - } - - - tstate->asyncio_cached_running_loop = loop; // borrowed, kept alive by ts_dict - tstate->asyncio_cached_running_loop_tsid = PyThreadState_GetID(tstate); + Py_XSETREF(tstate->asyncio_cached_running_loop, Py_NewRef(loop)); return 0; } @@ -406,10 +344,9 @@ get_event_loop(asyncio_state *state) PyObject *loop; PyObject *policy; - if (get_running_loop(state, &loop)) { - return NULL; - } - if (loop != NULL) { + get_running_loop(state, &loop); + + if (loop != NULL && loop != Py_None) { return loop; } @@ -3361,13 +3298,7 @@ _asyncio__get_running_loop_impl(PyObject *module) { PyObject *loop; asyncio_state *state = get_asyncio_state(module); - if (get_running_loop(state, &loop)) { - return NULL; - } - if (loop == NULL) { - /* There's no currently running event loop */ - Py_RETURN_NONE; - } + get_running_loop(state, &loop); return loop; } @@ -3428,13 +3359,12 @@ _asyncio_get_running_loop_impl(PyObject *module) { PyObject *loop; asyncio_state *state = get_asyncio_state(module); - if (get_running_loop(state, &loop)) { - return NULL; - } - if (loop == NULL) { + get_running_loop(state, &loop); + if (loop == Py_None) { /* There's no currently running event loop */ PyErr_SetString( PyExc_RuntimeError, "no running event loop"); + return NULL; } return loop; } diff --git a/Modules/clinic/_asynciomodule.c.h b/Modules/clinic/_asynciomodule.c.h index b7de9440ade4e8..d619a124ccead5 100644 --- a/Modules/clinic/_asynciomodule.c.h +++ b/Modules/clinic/_asynciomodule.c.h @@ -940,27 +940,6 @@ PyDoc_STRVAR(_asyncio_Task_set_name__doc__, #define _ASYNCIO_TASK_SET_NAME_METHODDEF \ {"set_name", (PyCFunction)_asyncio_Task_set_name, METH_O, _asyncio_Task_set_name__doc__}, -PyDoc_STRVAR(_asyncio__clear_freelist__doc__, -"_clear_freelist($module, /)\n" -"--\n" -"\n" -"Clears the asyncio freelist.\n" -"\n" -"Internal CPython implementation detail. Do not depend on this or use it!\n" -"This function is thread-specific."); - -#define _ASYNCIO__CLEAR_FREELIST_METHODDEF \ - {"_clear_freelist", (PyCFunction)_asyncio__clear_freelist, METH_NOARGS, _asyncio__clear_freelist__doc__}, - -static PyObject * -_asyncio__clear_freelist_impl(PyObject *module); - -static PyObject * -_asyncio__clear_freelist(PyObject *module, PyObject *Py_UNUSED(ignored)) -{ - return _asyncio__clear_freelist_impl(module); -} - PyDoc_STRVAR(_asyncio__get_running_loop__doc__, "_get_running_loop($module, /)\n" "--\n" @@ -1568,4 +1547,4 @@ _asyncio_all_tasks(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py exit: return return_value; } -/*[clinic end generated code: output=3e7f57f7c23221c9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ffe9b71bc65888b3 input=a9049054013a1b77]*/ diff --git a/Python/pystate.c b/Python/pystate.c index a74c6ce8c045a9..60d0d32c77e81d 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1499,6 +1499,8 @@ init_threadstate(_PyThreadStateImpl *_tstate, tstate->previous_executor = NULL; tstate->dict_global_version = 0; + tstate->asyncio_cached_running_loop = Py_None; + tstate->delete_later = NULL; llist_init(&_tstate->mem_free_queue); @@ -1700,7 +1702,7 @@ PyThreadState_Clear(PyThreadState *tstate) /* Don't clear tstate->pyframe: it is a borrowed reference */ - /* Don't clear asyncio_cached_running_loop, it's borrowed. */ + Py_CLEAR(tstate->asyncio_cached_running_loop); Py_CLEAR(tstate->dict); Py_CLEAR(tstate->async_exc); From a1ce7606ae2fa2078f7e2cd1b4fe6f63c9568659 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 16 Jul 2024 03:28:26 +0800 Subject: [PATCH 12/20] fix whitespace --- Lib/test/libregrtest/utils.py | 1 - Modules/_asynciomodule.c | 1 + Objects/object.c | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index 7ec19139f0b89d..2a3449016fe951 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -294,7 +294,6 @@ def clear_caches(): importlib_metadata.FastPath.__new__.cache_clear() - def get_build_info(): # Get most important configure and build options as a list of strings. # Example: ['debug', 'ASAN+MSAN'] or ['release', 'LTO+PGO']. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 831d20dfeadece..cc671ed1ae0dbc 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1819,6 +1819,7 @@ 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; diff --git a/Objects/object.c b/Objects/object.c index cc272e660fb15e..c4622359bb1035 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -815,7 +815,6 @@ _PyObject_ClearFreeLists(struct _Py_object_freelists *freelists, int is_finaliza _PyDict_ClearFreeList(freelists, is_finalization); _PyContext_ClearFreeList(freelists, is_finalization); _PyAsyncGen_ClearFreeLists(freelists, is_finalization); - // Only be cleared if is_finalization is true. _PyObjectStackChunk_ClearFreeList(freelists, is_finalization); _PySlice_ClearFreeList(freelists, is_finalization); From f3f1c5708af885ee544e069b4930eee089d80624 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 16 Jul 2024 03:31:15 +0800 Subject: [PATCH 13/20] fix stray thing --- Lib/test/test_asyncio/test_base_events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index dc52e1bedc45c8..c14a0bb180d79b 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -968,7 +968,7 @@ def callback(): # Confirm the loop has been cleaned up with self.assertRaises(RuntimeError): - raise Exception(asyncio.get_running_loop()) + asyncio.get_running_loop() self.assertFalse(self.loop.is_running()) # Confirm the loop actually did run, processing events 10 times, From 4e3c64a9896d700f0376dcfb04e4dffd94be8541 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 16 Jul 2024 03:31:37 +0800 Subject: [PATCH 14/20] Update comment --- Include/cpython/pystate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 1781adac1d4139..35df68a984f679 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -164,7 +164,7 @@ struct _ts { PyObject *async_gen_firstiter; PyObject *async_gen_finalizer; - PyObject *asyncio_cached_running_loop; // Borrowed reference + PyObject *asyncio_cached_running_loop; // Strong reference PyObject *context; uint64_t context_ver; From 19953a8d8acaa985e27e562799bd7ee15578624a Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 16 Jul 2024 03:32:43 +0800 Subject: [PATCH 15/20] Clean up code even more --- Modules/_asynciomodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index cc671ed1ae0dbc..06ef5449ed70fc 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -346,7 +346,7 @@ get_event_loop(asyncio_state *state) get_running_loop(state, &loop); - if (loop != NULL && loop != Py_None) { + if (loop != Py_None) { return loop; } From ba039eea88ff54698ed478435f12a9c237f3d9bd Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 16 Jul 2024 03:58:26 +0800 Subject: [PATCH 16/20] make regen-all --- Include/internal/pycore_global_objects_fini_generated.h | 1 - Include/internal/pycore_global_strings.h | 1 - Include/internal/pycore_runtime_init_generated.h | 1 - Include/internal/pycore_unicodeobject_generated.h | 4 ---- 4 files changed, 7 deletions(-) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index c0840f9eb7eca2..d9b46df507dfd7 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -588,7 +588,6 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__annotate__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__annotations__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__args__)); - _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__asyncio_running_event_loop__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__await__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bases__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bool__)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 51735a8a726e11..10773d7a6c7e3f 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -77,7 +77,6 @@ struct _Py_global_strings { STRUCT_FOR_ID(__annotate__) STRUCT_FOR_ID(__annotations__) STRUCT_FOR_ID(__args__) - STRUCT_FOR_ID(__asyncio_running_event_loop__) STRUCT_FOR_ID(__await__) STRUCT_FOR_ID(__bases__) STRUCT_FOR_ID(__bool__) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index c5be67c6d80b9d..618f8d0a36b6c3 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -586,7 +586,6 @@ extern "C" { INIT_ID(__annotate__), \ INIT_ID(__annotations__), \ INIT_ID(__args__), \ - INIT_ID(__asyncio_running_event_loop__), \ INIT_ID(__await__), \ INIT_ID(__bases__), \ INIT_ID(__bool__), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 0e0ad6518771e9..f848a002c3b5d1 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -108,10 +108,6 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); - string = &_Py_ID(__asyncio_running_event_loop__); - _PyUnicode_InternStatic(interp, &string); - assert(_PyUnicode_CheckConsistency(string, 1)); - assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(__await__); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); From 34292311a7ff15434d1d09e02e02d0f2f58028f1 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 16 Jul 2024 15:14:09 +0800 Subject: [PATCH 17/20] Partially address review --- Include/cpython/pystate.h | 2 +- Modules/_asynciomodule.c | 40 +++++++++++---------------------------- Python/pystate.c | 4 ++-- 3 files changed, 14 insertions(+), 32 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 35df68a984f679..42119eed681075 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -164,7 +164,7 @@ struct _ts { PyObject *async_gen_firstiter; PyObject *async_gen_finalizer; - PyObject *asyncio_cached_running_loop; // Strong reference + PyObject *asyncio_running_loop; // Strong reference PyObject *context; uint64_t context_ver; diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 06ef5449ed70fc..47c5cfd2ff9cbe 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -318,33 +318,15 @@ get_future_loop(asyncio_state *state, PyObject *fut) return PyObject_GetAttr(fut, &_Py_ID(_loop)); } - -static void -get_running_loop(asyncio_state *state, PyObject **loop) -{ - PyThreadState *ts = _PyThreadState_GET(); - assert(ts->asyncio_cached_running_loop != NULL); - *loop = Py_NewRef(ts->asyncio_cached_running_loop); -} - - -static int -set_running_loop(asyncio_state *state, PyObject *loop) -{ - PyThreadState *tstate = _PyThreadState_GET(); - Py_XSETREF(tstate->asyncio_cached_running_loop, Py_NewRef(loop)); - - return 0; -} - - static PyObject * get_event_loop(asyncio_state *state) { PyObject *loop; PyObject *policy; - get_running_loop(state, &loop); + PyThreadState *ts = _PyThreadState_GET(); + assert(ts->asyncio_running_loop != NULL); + loop = Py_NewRef(ts->asyncio_running_loop); if (loop != Py_None) { return loop; @@ -3298,8 +3280,9 @@ _asyncio__get_running_loop_impl(PyObject *module) /*[clinic end generated code: output=b4390af721411a0a input=0a21627e25a4bd43]*/ { PyObject *loop; - asyncio_state *state = get_asyncio_state(module); - get_running_loop(state, &loop); + PyThreadState *ts = _PyThreadState_GET(); + assert(ts->asyncio_running_loop != NULL); + loop = Py_NewRef(ts->asyncio_running_loop); return loop; } @@ -3318,10 +3301,8 @@ static PyObject * _asyncio__set_running_loop(PyObject *module, PyObject *loop) /*[clinic end generated code: output=ae56bf7a28ca189a input=4c9720233d606604]*/ { - asyncio_state *state = get_asyncio_state(module); - if (set_running_loop(state, loop)) { - return NULL; - } + PyThreadState *tstate = _PyThreadState_GET(); + Py_SETREF(tstate->asyncio_running_loop, Py_NewRef(loop)); Py_RETURN_NONE; } @@ -3359,8 +3340,9 @@ _asyncio_get_running_loop_impl(PyObject *module) /*[clinic end generated code: output=c247b5f9e529530e input=2a3bf02ba39f173d]*/ { PyObject *loop; - asyncio_state *state = get_asyncio_state(module); - get_running_loop(state, &loop); + PyThreadState *ts = _PyThreadState_GET(); + assert(ts->asyncio_running_loop != NULL); + loop = Py_NewRef(ts->asyncio_running_loop); if (loop == Py_None) { /* There's no currently running event loop */ PyErr_SetString( diff --git a/Python/pystate.c b/Python/pystate.c index 60d0d32c77e81d..86fbba9f032551 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1499,7 +1499,7 @@ init_threadstate(_PyThreadStateImpl *_tstate, tstate->previous_executor = NULL; tstate->dict_global_version = 0; - tstate->asyncio_cached_running_loop = Py_None; + tstate->asyncio_running_loop = Py_None; tstate->delete_later = NULL; @@ -1702,7 +1702,7 @@ PyThreadState_Clear(PyThreadState *tstate) /* Don't clear tstate->pyframe: it is a borrowed reference */ - Py_CLEAR(tstate->asyncio_cached_running_loop); + Py_SETREF(tstate->asyncio_running_loop, Py_None); Py_CLEAR(tstate->dict); Py_CLEAR(tstate->async_exc); From c6df89f89b66df9a294786d6ae5de0b03379a2f7 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:15:10 +0800 Subject: [PATCH 18/20] Address review --- Include/cpython/pystate.h | 4 ++-- Modules/_asynciomodule.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 42119eed681075..ce050424cccd49 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -68,6 +68,8 @@ struct _ts { pycore_ceval.h. */ uintptr_t eval_breaker; + PyObject *asyncio_running_loop; // Strong reference + struct { /* Has been initialized to a safe state. @@ -164,8 +166,6 @@ struct _ts { PyObject *async_gen_firstiter; PyObject *async_gen_finalizer; - PyObject *asyncio_running_loop; // Strong reference - PyObject *context; uint64_t context_ver; diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 47c5cfd2ff9cbe..25bc95ef98927c 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3301,8 +3301,8 @@ static PyObject * _asyncio__set_running_loop(PyObject *module, PyObject *loop) /*[clinic end generated code: output=ae56bf7a28ca189a input=4c9720233d606604]*/ { - PyThreadState *tstate = _PyThreadState_GET(); - Py_SETREF(tstate->asyncio_running_loop, Py_NewRef(loop)); + PyThreadState *ts = _PyThreadState_GET(); + Py_SETREF(ts->asyncio_running_loop, Py_NewRef(loop)); Py_RETURN_NONE; } From 91438a5eb361f8d82d844d20d7be39dc5eef02c2 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 17 Jul 2024 00:33:31 +0800 Subject: [PATCH 19/20] Change Py_None to NULL --- Modules/_asynciomodule.c | 22 ++++++++++++---------- Python/pystate.c | 4 ++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 25bc95ef98927c..4a4736b50d665a 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -325,10 +325,9 @@ get_event_loop(asyncio_state *state) PyObject *policy; PyThreadState *ts = _PyThreadState_GET(); - assert(ts->asyncio_running_loop != NULL); - loop = Py_NewRef(ts->asyncio_running_loop); + loop = Py_XNewRef(ts->asyncio_running_loop); - if (loop != Py_None) { + if (loop != NULL) { return loop; } @@ -3279,10 +3278,11 @@ static PyObject * _asyncio__get_running_loop_impl(PyObject *module) /*[clinic end generated code: output=b4390af721411a0a input=0a21627e25a4bd43]*/ { - PyObject *loop; PyThreadState *ts = _PyThreadState_GET(); - assert(ts->asyncio_running_loop != NULL); - loop = Py_NewRef(ts->asyncio_running_loop); + PyObject *loop = Py_XNewRef(ts->asyncio_running_loop); + if (loop == NULL) { + Py_RETURN_NONE; + } return loop; } @@ -3302,7 +3302,10 @@ _asyncio__set_running_loop(PyObject *module, PyObject *loop) /*[clinic end generated code: output=ae56bf7a28ca189a input=4c9720233d606604]*/ { PyThreadState *ts = _PyThreadState_GET(); - Py_SETREF(ts->asyncio_running_loop, Py_NewRef(loop)); + if (loop == Py_None) { + loop = NULL; + } + Py_XSETREF(ts->asyncio_running_loop, Py_XNewRef(loop)); Py_RETURN_NONE; } @@ -3341,9 +3344,8 @@ _asyncio_get_running_loop_impl(PyObject *module) { PyObject *loop; PyThreadState *ts = _PyThreadState_GET(); - assert(ts->asyncio_running_loop != NULL); - loop = Py_NewRef(ts->asyncio_running_loop); - if (loop == Py_None) { + loop = Py_XNewRef(ts->asyncio_running_loop); + if (loop == NULL) { /* There's no currently running event loop */ PyErr_SetString( PyExc_RuntimeError, "no running event loop"); diff --git a/Python/pystate.c b/Python/pystate.c index 86fbba9f032551..f77a2cc54e867a 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1499,7 +1499,7 @@ init_threadstate(_PyThreadStateImpl *_tstate, tstate->previous_executor = NULL; tstate->dict_global_version = 0; - tstate->asyncio_running_loop = Py_None; + tstate->asyncio_running_loop = NULL; tstate->delete_later = NULL; @@ -1702,7 +1702,7 @@ PyThreadState_Clear(PyThreadState *tstate) /* Don't clear tstate->pyframe: it is a borrowed reference */ - Py_SETREF(tstate->asyncio_running_loop, Py_None); + Py_CLEAR(tstate->asyncio_running_loop); Py_CLEAR(tstate->dict); Py_CLEAR(tstate->async_exc); From 46432e80797cb637b6b325e7b76e851793b3c39a Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 17 Jul 2024 00:35:08 +0800 Subject: [PATCH 20/20] add back missing comment --- Modules/_asynciomodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 4a4736b50d665a..b53ef5ea8f41fb 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3281,6 +3281,7 @@ _asyncio__get_running_loop_impl(PyObject *module) PyThreadState *ts = _PyThreadState_GET(); PyObject *loop = Py_XNewRef(ts->asyncio_running_loop); if (loop == NULL) { + /* There's no currently running event loop */ Py_RETURN_NONE; } return loop;