Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Lib/test/test_asyncio/test_futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,24 @@ def test_future_disallow_multiple_initialization(self):
with self.assertRaises(RuntimeError, msg="is already initialized"):
f.__init__(loop=self.loop)

def test_futureiter_send_after_throw_no_crash(self):
fut = self._new_future(loop=self.loop)
it = fut.__await__()
next(it)
with self.assertRaises(RuntimeError):
it.throw(RuntimeError)
with self.assertRaises(StopIteration):
it.send(None)

def test_futureiter_send_after_close_no_crash(self):
fut = self._new_future(loop=self.loop)
it = fut.__await__()
next(it)
it.close()
with self.assertRaises(StopIteration):
it.send(None)


@unittest.skipUnless(hasattr(futures, '_CFuture'),
'requires the C _asyncio module')
class CFutureTests(BaseFutureTests, test_utils.TestCase):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a crash in asyncio.Future iterator when send() is
called after throw() or close().
39 changes: 28 additions & 11 deletions Modules/_asynciomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1795,10 +1795,9 @@ FutureIter_dealloc(PyObject *it)
}

static PySendResult
FutureIter_am_send_lock_held(futureiterobject *it, PyObject **result)
FutureIter_am_send_lock_held(FutureObj *fut, PyObject **result)
{
PyObject *res;
FutureObj *fut = it->future;
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut);

*result = NULL;
Expand Down Expand Up @@ -1829,11 +1828,24 @@ FutureIter_am_send(PyObject *op,
PyObject **result)
{
futureiterobject *it = (futureiterobject*)op;

FutureObj *fut;
Py_BEGIN_CRITICAL_SECTION(op);
fut = (FutureObj*)Py_XNewRef(it->future);
Py_END_CRITICAL_SECTION();

if (fut == NULL) {
PyErr_SetNone(PyExc_StopIteration);
*result = NULL;
return PYGEN_ERROR;
}

/* arg is unused, see the comment on FutureIter_send for clarification */
PySendResult res;
Py_BEGIN_CRITICAL_SECTION(it->future);
res = FutureIter_am_send_lock_held(it, result);
Py_BEGIN_CRITICAL_SECTION(fut);
res = FutureIter_am_send_lock_held(fut, result);
Py_END_CRITICAL_SECTION();
Py_DECREF(fut);
return res;
}

Expand Down Expand Up @@ -1927,7 +1939,9 @@ FutureIter_throw(PyObject *op, PyObject *const *args, Py_ssize_t nargs)
goto fail;
}

Py_BEGIN_CRITICAL_SECTION(self);
Py_CLEAR(self->future);
Py_END_CRITICAL_SECTION();

PyErr_Restore(type, val, tb);

Expand All @@ -1940,6 +1954,16 @@ FutureIter_throw(PyObject *op, PyObject *const *args, Py_ssize_t nargs)
return NULL;
}

static PyObject *
FutureIter_close(PyObject *self, PyObject *arg)
{
futureiterobject *it = (futureiterobject*)self;
Py_BEGIN_CRITICAL_SECTION(self);
Py_CLEAR(it->future);
Py_END_CRITICAL_SECTION();
Py_RETURN_NONE;
}

static int
FutureIter_clear(PyObject *op)
{
Expand All @@ -1948,13 +1972,6 @@ FutureIter_clear(PyObject *op)
return 0;
}

static PyObject *
FutureIter_close(PyObject *self, PyObject *arg)
{
(void)FutureIter_clear(self);
Py_RETURN_NONE;
}

static int
FutureIter_traverse(PyObject *op, visitproc visit, void *arg)
{
Expand Down
Loading