This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author Ben.Darnell
Recipients Ben.Darnell
Date 2020-10-07.01:09:43
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1602032984.5.0.326433134447.issue41962@roundup.psfhosted.org>
In-reply-to
Content
I'm dealing with a subtle deadlock involving concurrent.futures.ThreadPoolExecutor, and my solution that worked in Python 3.8 broke with 3.9. I'm running some long-running (possibly infinite) tasks in the thread pool, and I cancel them in an `atexit` callback so that everything can shut down cleanly (before ThreadPoolExecutor joins all worker threads in its own `atexit` hook).

Python 3.9 broke this due to https://bugs.python.org/issue39812. That change introduced a new atexit-like mechanism to the threading module and uses it where Python 3.8 used regular atexit. This means that ThreadPoolExecutor's atexit runs before mine, and since I never get a chance to cancel my tasks, it deadlocks.

One way I can solve this is to move my own atexit function to `threading._register_atexit`, so my strawman proposal here is to make that function public and documented. 

On the other hand, even without the change in Python 3.9, my use of `atexit` smells like an abuse of implementation details in ThreadPoolExecutor (getting the atexit callbacks called in the right order was tricky when the concurrent.futures module started using lazy loading in Python 3.7). So I would welcome other suggestions about how to handle long-running but cancelable operations in a ThreadPoolExecutor at shutdown. 

One clean solution is to do the cancellation at the end of the main module instead of in an atexit hook. However, I'm doing this at a library so I don't have any way but atexit to ensure that this happens. Another option is to forego ThreadPoolExecutor entirely and manage the threads myself. 

My code in question is in a not-yet-released branch of Tornado: https://github.com/tornadoweb/tornado/blob/5913aa43ecfdaa76876fc57867062227b907b1dd/tornado/platform/asyncio.py#L57-L73

With the master branch of Tornado, Python 3.9, and Windows, `python -c "from tornado.httpclient import HTTPClient; c = HTTPClient()` reliably deadlocks at interpreter shutdown.
History
Date User Action Args
2020-10-07 01:09:44Ben.Darnellsetrecipients: + Ben.Darnell
2020-10-07 01:09:44Ben.Darnellsetmessageid: <1602032984.5.0.326433134447.issue41962@roundup.psfhosted.org>
2020-10-07 01:09:44Ben.Darnelllinkissue41962 messages
2020-10-07 01:09:43Ben.Darnellcreate