-
-
Notifications
You must be signed in to change notification settings - Fork 32k
SelectorEventLoop sock_connect can return before cleaning up if sock.connect encounters BlockingIOError or InterruptedError on cancellation
#131728
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
Comments
sock_connect can return without cleaning up if sock.connect encounters BlockingIOError or InterruptedError on cancellationsock_connect can return before cleaning up if sock.connect encounters BlockingIOError or InterruptedError on cancellation
Workaround the race documented in python/cpython#131728
Workaround the race documented in python/cpython#131728
|
I can't make a reproducer for this. I tried async def socket_runner(port: int) -> None:
loop = asyncio.get_running_loop()
while True:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(False)
task = asyncio.current_task()
loop.call_later(0.1, task.cancel)
try:
await loop.sock_connect(sock, ("1.1.1.1", port))
except asyncio.CancelledError:
sock.close()But the callback always fires in time. |
|
I thinkthe problem is actually happening in cpython/Lib/asyncio/base_events.py Line 1220 in 151d1bf
loop.sock_connect that ends up losing the race
|
|
So if cpython/Lib/asyncio/base_events.py Line 1224 in 151d1bf
transport.close but the call_soon in cpython/Lib/asyncio/selector_events.py Line 950 in 151d1bf
create_connection is done and the socket gets closed in the downstream code, than the call_soon to add the reader runs after
|
|
I'll keep working on a reproducer so this issue is actually actionable. |
|
I can't come up with a working reproducer for that either. I'm going to need more information for the original reporter. I'll close this for now and reopen if I can get enough information to make a reproducer. Sorry for the noise. |
|
related |
|
Reproducer that works on 3.12 and 3.13 import asyncio
import socket
from contextlib import suppress
async def socket_runner() -> None:
loop = asyncio.get_running_loop()
while True:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(False)
task = asyncio.create_task(
loop.create_connection(asyncio.Protocol, None, None, sock=sock)
)
for _ in range(10):
await asyncio.sleep(0)
task.cancel()
with suppress(asyncio.CancelledError):
await task
sock.close()
asyncio.run(socket_runner())Will produce After a few runs |
|
If I take the close it looks like the socket leaks even after |
|
Finished tracing it and its still not a good reproducer. |
|
Can you see what |
|
Still looking into this one, if I come up with something useful, I will reopen |


Uh oh!
There was an error while loading. Please reload this page.
Bug report
Bug description:
To make this happen:
Call
loop.sock_connectand cancel it before the socket is connected.Because
_sock_write_doneis called viaadd_done_callbackit gets called in the next iteration of the event loop on cancellation.cpython/Lib/asyncio/selector_events.py
Line 658 in 9c7ef0c
sock_connectwill return before_sock_write_doneis called which means the writer has not been removed yet and can get reused.Additionally, this makes the
closeincpython/Lib/asyncio/base_events.py
Line 1047 in 9c7ef0c
CPython versions tested on:
3.10 but the same code exists for 3.13+
Operating systems tested on:
Linux
The text was updated successfully, but these errors were encountered: