Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-100344: Provide C implementation for asyncio.current_task #100345

Merged
merged 11 commits into from
Dec 22, 2022
Merged
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ asyncio
a custom event loop factory.
(Contributed by Kumar Aditya in :gh:`99388`.)

* Add C implementation of :func:`asyncio.current_task` for 4x-6x speedup.
(Contributed by Itamar Ostricher and Pranav Thulasiram Bhat in :gh:`100344`.)

inspect
-------

Expand Down
10 changes: 8 additions & 2 deletions Lib/asyncio/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ def current_task(loop=None):
"""Return a currently executed task."""
if loop is None:
loop = events.get_running_loop()
return _current_tasks.get(loop)
try:
return _current_tasks[loop]
except KeyError:
return None


def all_tasks(loop=None):
Expand Down Expand Up @@ -964,6 +967,7 @@ def _unregister_task(task):
_all_tasks.discard(task)


_py_current_task = current_task
_py_register_task = _register_task
_py_unregister_task = _unregister_task
_py_enter_task = _enter_task
Expand All @@ -973,10 +977,12 @@ def _unregister_task(task):
try:
from _asyncio import (_register_task, _unregister_task,
_enter_task, _leave_task,
_all_tasks, _current_tasks)
_all_tasks, _current_tasks,
current_task)
except ImportError:
pass
else:
_c_current_task = current_task
_c_register_task = _register_task
_c_unregister_task = _unregister_task
_c_enter_task = _enter_task
Expand Down
21 changes: 14 additions & 7 deletions Lib/test/test_asyncio/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2804,6 +2804,7 @@ class CIntrospectionTests(test_utils.TestCase, BaseTaskIntrospectionTests):


class BaseCurrentLoopTests:
current_task = None

def setUp(self):
super().setUp()
Expand All @@ -2814,33 +2815,39 @@ def new_task(self, coro):
raise NotImplementedError

def test_current_task_no_running_loop(self):
self.assertIsNone(asyncio.current_task(loop=self.loop))
self.assertIsNone(self.current_task(loop=self.loop))

def test_current_task_no_running_loop_implicit(self):
with self.assertRaisesRegex(RuntimeError, 'no running event loop'):
asyncio.current_task()
self.current_task()

def test_current_task_with_implicit_loop(self):
async def coro():
self.assertIs(asyncio.current_task(loop=self.loop), task)
self.assertIs(self.current_task(loop=self.loop), task)

self.assertIs(asyncio.current_task(None), task)
self.assertIs(asyncio.current_task(), task)
self.assertIs(self.current_task(None), task)
self.assertIs(self.current_task(), task)

task = self.new_task(coro())
self.loop.run_until_complete(task)
self.assertIsNone(asyncio.current_task(loop=self.loop))
self.assertIsNone(self.current_task(loop=self.loop))


class PyCurrentLoopTests(BaseCurrentLoopTests, test_utils.TestCase):
current_task = staticmethod(tasks._py_current_task)

def new_task(self, coro):
return tasks._PyTask(coro, loop=self.loop)


@unittest.skipUnless(hasattr(tasks, '_CTask'),
@unittest.skipUnless(hasattr(tasks, '_CTask') and
hasattr(tasks, '_c_current_task'),
'requires the C _asyncio module')
class CCurrentLoopTests(BaseCurrentLoopTests, test_utils.TestCase):
if hasattr(tasks, '_c_current_task'):
current_task = staticmethod(tasks._c_current_task)
else:
current_task = None

def new_task(self, coro):
return getattr(tasks, '_CTask')(coro, loop=self.loop)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Provide C implementation for :func:`asyncio.current_task` for a 4x-6x
speedup.
39 changes: 39 additions & 0 deletions Modules/_asynciomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3314,6 +3314,44 @@ _asyncio__leave_task_impl(PyObject *module, PyObject *loop, PyObject *task)
}


/*[clinic input]
_asyncio.current_task

loop: object = None

Return a currently executed task.

[clinic start generated code]*/

static PyObject *
_asyncio_current_task_impl(PyObject *module, PyObject *loop)
/*[clinic end generated code: output=fe15ac331a7f981a input=58910f61a5627112]*/
{
PyObject *ret;
asyncio_state *state = get_asyncio_state(module);

if (loop == Py_None) {
loop = _asyncio_get_running_loop_impl(module);
if (loop == NULL) {
return NULL;
}
} else {
Py_INCREF(loop);
}

ret = PyDict_GetItemWithError(state->current_tasks, loop);
Py_DECREF(loop);
if (ret == NULL && PyErr_Occurred()) {
return NULL;
}
else if (ret == NULL) {
Py_RETURN_NONE;
}
Py_INCREF(ret);
return ret;
}


/*********************** Module **************************/


Expand Down Expand Up @@ -3494,6 +3532,7 @@ module_init(asyncio_state *state)
PyDoc_STRVAR(module_doc, "Accelerator module for asyncio");

static PyMethodDef asyncio_methods[] = {
_ASYNCIO_CURRENT_TASK_METHODDEF
_ASYNCIO_GET_EVENT_LOOP_METHODDEF
_ASYNCIO_GET_RUNNING_LOOP_METHODDEF
_ASYNCIO__GET_RUNNING_LOOP_METHODDEF
Expand Down
62 changes: 61 additions & 1 deletion Modules/clinic/_asynciomodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.