From 31f1b4839db4c6fd0162a93556e9d8941f419211 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Mon, 19 Dec 2022 12:21:27 +0000 Subject: [PATCH 1/4] optimize get_running_loop() by caching the result in module state --- Modules/_asynciomodule.c | 132 +++++---------------------------------- 1 file changed, 14 insertions(+), 118 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 60369d89dc39c9..b3060dcaa13783 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -23,7 +23,6 @@ typedef struct { PyTypeObject *TaskStepMethWrapper_Type; PyTypeObject *FutureType; PyTypeObject *TaskType; - PyTypeObject *PyRunningLoopHolder_Type; PyObject *asyncio_mod; PyObject *context_kwname; @@ -59,8 +58,8 @@ typedef struct { /* Imports from traceback. */ PyObject *traceback_extract_stack; - PyObject *cached_running_holder; // Borrowed ref. - volatile uint64_t cached_running_holder_tsid; + PyObject *cached_running_loop; // Borrowed reference + volatile uint64_t cached_running_loop_tsid; /* Counter for autogenerated Task names */ uint64_t task_name_counter; @@ -138,14 +137,6 @@ typedef struct { PyObject *sw_arg; } TaskStepMethWrapper; -typedef struct { - PyObject_HEAD - PyObject *rl_loop; -#if defined(HAVE_GETPID) && !defined(MS_WINDOWS) - pid_t rl_pid; -#endif -} PyRunningLoopHolder; - #define Future_CheckExact(state, obj) Py_IS_TYPE(obj, state->FutureType) #define Task_CheckExact(state, obj) Py_IS_TYPE(obj, state->TaskType) @@ -165,8 +156,6 @@ class _asyncio.Future "FutureObj *" "&Future_Type" /* Get FutureIter from Future */ static PyObject * future_new_iter(PyObject *); -static PyRunningLoopHolder * new_running_loop_holder(asyncio_state *, PyObject *); - static int _is_coroutine(asyncio_state *state, PyObject *coro) @@ -264,11 +253,11 @@ get_running_loop(asyncio_state *state, PyObject **loop) PyThreadState *ts = _PyThreadState_GET(); uint64_t ts_id = PyThreadState_GetID(ts); - if (state->cached_running_holder_tsid == ts_id && - state->cached_running_holder != NULL) + if (state->cached_running_loop_tsid == ts_id && + state->cached_running_loop != NULL) { // Fast path, check the cache. - rl = state->cached_running_holder; // borrowed + rl = state->cached_running_loop; } else { PyObject *ts_dict = _PyThreadState_GetDict(ts); // borrowed @@ -287,27 +276,16 @@ get_running_loop(asyncio_state *state, PyObject **loop) } } - state->cached_running_holder = rl; // borrowed - state->cached_running_holder_tsid = ts_id; + state->cached_running_loop = rl; + state->cached_running_loop_tsid = ts_id; } - assert(Py_IS_TYPE(rl, state->PyRunningLoopHolder_Type)); - PyObject *running_loop = ((PyRunningLoopHolder *)rl)->rl_loop; - - if (running_loop == Py_None) { - goto not_found; - } -#if defined(HAVE_GETPID) && !defined(MS_WINDOWS) - /* On Windows there is no getpid, but there is also no os.fork(), - so there is no need for this check. - */ - if (getpid() != ((PyRunningLoopHolder *)rl)->rl_pid) { + if (rl == Py_None) { goto not_found; } -#endif - *loop = Py_NewRef(running_loop); + *loop = Py_NewRef(rl); return 0; not_found: @@ -335,22 +313,16 @@ set_running_loop(asyncio_state *state, PyObject *loop) PyExc_RuntimeError, "thread-local storage is not available"); return -1; } - - PyRunningLoopHolder *rl = new_running_loop_holder(state, loop); - if (rl == NULL) { - return -1; - } - + Py_INCREF(loop); if (PyDict_SetItem( - ts_dict, &_Py_ID(__asyncio_running_event_loop__), (PyObject *)rl) < 0) + ts_dict, &_Py_ID(__asyncio_running_event_loop__), loop) < 0) { - Py_DECREF(rl); // will cleanup loop & current_pid + Py_DECREF(loop); return -1; } - Py_DECREF(rl); - state->cached_running_holder = (PyObject *)rl; - state->cached_running_holder_tsid = PyThreadState_GetID(tstate); + state->cached_running_loop = loop; + state->cached_running_loop_tsid = PyThreadState_GetID(tstate); return 0; } @@ -3344,79 +3316,6 @@ _asyncio__leave_task_impl(PyObject *module, PyObject *loop, PyObject *task) } -/*********************** PyRunningLoopHolder ********************/ - - -static PyRunningLoopHolder * -new_running_loop_holder(asyncio_state *state, PyObject *loop) -{ - PyRunningLoopHolder *rl = PyObject_GC_New( - PyRunningLoopHolder, state->PyRunningLoopHolder_Type); - if (rl == NULL) { - return NULL; - } - -#if defined(HAVE_GETPID) && !defined(MS_WINDOWS) - rl->rl_pid = getpid(); -#endif - rl->rl_loop = Py_NewRef(loop); - - PyObject_GC_Track(rl); - return rl; -} - - -static int -PyRunningLoopHolder_clear(PyRunningLoopHolder *rl) -{ - Py_CLEAR(rl->rl_loop); - return 0; -} - - -static int -PyRunningLoopHolder_traverse(PyRunningLoopHolder *rl, visitproc visit, - void *arg) -{ - Py_VISIT(Py_TYPE(rl)); - Py_VISIT(rl->rl_loop); - return 0; -} - - -static void -PyRunningLoopHolder_tp_dealloc(PyRunningLoopHolder *rl) -{ - asyncio_state *state = get_asyncio_state_by_def((PyObject *)rl); - if (state->cached_running_holder == (PyObject *)rl) { - state->cached_running_holder = NULL; - } - PyTypeObject *tp = Py_TYPE(rl); - PyObject_GC_UnTrack(rl); - PyRunningLoopHolder_clear(rl); - PyObject_GC_Del(rl); - Py_DECREF(tp); -} - - -static PyType_Slot PyRunningLoopHolder_slots[] = { - {Py_tp_getattro, PyObject_GenericGetAttr}, - {Py_tp_dealloc, (destructor)PyRunningLoopHolder_tp_dealloc}, - {Py_tp_traverse, (traverseproc)PyRunningLoopHolder_traverse}, - {Py_tp_clear, PyRunningLoopHolder_clear}, - {0, NULL}, -}; - - -static PyType_Spec PyRunningLoopHolder_spec = { - .name = "_asyncio._RunningLoopHolder", - .basicsize = sizeof(PyRunningLoopHolder), - .slots = PyRunningLoopHolder_slots, - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_IMMUTABLETYPE), -}; - - /*********************** Module **************************/ @@ -3448,7 +3347,6 @@ module_traverse(PyObject *mod, visitproc visit, void *arg) Py_VISIT(state->TaskStepMethWrapper_Type); Py_VISIT(state->FutureType); Py_VISIT(state->TaskType); - Py_VISIT(state->PyRunningLoopHolder_Type); Py_VISIT(state->asyncio_mod); Py_VISIT(state->traceback_extract_stack); @@ -3486,7 +3384,6 @@ module_clear(PyObject *mod) Py_CLEAR(state->TaskStepMethWrapper_Type); Py_CLEAR(state->FutureType); Py_CLEAR(state->TaskType); - Py_CLEAR(state->PyRunningLoopHolder_Type); Py_CLEAR(state->asyncio_mod); Py_CLEAR(state->traceback_extract_stack); @@ -3625,7 +3522,6 @@ module_exec(PyObject *mod) } while (0) CREATE_TYPE(mod, state->TaskStepMethWrapper_Type, &TaskStepMethWrapper_spec, NULL); - CREATE_TYPE(mod, state->PyRunningLoopHolder_Type, &PyRunningLoopHolder_spec, NULL); CREATE_TYPE(mod, state->FutureIterType, &FutureIter_spec, NULL); CREATE_TYPE(mod, state->FutureType, &Future_spec, NULL); CREATE_TYPE(mod, state->TaskType, &Task_spec, state->FutureType); From 34f2d3e274cbda09b9ded828974e1bdf1c72baef Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Mon, 19 Dec 2022 12:38:29 +0000 Subject: [PATCH 2/4] fix refleak --- Lib/asyncio/events.py | 1 + Modules/_asynciomodule.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index 34a8869dff8def..6cff8c59ea4779 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -836,6 +836,7 @@ def on_fork(): # Reset the loop and wakeupfd in the forked child process. if _event_loop_policy is not None: _event_loop_policy._local = BaseDefaultEventLoopPolicy._Local() + _set_running_loop(None) signal.set_wakeup_fd(-1) os.register_at_fork(after_in_child=on_fork) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index b3060dcaa13783..4358965e692e37 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -313,7 +313,6 @@ set_running_loop(asyncio_state *state, PyObject *loop) PyExc_RuntimeError, "thread-local storage is not available"); return -1; } - Py_INCREF(loop); if (PyDict_SetItem( ts_dict, &_Py_ID(__asyncio_running_event_loop__), loop) < 0) { From 14d9c6bfc6edb2489e48fd3b57ffe7bebfc9cc90 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Mon, 19 Dec 2022 12:40:22 +0000 Subject: [PATCH 3/4] add comment & remove stray decref --- Modules/_asynciomodule.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 4358965e692e37..32be537c00a524 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -316,11 +316,10 @@ set_running_loop(asyncio_state *state, PyObject *loop) if (PyDict_SetItem( ts_dict, &_Py_ID(__asyncio_running_event_loop__), loop) < 0) { - Py_DECREF(loop); return -1; } - state->cached_running_loop = loop; + state->cached_running_loop = loop; // borrowed, kept alive by ts_dict state->cached_running_loop_tsid = PyThreadState_GetID(tstate); return 0; From 8aba133639555f9899d31da19d3a3475e82656d6 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 20 Dec 2022 11:07:32 +0000 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2022-12-20-11-07-30.gh-issue-100363.Wo_Beg.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2022-12-20-11-07-30.gh-issue-100363.Wo_Beg.rst diff --git a/Misc/NEWS.d/next/Library/2022-12-20-11-07-30.gh-issue-100363.Wo_Beg.rst b/Misc/NEWS.d/next/Library/2022-12-20-11-07-30.gh-issue-100363.Wo_Beg.rst new file mode 100644 index 00000000000000..69bb5295613745 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-20-11-07-30.gh-issue-100363.Wo_Beg.rst @@ -0,0 +1 @@ +Speed up :func:`asyncio.get_running_loop` by removing redundant ``getpid`` checks. Patch by Kumar Aditya.