mirror of
https://github.com/python/cpython.git
synced 2025-11-01 14:11:41 +00:00
GH-107803: double linked list implementation for asyncio tasks (GH-107804)
* linked list * add tail optmiization to linked list * wip * wip * wip * more fixes * finally it works * add tests * remove weakreflist * add some comments * reduce code duplication in _asynciomodule.c * address some review comments * add invariants about the state of the linked list * add better explanation * clinic regen * reorder branches for better branch prediction * Update Modules/_asynciomodule.c * Apply suggestions from code review Co-authored-by: Itamar Oren <itamarost@gmail.com> * fix capturing of eager tasks * add comment to task finalization * fix tests and couple c implmentation to c task improved linked-list logic and more comments * fix test --------- Co-authored-by: Itamar Oren <itamarost@gmail.com>
This commit is contained in:
parent
e213475495
commit
4717aaa1a7
8 changed files with 374 additions and 96 deletions
|
|
@ -86,6 +86,7 @@ class BaseTaskTests:
|
|||
|
||||
Task = None
|
||||
Future = None
|
||||
all_tasks = None
|
||||
|
||||
def new_task(self, loop, coro, name='TestTask', context=None):
|
||||
return self.__class__.Task(coro, loop=loop, name=name, context=context)
|
||||
|
|
@ -2267,7 +2268,7 @@ async def kill_me(loop):
|
|||
coro = kill_me(self.loop)
|
||||
task = asyncio.ensure_future(coro, loop=self.loop)
|
||||
|
||||
self.assertEqual(asyncio.all_tasks(loop=self.loop), {task})
|
||||
self.assertEqual(self.all_tasks(loop=self.loop), {task})
|
||||
|
||||
asyncio.set_event_loop(None)
|
||||
|
||||
|
|
@ -2282,7 +2283,7 @@ async def kill_me(loop):
|
|||
# no more reference to kill_me() task: the task is destroyed by the GC
|
||||
support.gc_collect()
|
||||
|
||||
self.assertEqual(asyncio.all_tasks(loop=self.loop), set())
|
||||
self.assertEqual(self.all_tasks(loop=self.loop), set())
|
||||
|
||||
mock_handler.assert_called_with(self.loop, {
|
||||
'message': 'Task was destroyed but it is pending!',
|
||||
|
|
@ -2431,7 +2432,7 @@ async def coro():
|
|||
message = m_log.error.call_args[0][0]
|
||||
self.assertIn('Task was destroyed but it is pending', message)
|
||||
|
||||
self.assertEqual(asyncio.all_tasks(self.loop), set())
|
||||
self.assertEqual(self.all_tasks(self.loop), set())
|
||||
|
||||
def test_create_task_with_noncoroutine(self):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
|
|
@ -2731,6 +2732,7 @@ async def func():
|
|||
# Add patched Task & Future back to the test case
|
||||
cls.Task = Task
|
||||
cls.Future = Future
|
||||
cls.all_tasks = tasks.all_tasks
|
||||
|
||||
# Add an extra unit-test
|
||||
cls.test_subclasses_ctask_cfuture = test_subclasses_ctask_cfuture
|
||||
|
|
@ -2804,6 +2806,7 @@ class CTask_CFuture_Tests(BaseTaskTests, SetMethodsTest,
|
|||
|
||||
Task = getattr(tasks, '_CTask', None)
|
||||
Future = getattr(futures, '_CFuture', None)
|
||||
all_tasks = getattr(tasks, '_c_all_tasks', None)
|
||||
|
||||
@support.refcount_test
|
||||
def test_refleaks_in_task___init__(self):
|
||||
|
|
@ -2835,6 +2838,7 @@ class CTask_CFuture_SubclassTests(BaseTaskTests, test_utils.TestCase):
|
|||
|
||||
Task = getattr(tasks, '_CTask', None)
|
||||
Future = getattr(futures, '_CFuture', None)
|
||||
all_tasks = getattr(tasks, '_c_all_tasks', None)
|
||||
|
||||
|
||||
@unittest.skipUnless(hasattr(tasks, '_CTask'),
|
||||
|
|
@ -2844,6 +2848,7 @@ class CTaskSubclass_PyFuture_Tests(BaseTaskTests, test_utils.TestCase):
|
|||
|
||||
Task = getattr(tasks, '_CTask', None)
|
||||
Future = futures._PyFuture
|
||||
all_tasks = getattr(tasks, '_c_all_tasks', None)
|
||||
|
||||
|
||||
@unittest.skipUnless(hasattr(futures, '_CFuture'),
|
||||
|
|
@ -2853,6 +2858,7 @@ class PyTask_CFutureSubclass_Tests(BaseTaskTests, test_utils.TestCase):
|
|||
|
||||
Future = getattr(futures, '_CFuture', None)
|
||||
Task = tasks._PyTask
|
||||
all_tasks = tasks._py_all_tasks
|
||||
|
||||
|
||||
@unittest.skipUnless(hasattr(tasks, '_CTask'),
|
||||
|
|
@ -2861,6 +2867,7 @@ class CTask_PyFuture_Tests(BaseTaskTests, test_utils.TestCase):
|
|||
|
||||
Task = getattr(tasks, '_CTask', None)
|
||||
Future = futures._PyFuture
|
||||
all_tasks = getattr(tasks, '_c_all_tasks', None)
|
||||
|
||||
|
||||
@unittest.skipUnless(hasattr(futures, '_CFuture'),
|
||||
|
|
@ -2869,6 +2876,7 @@ class PyTask_CFuture_Tests(BaseTaskTests, test_utils.TestCase):
|
|||
|
||||
Task = tasks._PyTask
|
||||
Future = getattr(futures, '_CFuture', None)
|
||||
all_tasks = staticmethod(tasks._py_all_tasks)
|
||||
|
||||
|
||||
class PyTask_PyFuture_Tests(BaseTaskTests, SetMethodsTest,
|
||||
|
|
@ -2876,6 +2884,7 @@ class PyTask_PyFuture_Tests(BaseTaskTests, SetMethodsTest,
|
|||
|
||||
Task = tasks._PyTask
|
||||
Future = futures._PyFuture
|
||||
all_tasks = staticmethod(tasks._py_all_tasks)
|
||||
|
||||
|
||||
@add_subclass_tests
|
||||
|
|
@ -2915,6 +2924,7 @@ class BaseTaskIntrospectionTests:
|
|||
_unregister_task = None
|
||||
_enter_task = None
|
||||
_leave_task = None
|
||||
all_tasks = None
|
||||
|
||||
def test__register_task_1(self):
|
||||
class TaskLike:
|
||||
|
|
@ -2928,9 +2938,9 @@ def done(self):
|
|||
task = TaskLike()
|
||||
loop = mock.Mock()
|
||||
|
||||
self.assertEqual(asyncio.all_tasks(loop), set())
|
||||
self.assertEqual(self.all_tasks(loop), set())
|
||||
self._register_task(task)
|
||||
self.assertEqual(asyncio.all_tasks(loop), {task})
|
||||
self.assertEqual(self.all_tasks(loop), {task})
|
||||
self._unregister_task(task)
|
||||
|
||||
def test__register_task_2(self):
|
||||
|
|
@ -2944,9 +2954,9 @@ def done(self):
|
|||
task = TaskLike()
|
||||
loop = mock.Mock()
|
||||
|
||||
self.assertEqual(asyncio.all_tasks(loop), set())
|
||||
self.assertEqual(self.all_tasks(loop), set())
|
||||
self._register_task(task)
|
||||
self.assertEqual(asyncio.all_tasks(loop), {task})
|
||||
self.assertEqual(self.all_tasks(loop), {task})
|
||||
self._unregister_task(task)
|
||||
|
||||
def test__register_task_3(self):
|
||||
|
|
@ -2960,9 +2970,9 @@ def done(self):
|
|||
task = TaskLike()
|
||||
loop = mock.Mock()
|
||||
|
||||
self.assertEqual(asyncio.all_tasks(loop), set())
|
||||
self.assertEqual(self.all_tasks(loop), set())
|
||||
self._register_task(task)
|
||||
self.assertEqual(asyncio.all_tasks(loop), set())
|
||||
self.assertEqual(self.all_tasks(loop), set())
|
||||
self._unregister_task(task)
|
||||
|
||||
def test__enter_task(self):
|
||||
|
|
@ -3013,13 +3023,13 @@ def test__unregister_task(self):
|
|||
task.get_loop = lambda: loop
|
||||
self._register_task(task)
|
||||
self._unregister_task(task)
|
||||
self.assertEqual(asyncio.all_tasks(loop), set())
|
||||
self.assertEqual(self.all_tasks(loop), set())
|
||||
|
||||
def test__unregister_task_not_registered(self):
|
||||
task = mock.Mock()
|
||||
loop = mock.Mock()
|
||||
self._unregister_task(task)
|
||||
self.assertEqual(asyncio.all_tasks(loop), set())
|
||||
self.assertEqual(self.all_tasks(loop), set())
|
||||
|
||||
|
||||
class PyIntrospectionTests(test_utils.TestCase, BaseTaskIntrospectionTests):
|
||||
|
|
@ -3027,6 +3037,7 @@ class PyIntrospectionTests(test_utils.TestCase, BaseTaskIntrospectionTests):
|
|||
_unregister_task = staticmethod(tasks._py_unregister_task)
|
||||
_enter_task = staticmethod(tasks._py_enter_task)
|
||||
_leave_task = staticmethod(tasks._py_leave_task)
|
||||
all_tasks = staticmethod(tasks._py_all_tasks)
|
||||
|
||||
|
||||
@unittest.skipUnless(hasattr(tasks, '_c_register_task'),
|
||||
|
|
@ -3037,6 +3048,7 @@ class CIntrospectionTests(test_utils.TestCase, BaseTaskIntrospectionTests):
|
|||
_unregister_task = staticmethod(tasks._c_unregister_task)
|
||||
_enter_task = staticmethod(tasks._c_enter_task)
|
||||
_leave_task = staticmethod(tasks._c_leave_task)
|
||||
all_tasks = staticmethod(tasks._c_all_tasks)
|
||||
else:
|
||||
_register_task = _unregister_task = _enter_task = _leave_task = None
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue