mirror of
https://github.com/python/cpython.git
synced 2025-08-30 21:48:47 +00:00
bpo-32348: Optimize asyncio.Future schedule/add/remove callback. (#4907)
This commit is contained in:
parent
4c72bc4a38
commit
1b7c11ff0e
4 changed files with 499 additions and 102 deletions
|
@ -145,37 +145,60 @@ class BaseFutureTests:
|
||||||
self.assertRaises(TypeError, self._new_future, 42)
|
self.assertRaises(TypeError, self._new_future, 42)
|
||||||
|
|
||||||
def test_uninitialized(self):
|
def test_uninitialized(self):
|
||||||
|
# Test that C Future doesn't crash when Future.__init__()
|
||||||
|
# call was skipped.
|
||||||
|
|
||||||
fut = self.cls.__new__(self.cls, loop=self.loop)
|
fut = self.cls.__new__(self.cls, loop=self.loop)
|
||||||
self.assertRaises(asyncio.InvalidStateError, fut.result)
|
self.assertRaises(asyncio.InvalidStateError, fut.result)
|
||||||
|
|
||||||
fut = self.cls.__new__(self.cls, loop=self.loop)
|
fut = self.cls.__new__(self.cls, loop=self.loop)
|
||||||
self.assertRaises(asyncio.InvalidStateError, fut.exception)
|
self.assertRaises(asyncio.InvalidStateError, fut.exception)
|
||||||
|
|
||||||
fut = self.cls.__new__(self.cls, loop=self.loop)
|
fut = self.cls.__new__(self.cls, loop=self.loop)
|
||||||
with self.assertRaises((RuntimeError, AttributeError)):
|
with self.assertRaises((RuntimeError, AttributeError)):
|
||||||
fut.set_result(None)
|
fut.set_result(None)
|
||||||
|
|
||||||
fut = self.cls.__new__(self.cls, loop=self.loop)
|
fut = self.cls.__new__(self.cls, loop=self.loop)
|
||||||
with self.assertRaises((RuntimeError, AttributeError)):
|
with self.assertRaises((RuntimeError, AttributeError)):
|
||||||
fut.set_exception(Exception)
|
fut.set_exception(Exception)
|
||||||
|
|
||||||
fut = self.cls.__new__(self.cls, loop=self.loop)
|
fut = self.cls.__new__(self.cls, loop=self.loop)
|
||||||
with self.assertRaises((RuntimeError, AttributeError)):
|
with self.assertRaises((RuntimeError, AttributeError)):
|
||||||
fut.cancel()
|
fut.cancel()
|
||||||
|
|
||||||
fut = self.cls.__new__(self.cls, loop=self.loop)
|
fut = self.cls.__new__(self.cls, loop=self.loop)
|
||||||
with self.assertRaises((RuntimeError, AttributeError)):
|
with self.assertRaises((RuntimeError, AttributeError)):
|
||||||
fut.add_done_callback(lambda f: None)
|
fut.add_done_callback(lambda f: None)
|
||||||
|
|
||||||
fut = self.cls.__new__(self.cls, loop=self.loop)
|
fut = self.cls.__new__(self.cls, loop=self.loop)
|
||||||
with self.assertRaises((RuntimeError, AttributeError)):
|
with self.assertRaises((RuntimeError, AttributeError)):
|
||||||
fut.remove_done_callback(lambda f: None)
|
fut.remove_done_callback(lambda f: None)
|
||||||
|
|
||||||
fut = self.cls.__new__(self.cls, loop=self.loop)
|
fut = self.cls.__new__(self.cls, loop=self.loop)
|
||||||
with self.assertRaises((RuntimeError, AttributeError)):
|
with self.assertRaises((RuntimeError, AttributeError)):
|
||||||
fut._schedule_callbacks()
|
fut._schedule_callbacks()
|
||||||
|
|
||||||
fut = self.cls.__new__(self.cls, loop=self.loop)
|
fut = self.cls.__new__(self.cls, loop=self.loop)
|
||||||
try:
|
try:
|
||||||
repr(fut)
|
repr(fut)
|
||||||
except AttributeError:
|
except (RuntimeError, AttributeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
fut = self.cls.__new__(self.cls, loop=self.loop)
|
fut = self.cls.__new__(self.cls, loop=self.loop)
|
||||||
fut.cancelled()
|
try:
|
||||||
fut.done()
|
fut.__await__()
|
||||||
iter(fut)
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
fut = self.cls.__new__(self.cls, loop=self.loop)
|
||||||
|
try:
|
||||||
|
iter(fut)
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
fut = self.cls.__new__(self.cls, loop=self.loop)
|
||||||
|
self.assertFalse(fut.cancelled())
|
||||||
|
self.assertFalse(fut.done())
|
||||||
|
|
||||||
def test_cancel(self):
|
def test_cancel(self):
|
||||||
f = self._new_future(loop=self.loop)
|
f = self._new_future(loop=self.loop)
|
||||||
|
@ -246,30 +269,32 @@ class BaseFutureTests:
|
||||||
self.loop.set_debug(True)
|
self.loop.set_debug(True)
|
||||||
f_pending_debug = self._new_future(loop=self.loop)
|
f_pending_debug = self._new_future(loop=self.loop)
|
||||||
frame = f_pending_debug._source_traceback[-1]
|
frame = f_pending_debug._source_traceback[-1]
|
||||||
self.assertEqual(repr(f_pending_debug),
|
self.assertEqual(
|
||||||
'<Future pending created at %s:%s>'
|
repr(f_pending_debug),
|
||||||
% (frame[0], frame[1]))
|
f'<{self.cls.__name__} pending created at {frame[0]}:{frame[1]}>')
|
||||||
f_pending_debug.cancel()
|
f_pending_debug.cancel()
|
||||||
|
|
||||||
self.loop.set_debug(False)
|
self.loop.set_debug(False)
|
||||||
f_pending = self._new_future(loop=self.loop)
|
f_pending = self._new_future(loop=self.loop)
|
||||||
self.assertEqual(repr(f_pending), '<Future pending>')
|
self.assertEqual(repr(f_pending), f'<{self.cls.__name__} pending>')
|
||||||
f_pending.cancel()
|
f_pending.cancel()
|
||||||
|
|
||||||
f_cancelled = self._new_future(loop=self.loop)
|
f_cancelled = self._new_future(loop=self.loop)
|
||||||
f_cancelled.cancel()
|
f_cancelled.cancel()
|
||||||
self.assertEqual(repr(f_cancelled), '<Future cancelled>')
|
self.assertEqual(repr(f_cancelled), f'<{self.cls.__name__} cancelled>')
|
||||||
|
|
||||||
f_result = self._new_future(loop=self.loop)
|
f_result = self._new_future(loop=self.loop)
|
||||||
f_result.set_result(4)
|
f_result.set_result(4)
|
||||||
self.assertEqual(repr(f_result), '<Future finished result=4>')
|
self.assertEqual(
|
||||||
|
repr(f_result), f'<{self.cls.__name__} finished result=4>')
|
||||||
self.assertEqual(f_result.result(), 4)
|
self.assertEqual(f_result.result(), 4)
|
||||||
|
|
||||||
exc = RuntimeError()
|
exc = RuntimeError()
|
||||||
f_exception = self._new_future(loop=self.loop)
|
f_exception = self._new_future(loop=self.loop)
|
||||||
f_exception.set_exception(exc)
|
f_exception.set_exception(exc)
|
||||||
self.assertEqual(repr(f_exception),
|
self.assertEqual(
|
||||||
'<Future finished exception=RuntimeError()>')
|
repr(f_exception),
|
||||||
|
f'<{self.cls.__name__} finished exception=RuntimeError()>')
|
||||||
self.assertIs(f_exception.exception(), exc)
|
self.assertIs(f_exception.exception(), exc)
|
||||||
|
|
||||||
def func_repr(func):
|
def func_repr(func):
|
||||||
|
@ -280,11 +305,12 @@ class BaseFutureTests:
|
||||||
f_one_callbacks = self._new_future(loop=self.loop)
|
f_one_callbacks = self._new_future(loop=self.loop)
|
||||||
f_one_callbacks.add_done_callback(_fakefunc)
|
f_one_callbacks.add_done_callback(_fakefunc)
|
||||||
fake_repr = func_repr(_fakefunc)
|
fake_repr = func_repr(_fakefunc)
|
||||||
self.assertRegex(repr(f_one_callbacks),
|
self.assertRegex(
|
||||||
r'<Future pending cb=\[%s\]>' % fake_repr)
|
repr(f_one_callbacks),
|
||||||
|
r'<' + self.cls.__name__ + r' pending cb=\[%s\]>' % fake_repr)
|
||||||
f_one_callbacks.cancel()
|
f_one_callbacks.cancel()
|
||||||
self.assertEqual(repr(f_one_callbacks),
|
self.assertEqual(repr(f_one_callbacks),
|
||||||
'<Future cancelled>')
|
f'<{self.cls.__name__} cancelled>')
|
||||||
|
|
||||||
f_two_callbacks = self._new_future(loop=self.loop)
|
f_two_callbacks = self._new_future(loop=self.loop)
|
||||||
f_two_callbacks.add_done_callback(first_cb)
|
f_two_callbacks.add_done_callback(first_cb)
|
||||||
|
@ -292,7 +318,7 @@ class BaseFutureTests:
|
||||||
first_repr = func_repr(first_cb)
|
first_repr = func_repr(first_cb)
|
||||||
last_repr = func_repr(last_cb)
|
last_repr = func_repr(last_cb)
|
||||||
self.assertRegex(repr(f_two_callbacks),
|
self.assertRegex(repr(f_two_callbacks),
|
||||||
r'<Future pending cb=\[%s, %s\]>'
|
r'<' + self.cls.__name__ + r' pending cb=\[%s, %s\]>'
|
||||||
% (first_repr, last_repr))
|
% (first_repr, last_repr))
|
||||||
|
|
||||||
f_many_callbacks = self._new_future(loop=self.loop)
|
f_many_callbacks = self._new_future(loop=self.loop)
|
||||||
|
@ -301,11 +327,12 @@ class BaseFutureTests:
|
||||||
f_many_callbacks.add_done_callback(_fakefunc)
|
f_many_callbacks.add_done_callback(_fakefunc)
|
||||||
f_many_callbacks.add_done_callback(last_cb)
|
f_many_callbacks.add_done_callback(last_cb)
|
||||||
cb_regex = r'%s, <8 more>, %s' % (first_repr, last_repr)
|
cb_regex = r'%s, <8 more>, %s' % (first_repr, last_repr)
|
||||||
self.assertRegex(repr(f_many_callbacks),
|
self.assertRegex(
|
||||||
r'<Future pending cb=\[%s\]>' % cb_regex)
|
repr(f_many_callbacks),
|
||||||
|
r'<' + self.cls.__name__ + r' pending cb=\[%s\]>' % cb_regex)
|
||||||
f_many_callbacks.cancel()
|
f_many_callbacks.cancel()
|
||||||
self.assertEqual(repr(f_many_callbacks),
|
self.assertEqual(repr(f_many_callbacks),
|
||||||
'<Future cancelled>')
|
f'<{self.cls.__name__} cancelled>')
|
||||||
|
|
||||||
def test_copy_state(self):
|
def test_copy_state(self):
|
||||||
from asyncio.futures import _copy_future_state
|
from asyncio.futures import _copy_future_state
|
||||||
|
@ -475,7 +502,7 @@ class BaseFutureTests:
|
||||||
support.gc_collect()
|
support.gc_collect()
|
||||||
|
|
||||||
if sys.version_info >= (3, 4):
|
if sys.version_info >= (3, 4):
|
||||||
regex = r'^Future exception was never retrieved\n'
|
regex = f'^{self.cls.__name__} exception was never retrieved\n'
|
||||||
exc_info = (type(exc), exc, exc.__traceback__)
|
exc_info = (type(exc), exc, exc.__traceback__)
|
||||||
m_log.error.assert_called_once_with(mock.ANY, exc_info=exc_info)
|
m_log.error.assert_called_once_with(mock.ANY, exc_info=exc_info)
|
||||||
else:
|
else:
|
||||||
|
@ -531,7 +558,16 @@ class BaseFutureTests:
|
||||||
@unittest.skipUnless(hasattr(futures, '_CFuture'),
|
@unittest.skipUnless(hasattr(futures, '_CFuture'),
|
||||||
'requires the C _asyncio module')
|
'requires the C _asyncio module')
|
||||||
class CFutureTests(BaseFutureTests, test_utils.TestCase):
|
class CFutureTests(BaseFutureTests, test_utils.TestCase):
|
||||||
cls = getattr(futures, '_CFuture')
|
cls = futures._CFuture
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(futures, '_CFuture'),
|
||||||
|
'requires the C _asyncio module')
|
||||||
|
class CSubFutureTests(BaseFutureTests, test_utils.TestCase):
|
||||||
|
class CSubFuture(futures._CFuture):
|
||||||
|
pass
|
||||||
|
|
||||||
|
cls = CSubFuture
|
||||||
|
|
||||||
|
|
||||||
class PyFutureTests(BaseFutureTests, test_utils.TestCase):
|
class PyFutureTests(BaseFutureTests, test_utils.TestCase):
|
||||||
|
@ -556,6 +592,76 @@ class BaseFutureDoneCallbackTests():
|
||||||
def _new_future(self):
|
def _new_future(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def test_callbacks_remove_first_callback(self):
|
||||||
|
bag = []
|
||||||
|
f = self._new_future()
|
||||||
|
|
||||||
|
cb1 = self._make_callback(bag, 42)
|
||||||
|
cb2 = self._make_callback(bag, 17)
|
||||||
|
cb3 = self._make_callback(bag, 100)
|
||||||
|
|
||||||
|
f.add_done_callback(cb1)
|
||||||
|
f.add_done_callback(cb2)
|
||||||
|
f.add_done_callback(cb3)
|
||||||
|
|
||||||
|
f.remove_done_callback(cb1)
|
||||||
|
f.remove_done_callback(cb1)
|
||||||
|
|
||||||
|
self.assertEqual(bag, [])
|
||||||
|
f.set_result('foo')
|
||||||
|
|
||||||
|
self.run_briefly()
|
||||||
|
|
||||||
|
self.assertEqual(bag, [17, 100])
|
||||||
|
self.assertEqual(f.result(), 'foo')
|
||||||
|
|
||||||
|
def test_callbacks_remove_first_and_second_callback(self):
|
||||||
|
bag = []
|
||||||
|
f = self._new_future()
|
||||||
|
|
||||||
|
cb1 = self._make_callback(bag, 42)
|
||||||
|
cb2 = self._make_callback(bag, 17)
|
||||||
|
cb3 = self._make_callback(bag, 100)
|
||||||
|
|
||||||
|
f.add_done_callback(cb1)
|
||||||
|
f.add_done_callback(cb2)
|
||||||
|
f.add_done_callback(cb3)
|
||||||
|
|
||||||
|
f.remove_done_callback(cb1)
|
||||||
|
f.remove_done_callback(cb2)
|
||||||
|
f.remove_done_callback(cb1)
|
||||||
|
|
||||||
|
self.assertEqual(bag, [])
|
||||||
|
f.set_result('foo')
|
||||||
|
|
||||||
|
self.run_briefly()
|
||||||
|
|
||||||
|
self.assertEqual(bag, [100])
|
||||||
|
self.assertEqual(f.result(), 'foo')
|
||||||
|
|
||||||
|
def test_callbacks_remove_third_callback(self):
|
||||||
|
bag = []
|
||||||
|
f = self._new_future()
|
||||||
|
|
||||||
|
cb1 = self._make_callback(bag, 42)
|
||||||
|
cb2 = self._make_callback(bag, 17)
|
||||||
|
cb3 = self._make_callback(bag, 100)
|
||||||
|
|
||||||
|
f.add_done_callback(cb1)
|
||||||
|
f.add_done_callback(cb2)
|
||||||
|
f.add_done_callback(cb3)
|
||||||
|
|
||||||
|
f.remove_done_callback(cb3)
|
||||||
|
f.remove_done_callback(cb3)
|
||||||
|
|
||||||
|
self.assertEqual(bag, [])
|
||||||
|
f.set_result('foo')
|
||||||
|
|
||||||
|
self.run_briefly()
|
||||||
|
|
||||||
|
self.assertEqual(bag, [42, 17])
|
||||||
|
self.assertEqual(f.result(), 'foo')
|
||||||
|
|
||||||
def test_callbacks_invoked_on_set_result(self):
|
def test_callbacks_invoked_on_set_result(self):
|
||||||
bag = []
|
bag = []
|
||||||
f = self._new_future()
|
f = self._new_future()
|
||||||
|
@ -678,6 +784,17 @@ class CFutureDoneCallbackTests(BaseFutureDoneCallbackTests,
|
||||||
return futures._CFuture(loop=self.loop)
|
return futures._CFuture(loop=self.loop)
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(futures, '_CFuture'),
|
||||||
|
'requires the C _asyncio module')
|
||||||
|
class CSubFutureDoneCallbackTests(BaseFutureDoneCallbackTests,
|
||||||
|
test_utils.TestCase):
|
||||||
|
|
||||||
|
def _new_future(self):
|
||||||
|
class CSubFuture(futures._CFuture):
|
||||||
|
pass
|
||||||
|
return CSubFuture(loop=self.loop)
|
||||||
|
|
||||||
|
|
||||||
class PyFutureDoneCallbackTests(BaseFutureDoneCallbackTests,
|
class PyFutureDoneCallbackTests(BaseFutureDoneCallbackTests,
|
||||||
test_utils.TestCase):
|
test_utils.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -2187,23 +2187,51 @@ def add_subclass_tests(cls):
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(futures, '_CFuture'),
|
@unittest.skipUnless(hasattr(futures, '_CFuture') and
|
||||||
|
hasattr(tasks, '_CTask'),
|
||||||
'requires the C _asyncio module')
|
'requires the C _asyncio module')
|
||||||
class CTask_CFuture_Tests(BaseTaskTests, test_utils.TestCase):
|
class CTask_CFuture_Tests(BaseTaskTests, test_utils.TestCase):
|
||||||
Task = getattr(tasks, '_CTask', None)
|
Task = getattr(tasks, '_CTask', None)
|
||||||
Future = getattr(futures, '_CFuture', None)
|
Future = getattr(futures, '_CFuture', None)
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(futures, '_CFuture'),
|
@unittest.skipUnless(hasattr(futures, '_CFuture') and
|
||||||
|
hasattr(tasks, '_CTask'),
|
||||||
'requires the C _asyncio module')
|
'requires the C _asyncio module')
|
||||||
@add_subclass_tests
|
@add_subclass_tests
|
||||||
class CTask_CFuture_SubclassTests(BaseTaskTests, test_utils.TestCase):
|
class CTask_CFuture_SubclassTests(BaseTaskTests, test_utils.TestCase):
|
||||||
Task = getattr(tasks, '_CTask', None)
|
|
||||||
Future = getattr(futures, '_CFuture', None)
|
class Task(tasks._CTask):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Future(futures._CFuture):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(tasks, '_CTask'),
|
||||||
|
'requires the C _asyncio module')
|
||||||
|
@add_subclass_tests
|
||||||
|
class CTaskSubclass_PyFuture_Tests(BaseTaskTests, test_utils.TestCase):
|
||||||
|
|
||||||
|
class Task(tasks._CTask):
|
||||||
|
pass
|
||||||
|
|
||||||
|
Future = futures._PyFuture
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(futures, '_CFuture'),
|
@unittest.skipUnless(hasattr(futures, '_CFuture'),
|
||||||
'requires the C _asyncio module')
|
'requires the C _asyncio module')
|
||||||
|
@add_subclass_tests
|
||||||
|
class PyTask_CFutureSubclass_Tests(BaseTaskTests, test_utils.TestCase):
|
||||||
|
|
||||||
|
class Future(futures._CFuture):
|
||||||
|
pass
|
||||||
|
|
||||||
|
Task = tasks._PyTask
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(tasks, '_CTask'),
|
||||||
|
'requires the C _asyncio module')
|
||||||
class CTask_PyFuture_Tests(BaseTaskTests, test_utils.TestCase):
|
class CTask_PyFuture_Tests(BaseTaskTests, test_utils.TestCase):
|
||||||
Task = getattr(tasks, '_CTask', None)
|
Task = getattr(tasks, '_CTask', None)
|
||||||
Future = futures._PyFuture
|
Future = futures._PyFuture
|
||||||
|
@ -2223,8 +2251,11 @@ class PyTask_PyFuture_Tests(BaseTaskTests, test_utils.TestCase):
|
||||||
|
|
||||||
@add_subclass_tests
|
@add_subclass_tests
|
||||||
class PyTask_PyFuture_SubclassTests(BaseTaskTests, test_utils.TestCase):
|
class PyTask_PyFuture_SubclassTests(BaseTaskTests, test_utils.TestCase):
|
||||||
Task = tasks._PyTask
|
class Task(tasks._PyTask):
|
||||||
Future = futures._PyFuture
|
pass
|
||||||
|
|
||||||
|
class Future(futures._PyFuture):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BaseTaskIntrospectionTests:
|
class BaseTaskIntrospectionTests:
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Optimize asyncio.Future schedule/add/remove callback. The optimization
|
||||||
|
shows 3-6% performance improvements of async/await code.
|
|
@ -59,6 +59,7 @@ typedef enum {
|
||||||
#define FutureObj_HEAD(prefix) \
|
#define FutureObj_HEAD(prefix) \
|
||||||
PyObject_HEAD \
|
PyObject_HEAD \
|
||||||
PyObject *prefix##_loop; \
|
PyObject *prefix##_loop; \
|
||||||
|
PyObject *prefix##_callback0; \
|
||||||
PyObject *prefix##_callbacks; \
|
PyObject *prefix##_callbacks; \
|
||||||
PyObject *prefix##_exception; \
|
PyObject *prefix##_exception; \
|
||||||
PyObject *prefix##_result; \
|
PyObject *prefix##_result; \
|
||||||
|
@ -93,6 +94,16 @@ typedef struct {
|
||||||
} TaskWakeupMethWrapper;
|
} TaskWakeupMethWrapper;
|
||||||
|
|
||||||
|
|
||||||
|
static PyTypeObject FutureType;
|
||||||
|
static PyTypeObject TaskType;
|
||||||
|
|
||||||
|
|
||||||
|
#define Future_CheckExact(obj) (Py_TYPE(obj) == &FutureType)
|
||||||
|
#define Task_CheckExact(obj) (Py_TYPE(obj) == &TaskType)
|
||||||
|
|
||||||
|
#define Future_Check(obj) PyObject_TypeCheck(obj, &FutureType)
|
||||||
|
#define Task_Check(obj) PyObject_TypeCheck(obj, &TaskType)
|
||||||
|
|
||||||
#include "clinic/_asynciomodule.c.h"
|
#include "clinic/_asynciomodule.c.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,6 +112,7 @@ class _asyncio.Future "FutureObj *" "&Future_Type"
|
||||||
[clinic start generated code]*/
|
[clinic start generated code]*/
|
||||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=00d3e4abca711e0f]*/
|
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=00d3e4abca711e0f]*/
|
||||||
|
|
||||||
|
|
||||||
/* Get FutureIter from Future */
|
/* Get FutureIter from Future */
|
||||||
static PyObject* future_new_iter(PyObject *);
|
static PyObject* future_new_iter(PyObject *);
|
||||||
static inline int future_call_schedule_callbacks(FutureObj *);
|
static inline int future_call_schedule_callbacks(FutureObj *);
|
||||||
|
@ -233,47 +245,95 @@ get_event_loop(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
call_soon(PyObject *loop, PyObject *func, PyObject *arg)
|
||||||
|
{
|
||||||
|
PyObject *handle;
|
||||||
|
handle = _PyObject_CallMethodIdObjArgs(
|
||||||
|
loop, &PyId_call_soon, func, arg, NULL);
|
||||||
|
if (handle == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Py_DECREF(handle);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
future_is_alive(FutureObj *fut)
|
||||||
|
{
|
||||||
|
return fut->fut_loop != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
future_ensure_alive(FutureObj *fut)
|
||||||
|
{
|
||||||
|
if (!future_is_alive(fut)) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError,
|
||||||
|
"Future object is not initialized.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define ENSURE_FUTURE_ALIVE(fut) \
|
||||||
|
do { \
|
||||||
|
assert(Future_Check(fut) || Task_Check(fut)); \
|
||||||
|
if (future_ensure_alive((FutureObj*)fut)) { \
|
||||||
|
return NULL; \
|
||||||
|
} \
|
||||||
|
} while(0);
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
future_schedule_callbacks(FutureObj *fut)
|
future_schedule_callbacks(FutureObj *fut)
|
||||||
{
|
{
|
||||||
Py_ssize_t len;
|
Py_ssize_t len;
|
||||||
PyObject *callbacks;
|
Py_ssize_t i;
|
||||||
int i;
|
|
||||||
|
if (fut->fut_callback0 != NULL) {
|
||||||
|
/* There's a 1st callback */
|
||||||
|
|
||||||
|
int ret = call_soon(
|
||||||
|
fut->fut_loop, fut->fut_callback0, (PyObject *)fut);
|
||||||
|
Py_CLEAR(fut->fut_callback0);
|
||||||
|
if (ret) {
|
||||||
|
/* If an error occurs in pure-Python implementation,
|
||||||
|
all callbacks are cleared. */
|
||||||
|
Py_CLEAR(fut->fut_callbacks);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we called the first callback, now try calling
|
||||||
|
callbacks from the 'fut_callbacks' list. */
|
||||||
|
}
|
||||||
|
|
||||||
if (fut->fut_callbacks == NULL) {
|
if (fut->fut_callbacks == NULL) {
|
||||||
PyErr_SetString(PyExc_RuntimeError, "uninitialized Future object");
|
/* No more callbacks, return. */
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = PyList_GET_SIZE(fut->fut_callbacks);
|
len = PyList_GET_SIZE(fut->fut_callbacks);
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
|
/* The list of callbacks was empty; clear it and return. */
|
||||||
|
Py_CLEAR(fut->fut_callbacks);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks = PyList_GetSlice(fut->fut_callbacks, 0, len);
|
|
||||||
if (callbacks == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (PyList_SetSlice(fut->fut_callbacks, 0, len, NULL) < 0) {
|
|
||||||
Py_DECREF(callbacks);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
PyObject *handle;
|
PyObject *cb = PyList_GET_ITEM(fut->fut_callbacks, i);
|
||||||
PyObject *cb = PyList_GET_ITEM(callbacks, i);
|
|
||||||
|
|
||||||
handle = _PyObject_CallMethodIdObjArgs(fut->fut_loop, &PyId_call_soon,
|
if (call_soon(fut->fut_loop, cb, (PyObject *)fut)) {
|
||||||
cb, fut, NULL);
|
/* If an error occurs in pure-Python implementation,
|
||||||
|
all callbacks are cleared. */
|
||||||
if (handle == NULL) {
|
Py_CLEAR(fut->fut_callbacks);
|
||||||
Py_DECREF(callbacks);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
Py_DECREF(handle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_DECREF(callbacks);
|
Py_CLEAR(fut->fut_callbacks);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,10 +371,8 @@ future_init(FutureObj *fut, PyObject *loop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_XSETREF(fut->fut_callbacks, PyList_New(0));
|
fut->fut_callback0 = NULL;
|
||||||
if (fut->fut_callbacks == NULL) {
|
fut->fut_callbacks = NULL;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -322,6 +380,10 @@ future_init(FutureObj *fut, PyObject *loop)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
future_set_result(FutureObj *fut, PyObject *res)
|
future_set_result(FutureObj *fut, PyObject *res)
|
||||||
{
|
{
|
||||||
|
if (future_ensure_alive(fut)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (fut->fut_state != STATE_PENDING) {
|
if (fut->fut_state != STATE_PENDING) {
|
||||||
PyErr_SetString(asyncio_InvalidStateError, "invalid state");
|
PyErr_SetString(asyncio_InvalidStateError, "invalid state");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -416,25 +478,61 @@ future_get_result(FutureObj *fut, PyObject **result)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
future_add_done_callback(FutureObj *fut, PyObject *arg)
|
future_add_done_callback(FutureObj *fut, PyObject *arg)
|
||||||
{
|
{
|
||||||
|
if (!future_is_alive(fut)) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "uninitialized Future object");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (fut->fut_state != STATE_PENDING) {
|
if (fut->fut_state != STATE_PENDING) {
|
||||||
PyObject *handle = _PyObject_CallMethodIdObjArgs(fut->fut_loop,
|
/* The future is done/cancelled, so schedule the callback
|
||||||
&PyId_call_soon,
|
right away. */
|
||||||
arg, fut, NULL);
|
if (call_soon(fut->fut_loop, arg, (PyObject*) fut)) {
|
||||||
if (handle == NULL) {
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
Py_DECREF(handle);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (fut->fut_callbacks == NULL) {
|
/* The future is pending, add a callback.
|
||||||
PyErr_SetString(PyExc_RuntimeError, "uninitialized Future object");
|
|
||||||
return NULL;
|
Callbacks in the future object are stored as follows:
|
||||||
|
|
||||||
|
callback0 -- a pointer to the first callback
|
||||||
|
callbacks -- a list of 2nd, 3rd, ... callbacks
|
||||||
|
|
||||||
|
Invariants:
|
||||||
|
|
||||||
|
* callbacks != NULL:
|
||||||
|
There are some callbacks in in the list. Just
|
||||||
|
add the new callback to it.
|
||||||
|
|
||||||
|
* callbacks == NULL and callback0 == NULL:
|
||||||
|
This is the first callback. Set it to callback0.
|
||||||
|
|
||||||
|
* callbacks == NULL and callback0 != NULL:
|
||||||
|
This is a second callback. Initialize callbacks
|
||||||
|
with a new list and add the new callback to it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (fut->fut_callbacks != NULL) {
|
||||||
|
int err = PyList_Append(fut->fut_callbacks, arg);
|
||||||
|
if (err != 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
int err = PyList_Append(fut->fut_callbacks, arg);
|
else if (fut->fut_callback0 == NULL) {
|
||||||
if (err != 0) {
|
Py_INCREF(arg);
|
||||||
return NULL;
|
fut->fut_callback0 = arg;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fut->fut_callbacks = PyList_New(1);
|
||||||
|
if (fut->fut_callbacks == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_INCREF(arg);
|
||||||
|
PyList_SET_ITEM(fut->fut_callbacks, 0, arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,6 +585,7 @@ static int
|
||||||
FutureObj_clear(FutureObj *fut)
|
FutureObj_clear(FutureObj *fut)
|
||||||
{
|
{
|
||||||
Py_CLEAR(fut->fut_loop);
|
Py_CLEAR(fut->fut_loop);
|
||||||
|
Py_CLEAR(fut->fut_callback0);
|
||||||
Py_CLEAR(fut->fut_callbacks);
|
Py_CLEAR(fut->fut_callbacks);
|
||||||
Py_CLEAR(fut->fut_result);
|
Py_CLEAR(fut->fut_result);
|
||||||
Py_CLEAR(fut->fut_exception);
|
Py_CLEAR(fut->fut_exception);
|
||||||
|
@ -499,6 +598,7 @@ static int
|
||||||
FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg)
|
FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg)
|
||||||
{
|
{
|
||||||
Py_VISIT(fut->fut_loop);
|
Py_VISIT(fut->fut_loop);
|
||||||
|
Py_VISIT(fut->fut_callback0);
|
||||||
Py_VISIT(fut->fut_callbacks);
|
Py_VISIT(fut->fut_callbacks);
|
||||||
Py_VISIT(fut->fut_result);
|
Py_VISIT(fut->fut_result);
|
||||||
Py_VISIT(fut->fut_exception);
|
Py_VISIT(fut->fut_exception);
|
||||||
|
@ -522,6 +622,13 @@ _asyncio_Future_result_impl(FutureObj *self)
|
||||||
/*[clinic end generated code: output=f35f940936a4b1e5 input=49ecf9cf5ec50dc5]*/
|
/*[clinic end generated code: output=f35f940936a4b1e5 input=49ecf9cf5ec50dc5]*/
|
||||||
{
|
{
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
|
|
||||||
|
if (!future_is_alive(self)) {
|
||||||
|
PyErr_SetString(asyncio_InvalidStateError,
|
||||||
|
"Future object is not initialized.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int res = future_get_result(self, &result);
|
int res = future_get_result(self, &result);
|
||||||
|
|
||||||
if (res == -1) {
|
if (res == -1) {
|
||||||
|
@ -554,6 +661,12 @@ static PyObject *
|
||||||
_asyncio_Future_exception_impl(FutureObj *self)
|
_asyncio_Future_exception_impl(FutureObj *self)
|
||||||
/*[clinic end generated code: output=88b20d4f855e0710 input=733547a70c841c68]*/
|
/*[clinic end generated code: output=88b20d4f855e0710 input=733547a70c841c68]*/
|
||||||
{
|
{
|
||||||
|
if (!future_is_alive(self)) {
|
||||||
|
PyErr_SetString(asyncio_InvalidStateError,
|
||||||
|
"Future object is not initialized.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (self->fut_state == STATE_CANCELLED) {
|
if (self->fut_state == STATE_CANCELLED) {
|
||||||
PyErr_SetNone(asyncio_CancelledError);
|
PyErr_SetNone(asyncio_CancelledError);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -589,6 +702,7 @@ static PyObject *
|
||||||
_asyncio_Future_set_result(FutureObj *self, PyObject *res)
|
_asyncio_Future_set_result(FutureObj *self, PyObject *res)
|
||||||
/*[clinic end generated code: output=a620abfc2796bfb6 input=5b9dc180f1baa56d]*/
|
/*[clinic end generated code: output=a620abfc2796bfb6 input=5b9dc180f1baa56d]*/
|
||||||
{
|
{
|
||||||
|
ENSURE_FUTURE_ALIVE(self)
|
||||||
return future_set_result(self, res);
|
return future_set_result(self, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,6 +722,7 @@ static PyObject *
|
||||||
_asyncio_Future_set_exception(FutureObj *self, PyObject *exception)
|
_asyncio_Future_set_exception(FutureObj *self, PyObject *exception)
|
||||||
/*[clinic end generated code: output=f1c1b0cd321be360 input=e45b7d7aa71cc66d]*/
|
/*[clinic end generated code: output=f1c1b0cd321be360 input=e45b7d7aa71cc66d]*/
|
||||||
{
|
{
|
||||||
|
ENSURE_FUTURE_ALIVE(self)
|
||||||
return future_set_exception(self, exception);
|
return future_set_exception(self, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,15 +763,45 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
|
||||||
{
|
{
|
||||||
PyObject *newlist;
|
PyObject *newlist;
|
||||||
Py_ssize_t len, i, j=0;
|
Py_ssize_t len, i, j=0;
|
||||||
|
Py_ssize_t cleared_callback0 = 0;
|
||||||
|
|
||||||
|
ENSURE_FUTURE_ALIVE(self)
|
||||||
|
|
||||||
|
if (self->fut_callback0 != NULL) {
|
||||||
|
int cmp = PyObject_RichCompareBool(fn, self->fut_callback0, Py_EQ);
|
||||||
|
if (cmp == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (cmp == 1) {
|
||||||
|
/* callback0 == fn */
|
||||||
|
Py_CLEAR(self->fut_callback0);
|
||||||
|
cleared_callback0 = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (self->fut_callbacks == NULL) {
|
if (self->fut_callbacks == NULL) {
|
||||||
PyErr_SetString(PyExc_RuntimeError, "uninitialized Future object");
|
return PyLong_FromSsize_t(cleared_callback0);
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
len = PyList_GET_SIZE(self->fut_callbacks);
|
len = PyList_GET_SIZE(self->fut_callbacks);
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
return PyLong_FromSsize_t(0);
|
Py_CLEAR(self->fut_callbacks);
|
||||||
|
return PyLong_FromSsize_t(cleared_callback0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 1) {
|
||||||
|
int cmp = PyObject_RichCompareBool(
|
||||||
|
fn, PyList_GET_ITEM(self->fut_callbacks, 0), Py_EQ);
|
||||||
|
if (cmp == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (cmp == 1) {
|
||||||
|
/* callbacks[0] == fn */
|
||||||
|
Py_CLEAR(self->fut_callbacks);
|
||||||
|
return PyLong_FromSsize_t(1 + cleared_callback0);
|
||||||
|
}
|
||||||
|
/* callbacks[0] != fn and len(callbacks) == 1 */
|
||||||
|
return PyLong_FromSsize_t(cleared_callback0);
|
||||||
}
|
}
|
||||||
|
|
||||||
newlist = PyList_New(len);
|
newlist = PyList_New(len);
|
||||||
|
@ -683,6 +828,12 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (j == 0) {
|
||||||
|
Py_CLEAR(self->fut_callbacks);
|
||||||
|
Py_DECREF(newlist);
|
||||||
|
return PyLong_FromSsize_t(len + cleared_callback0);
|
||||||
|
}
|
||||||
|
|
||||||
if (j < len) {
|
if (j < len) {
|
||||||
Py_SIZE(newlist) = j;
|
Py_SIZE(newlist) = j;
|
||||||
}
|
}
|
||||||
|
@ -694,7 +845,7 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Py_DECREF(newlist);
|
Py_DECREF(newlist);
|
||||||
return PyLong_FromSsize_t(len - j);
|
return PyLong_FromSsize_t(len - j + cleared_callback0);
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
Py_DECREF(newlist);
|
Py_DECREF(newlist);
|
||||||
|
@ -715,6 +866,7 @@ static PyObject *
|
||||||
_asyncio_Future_cancel_impl(FutureObj *self)
|
_asyncio_Future_cancel_impl(FutureObj *self)
|
||||||
/*[clinic end generated code: output=e45b932ba8bd68a1 input=515709a127995109]*/
|
/*[clinic end generated code: output=e45b932ba8bd68a1 input=515709a127995109]*/
|
||||||
{
|
{
|
||||||
|
ENSURE_FUTURE_ALIVE(self)
|
||||||
return future_cancel(self);
|
return future_cancel(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -728,7 +880,7 @@ static PyObject *
|
||||||
_asyncio_Future_cancelled_impl(FutureObj *self)
|
_asyncio_Future_cancelled_impl(FutureObj *self)
|
||||||
/*[clinic end generated code: output=145197ced586357d input=943ab8b7b7b17e45]*/
|
/*[clinic end generated code: output=145197ced586357d input=943ab8b7b7b17e45]*/
|
||||||
{
|
{
|
||||||
if (self->fut_state == STATE_CANCELLED) {
|
if (future_is_alive(self) && self->fut_state == STATE_CANCELLED) {
|
||||||
Py_RETURN_TRUE;
|
Py_RETURN_TRUE;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -749,7 +901,7 @@ static PyObject *
|
||||||
_asyncio_Future_done_impl(FutureObj *self)
|
_asyncio_Future_done_impl(FutureObj *self)
|
||||||
/*[clinic end generated code: output=244c5ac351145096 input=28d7b23fdb65d2ac]*/
|
/*[clinic end generated code: output=244c5ac351145096 input=28d7b23fdb65d2ac]*/
|
||||||
{
|
{
|
||||||
if (self->fut_state == STATE_PENDING) {
|
if (!future_is_alive(self) || self->fut_state == STATE_PENDING) {
|
||||||
Py_RETURN_FALSE;
|
Py_RETURN_FALSE;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -760,7 +912,7 @@ _asyncio_Future_done_impl(FutureObj *self)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
FutureObj_get_blocking(FutureObj *fut)
|
FutureObj_get_blocking(FutureObj *fut)
|
||||||
{
|
{
|
||||||
if (fut->fut_blocking) {
|
if (future_is_alive(fut) && fut->fut_blocking) {
|
||||||
Py_RETURN_TRUE;
|
Py_RETURN_TRUE;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -771,6 +923,10 @@ FutureObj_get_blocking(FutureObj *fut)
|
||||||
static int
|
static int
|
||||||
FutureObj_set_blocking(FutureObj *fut, PyObject *val)
|
FutureObj_set_blocking(FutureObj *fut, PyObject *val)
|
||||||
{
|
{
|
||||||
|
if (future_ensure_alive(fut)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int is_true = PyObject_IsTrue(val);
|
int is_true = PyObject_IsTrue(val);
|
||||||
if (is_true < 0) {
|
if (is_true < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -782,6 +938,7 @@ FutureObj_set_blocking(FutureObj *fut, PyObject *val)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
FutureObj_get_log_traceback(FutureObj *fut)
|
FutureObj_get_log_traceback(FutureObj *fut)
|
||||||
{
|
{
|
||||||
|
ENSURE_FUTURE_ALIVE(fut)
|
||||||
if (fut->fut_log_tb) {
|
if (fut->fut_log_tb) {
|
||||||
Py_RETURN_TRUE;
|
Py_RETURN_TRUE;
|
||||||
}
|
}
|
||||||
|
@ -804,7 +961,7 @@ FutureObj_set_log_traceback(FutureObj *fut, PyObject *val)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
FutureObj_get_loop(FutureObj *fut)
|
FutureObj_get_loop(FutureObj *fut)
|
||||||
{
|
{
|
||||||
if (fut->fut_loop == NULL) {
|
if (!future_is_alive(fut)) {
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
Py_INCREF(fut->fut_loop);
|
Py_INCREF(fut->fut_loop);
|
||||||
|
@ -814,16 +971,57 @@ FutureObj_get_loop(FutureObj *fut)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
FutureObj_get_callbacks(FutureObj *fut)
|
FutureObj_get_callbacks(FutureObj *fut)
|
||||||
{
|
{
|
||||||
|
Py_ssize_t i;
|
||||||
|
Py_ssize_t len;
|
||||||
|
PyObject *new_list;
|
||||||
|
|
||||||
|
ENSURE_FUTURE_ALIVE(fut)
|
||||||
|
|
||||||
if (fut->fut_callbacks == NULL) {
|
if (fut->fut_callbacks == NULL) {
|
||||||
Py_RETURN_NONE;
|
if (fut->fut_callback0 == NULL) {
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
new_list = PyList_New(1);
|
||||||
|
if (new_list == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_INCREF(fut->fut_callback0);
|
||||||
|
PyList_SET_ITEM(new_list, 0, fut->fut_callback0);
|
||||||
|
return new_list;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Py_INCREF(fut->fut_callbacks);
|
|
||||||
return fut->fut_callbacks;
|
assert(fut->fut_callbacks != NULL);
|
||||||
|
|
||||||
|
if (fut->fut_callback0 == NULL) {
|
||||||
|
Py_INCREF(fut->fut_callbacks);
|
||||||
|
return fut->fut_callbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(fut->fut_callback0 != NULL);
|
||||||
|
|
||||||
|
len = PyList_GET_SIZE(fut->fut_callbacks);
|
||||||
|
new_list = PyList_New(len + 1);
|
||||||
|
if (new_list == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_INCREF(fut->fut_callback0);
|
||||||
|
PyList_SET_ITEM(new_list, 0, fut->fut_callback0);
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
PyObject *cb = PyList_GET_ITEM(fut->fut_callbacks, i);
|
||||||
|
Py_INCREF(cb);
|
||||||
|
PyList_SET_ITEM(new_list, i + 1, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
FutureObj_get_result(FutureObj *fut)
|
FutureObj_get_result(FutureObj *fut)
|
||||||
{
|
{
|
||||||
|
ENSURE_FUTURE_ALIVE(fut)
|
||||||
if (fut->fut_result == NULL) {
|
if (fut->fut_result == NULL) {
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
@ -834,6 +1032,7 @@ FutureObj_get_result(FutureObj *fut)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
FutureObj_get_exception(FutureObj *fut)
|
FutureObj_get_exception(FutureObj *fut)
|
||||||
{
|
{
|
||||||
|
ENSURE_FUTURE_ALIVE(fut)
|
||||||
if (fut->fut_exception == NULL) {
|
if (fut->fut_exception == NULL) {
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
@ -844,7 +1043,7 @@ FutureObj_get_exception(FutureObj *fut)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
FutureObj_get_source_traceback(FutureObj *fut)
|
FutureObj_get_source_traceback(FutureObj *fut)
|
||||||
{
|
{
|
||||||
if (fut->fut_source_tb == NULL) {
|
if (!future_is_alive(fut) || fut->fut_source_tb == NULL) {
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
Py_INCREF(fut->fut_source_tb);
|
Py_INCREF(fut->fut_source_tb);
|
||||||
|
@ -859,6 +1058,8 @@ FutureObj_get_state(FutureObj *fut)
|
||||||
_Py_IDENTIFIER(FINISHED);
|
_Py_IDENTIFIER(FINISHED);
|
||||||
PyObject *ret = NULL;
|
PyObject *ret = NULL;
|
||||||
|
|
||||||
|
ENSURE_FUTURE_ALIVE(fut)
|
||||||
|
|
||||||
switch (fut->fut_state) {
|
switch (fut->fut_state) {
|
||||||
case STATE_PENDING:
|
case STATE_PENDING:
|
||||||
ret = _PyUnicode_FromId(&PyId_PENDING);
|
ret = _PyUnicode_FromId(&PyId_PENDING);
|
||||||
|
@ -896,6 +1097,8 @@ static PyObject *
|
||||||
_asyncio_Future__schedule_callbacks_impl(FutureObj *self)
|
_asyncio_Future__schedule_callbacks_impl(FutureObj *self)
|
||||||
/*[clinic end generated code: output=5e8958d89ea1c5dc input=4f5f295f263f4a88]*/
|
/*[clinic end generated code: output=5e8958d89ea1c5dc input=4f5f295f263f4a88]*/
|
||||||
{
|
{
|
||||||
|
ENSURE_FUTURE_ALIVE(self)
|
||||||
|
|
||||||
int ret = future_schedule_callbacks(self);
|
int ret = future_schedule_callbacks(self);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -908,6 +1111,8 @@ FutureObj_repr(FutureObj *fut)
|
||||||
{
|
{
|
||||||
_Py_IDENTIFIER(_repr_info);
|
_Py_IDENTIFIER(_repr_info);
|
||||||
|
|
||||||
|
ENSURE_FUTURE_ALIVE(fut)
|
||||||
|
|
||||||
PyObject *rinfo = _PyObject_CallMethodIdObjArgs((PyObject*)fut,
|
PyObject *rinfo = _PyObject_CallMethodIdObjArgs((PyObject*)fut,
|
||||||
&PyId__repr_info,
|
&PyId__repr_info,
|
||||||
NULL);
|
NULL);
|
||||||
|
@ -1068,12 +1273,10 @@ static PyTypeObject FutureType = {
|
||||||
.tp_finalize = (destructor)FutureObj_finalize,
|
.tp_finalize = (destructor)FutureObj_finalize,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define Future_CheckExact(obj) (Py_TYPE(obj) == &FutureType)
|
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
future_call_schedule_callbacks(FutureObj *fut)
|
future_call_schedule_callbacks(FutureObj *fut)
|
||||||
{
|
{
|
||||||
if (Future_CheckExact(fut)) {
|
if (Future_CheckExact(fut) || Task_CheckExact(fut)) {
|
||||||
return future_schedule_callbacks(fut);
|
return future_schedule_callbacks(fut);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1122,12 +1325,26 @@ typedef struct {
|
||||||
FutureObj *future;
|
FutureObj *future;
|
||||||
} futureiterobject;
|
} futureiterobject;
|
||||||
|
|
||||||
|
|
||||||
|
#define FI_FREELIST_MAXLEN 255
|
||||||
|
static futureiterobject *fi_freelist = NULL;
|
||||||
|
static Py_ssize_t fi_freelist_len = 0;
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
FutureIter_dealloc(futureiterobject *it)
|
FutureIter_dealloc(futureiterobject *it)
|
||||||
{
|
{
|
||||||
PyObject_GC_UnTrack(it);
|
PyObject_GC_UnTrack(it);
|
||||||
Py_XDECREF(it->future);
|
Py_CLEAR(it->future);
|
||||||
PyObject_GC_Del(it);
|
|
||||||
|
if (fi_freelist_len < FI_FREELIST_MAXLEN) {
|
||||||
|
fi_freelist_len++;
|
||||||
|
it->future = (FutureObj*) fi_freelist;
|
||||||
|
fi_freelist = it;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyObject_GC_Del(it);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -1272,10 +1489,23 @@ future_new_iter(PyObject *fut)
|
||||||
PyErr_BadInternalCall();
|
PyErr_BadInternalCall();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
it = PyObject_GC_New(futureiterobject, &FutureIterType);
|
|
||||||
if (it == NULL) {
|
ENSURE_FUTURE_ALIVE(fut)
|
||||||
return NULL;
|
|
||||||
|
if (fi_freelist_len) {
|
||||||
|
fi_freelist_len--;
|
||||||
|
it = fi_freelist;
|
||||||
|
fi_freelist = (futureiterobject*) it->future;
|
||||||
|
it->future = NULL;
|
||||||
|
_Py_NewReference((PyObject*) it);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
it = PyObject_GC_New(futureiterobject, &FutureIterType);
|
||||||
|
if (it == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Py_INCREF(fut);
|
Py_INCREF(fut);
|
||||||
it->future = (FutureObj*)fut;
|
it->future = (FutureObj*)fut;
|
||||||
PyObject_GC_Track(it);
|
PyObject_GC_Track(it);
|
||||||
|
@ -1549,20 +1779,25 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop)
|
||||||
/*[clinic end generated code: output=9f24774c2287fc2f input=8d132974b049593e]*/
|
/*[clinic end generated code: output=9f24774c2287fc2f input=8d132974b049593e]*/
|
||||||
{
|
{
|
||||||
PyObject *res;
|
PyObject *res;
|
||||||
int tmp;
|
|
||||||
if (future_init((FutureObj*)self, loop)) {
|
if (future_init((FutureObj*)self, loop)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PyCoro_CheckExact(coro)) {
|
if (!PyCoro_CheckExact(coro)) {
|
||||||
// fastpath failed, perfom slow check
|
/* 'coro' is not a native coroutine, call asyncio.iscoroutine()
|
||||||
// raise after Future.__init__(), attrs are required for __del__
|
to check if it's another coroutine flavour.
|
||||||
res = PyObject_CallFunctionObjArgs(asyncio_iscoroutine_func,
|
|
||||||
coro, NULL);
|
Do this check after 'future_init()'; in case we need to raise
|
||||||
|
an error, __del__ needs a properly initialized object.
|
||||||
|
*/
|
||||||
|
res = PyObject_CallFunctionObjArgs(
|
||||||
|
asyncio_iscoroutine_func, coro, NULL);
|
||||||
if (res == NULL) {
|
if (res == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
tmp = PyObject_Not(res);
|
|
||||||
|
int tmp = PyObject_Not(res);
|
||||||
Py_DECREF(res);
|
Py_DECREF(res);
|
||||||
if (tmp < 0) {
|
if (tmp < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -2023,8 +2258,6 @@ static PyTypeObject TaskType = {
|
||||||
.tp_finalize = (destructor)TaskObj_finalize,
|
.tp_finalize = (destructor)TaskObj_finalize,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define Task_CheckExact(obj) (Py_TYPE(obj) == &TaskType)
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
TaskObj_dealloc(PyObject *self)
|
TaskObj_dealloc(PyObject *self)
|
||||||
{
|
{
|
||||||
|
@ -2079,22 +2312,14 @@ task_call_step(TaskObj *task, PyObject *arg)
|
||||||
static int
|
static int
|
||||||
task_call_step_soon(TaskObj *task, PyObject *arg)
|
task_call_step_soon(TaskObj *task, PyObject *arg)
|
||||||
{
|
{
|
||||||
PyObject *handle;
|
|
||||||
|
|
||||||
PyObject *cb = TaskStepMethWrapper_new(task, arg);
|
PyObject *cb = TaskStepMethWrapper_new(task, arg);
|
||||||
if (cb == NULL) {
|
if (cb == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle = _PyObject_CallMethodIdObjArgs(task->task_loop, &PyId_call_soon,
|
int ret = call_soon(task->task_loop, cb, NULL);
|
||||||
cb, NULL);
|
|
||||||
Py_DECREF(cb);
|
Py_DECREF(cb);
|
||||||
if (handle == NULL) {
|
return ret;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_DECREF(handle);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -2746,6 +2971,26 @@ _asyncio__leave_task_impl(PyObject *module, PyObject *loop, PyObject *task)
|
||||||
/*********************** Module **************************/
|
/*********************** Module **************************/
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
module_free_freelists()
|
||||||
|
{
|
||||||
|
PyObject *next;
|
||||||
|
PyObject *current;
|
||||||
|
|
||||||
|
next = (PyObject*) fi_freelist;
|
||||||
|
while (next != NULL) {
|
||||||
|
assert(fi_freelist_len > 0);
|
||||||
|
fi_freelist_len--;
|
||||||
|
|
||||||
|
current = next;
|
||||||
|
next = (PyObject*) ((futureiterobject*) current)->future;
|
||||||
|
PyObject_GC_Del(current);
|
||||||
|
}
|
||||||
|
assert(fi_freelist_len == 0);
|
||||||
|
fi_freelist = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
module_free(void *m)
|
module_free(void *m)
|
||||||
{
|
{
|
||||||
|
@ -2764,6 +3009,8 @@ module_free(void *m)
|
||||||
|
|
||||||
Py_CLEAR(current_tasks);
|
Py_CLEAR(current_tasks);
|
||||||
Py_CLEAR(all_tasks);
|
Py_CLEAR(all_tasks);
|
||||||
|
|
||||||
|
module_free_freelists();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue