-
-
Notifications
You must be signed in to change notification settings - Fork 753
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Ctrl+C hang on Python 3.12 when connections are open #2145
Conversation
The asyncio.Server.wait_closed() method was a no-op in versions of Python earlier than 3.12. The intention of this method was to block until all existing connections are closed, but due to a bug in its implementation, it wouldn't actually wait. This bug was fixed in Python 3.12, which exposed uvicorn's dependence on the buggy behavior: the implementation of uvicorn.Server.shutdown() called wait_closed() before asking existing connections to close. As a result, attempting to stop a Uvicorn server with an open connection would result in a blocked process, with further attempts to Ctrl+C having no effect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Thanks for contributing this fix
Did you test this with Python 3.12? |
@zanieb Yes, and I ran pytest on 3.12. Are there other tests I should run? Edit: This was on Mac. I'll try on Windows as well |
Works well on Windows, except that on the combination of Windows Terminal and PowerShell, Uvicorn doesn't respond to Ctrl+C or Ctrl+Break at all, even with much older versions of Uvicorn. No problem with:
In all of those instances, the behavior is what I expect--hang when exiting with open connections without this fix, clean exit with this fix. |
On the combination of Windows Terminal (v1.18.2822.0) and PowerShell, I can't even use Ctrl+C to stop Update: Restarted Windows Terminal and now WT + PS work as expected, just like the other configurations. 🤷♂️ |
Nice :) |
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
When using urllib3<=1.26.18 with uvicorn<=0.23.2 and python>=3.12, the socket shutdown method of urllib3 combines with the uvicorn server shutdown to leave the server stuck for many seconds: encode/uvicorn#2145 Updating urllib3 or uvicorn (or downgrading Python) fixes the bug, but Fedora 39 shipped Python 3.12 with old urllib3 and uvicorn. Since we (sadly) open a connection just for a single request every time, we can disable the HTTP keep-alive. This allows the server to close the connection earlier, without waiting for the client. This fixes the failing tests/system/test_astacus.py::test_astacus on Fedora 39.
Summary
The asyncio.Server.wait_closed() method was a no-op in versions of Python earlier than 3.12. The intention of this method was to block until all existing connections are closed, but due to a bug in its implementation, it wouldn't actually wait. This bug was fixed in Python 3.12, which exposed uvicorn's dependence on the buggy behavior: the implementation of uvicorn.Server.shutdown() called wait_closed() before asking existing connections to close. As a result, attempting to stop a Uvicorn server with an open connection would result in a blocked process, with further attempts to Ctrl+C having no effect.
See discussion at #2122
Checklist