Skip to content

Commit

Permalink
GH-100363: Speed up asyncio.get_running_loop (#100364)
Browse files Browse the repository at this point in the history
  • Loading branch information
kumaraditya303 committed Dec 21, 2022
1 parent 79311cb commit 4994f24
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 118 deletions.
1 change: 1 addition & 0 deletions Lib/asyncio/events.py
Expand Up @@ -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)
@@ -0,0 +1 @@
Speed up :func:`asyncio.get_running_loop` by removing redundant ``getpid`` checks. Patch by Kumar Aditya.
130 changes: 12 additions & 118 deletions Modules/_asynciomodule.c
Expand Up @@ -23,7 +23,6 @@ typedef struct {
PyTypeObject *TaskStepMethWrapper_Type;
PyTypeObject *FutureType;
PyTypeObject *TaskType;
PyTypeObject *PyRunningLoopHolder_Type;

PyObject *asyncio_mod;
PyObject *context_kwname;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -335,22 +313,14 @@ 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;
}

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
return -1;
}
Py_DECREF(rl);

state->cached_running_holder = (PyObject *)rl;
state->cached_running_holder_tsid = PyThreadState_GetID(tstate);
state->cached_running_loop = loop; // borrowed, kept alive by ts_dict
state->cached_running_loop_tsid = PyThreadState_GetID(tstate);

return 0;
}
Expand Down Expand Up @@ -3344,79 +3314,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 **************************/


Expand Down Expand Up @@ -3448,7 +3345,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);
Expand Down Expand Up @@ -3486,7 +3382,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);
Expand Down Expand Up @@ -3625,7 +3520,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);
Expand Down

0 comments on commit 4994f24

Please sign in to comment.