From de34cbe9cdaaf7b85fed86f99c2fd071e1a7b1d2 Mon Sep 17 00:00:00 2001 From: Alexander Mohr Date: Tue, 1 Aug 2017 23:31:07 -0700 Subject: [PATCH] bpo-31061: fix crash in asyncio speedup module (GH-2966) --- Lib/test/test_asyncio/test_futures.py | 12 ++++++++++++ Lib/test/test_asyncio/test_tasks.py | 15 +++++++++++++++ .../2017-08-01-09-32-58.bpo-31061.husAYX.rst | 1 + Modules/_asynciomodule.c | 4 ++++ 4 files changed, 32 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2017-08-01-09-32-58.bpo-31061.husAYX.rst diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index ce657fc1b6a..ebedfec7fa3 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -1,6 +1,7 @@ """Tests for futures.py.""" import concurrent.futures +import gc import re import sys import threading @@ -19,9 +20,11 @@ except ImportError: def _fakefunc(f): return f + def first_cb(): pass + def last_cb(): pass @@ -483,6 +486,15 @@ class BaseFutureTests: Exception("elephant"), Exception("elephant")) self.assertRaises(TypeError, fi.throw, list) + def test_future_del_collect(self): + class Evil: + def __del__(self): + gc.collect() + + for i in range(100): + fut = self._new_future(loop=self.loop) + fut.set_result(Evil()) + @unittest.skipUnless(hasattr(futures, '_CFuture'), 'requires the C _asyncio module') diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 686387f4f21..36082ec7c6a 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -3,6 +3,7 @@ import collections import contextlib import functools +import gc import io import os import re @@ -91,6 +92,20 @@ class BaseTaskTests: self.loop.set_task_factory(self.new_task) self.loop.create_future = lambda: self.new_future(self.loop) + def test_task_del_collect(self): + class Evil: + def __del__(self): + gc.collect() + + @asyncio.coroutine + def run(): + return Evil() + + self.loop.run_until_complete( + asyncio.gather(*[ + self.new_task(self.loop, run()) for _ in range(100) + ], loop=self.loop)) + def test_other_loop_future(self): other_loop = asyncio.new_event_loop() fut = self.new_future(other_loop) diff --git a/Misc/NEWS.d/next/Library/2017-08-01-09-32-58.bpo-31061.husAYX.rst b/Misc/NEWS.d/next/Library/2017-08-01-09-32-58.bpo-31061.husAYX.rst new file mode 100644 index 00000000000..650e5f95f94 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-08-01-09-32-58.bpo-31061.husAYX.rst @@ -0,0 +1 @@ +Fixed a crash when using asyncio and threads. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index b998a04e623..d4b313480e9 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -972,6 +972,8 @@ FutureObj_dealloc(PyObject *self) } } + PyObject_GC_UnTrack(self); + if (fut->fut_weakreflist != NULL) { PyObject_ClearWeakRefs(self); } @@ -1846,6 +1848,8 @@ TaskObj_dealloc(PyObject *self) } } + PyObject_GC_UnTrack(self); + if (task->task_weakreflist != NULL) { PyObject_ClearWeakRefs(self); }