bpo-30064: Fix asyncio loop.sock_* race condition issue by fantix · Pull Request #20369 · python/cpython

https://bugs.python.org/issue30064

This is reproducible by a cancel() followed directly by a retry:

done, pending = await asyncio.wait([loop.sock_recv(sock, 1024)], timeout=0.1)
if done:
    data = await done.pop()
else:
    pending.pop().cancel()
    data = await loop.sock_recv(sock, 1024)

This issue applies to all the loop.sock_* methods. Without this PR, users could work it around by adding a sleep(0):

done, pending = await asyncio.wait([loop.sock_recv(sock, 1024)], timeout=0.1)
if done:
    data = await done.pop()
else:
    pending.pop().cancel()
    await asyncio.sleep(0)  # <---- this ensures that the right reader is removed
    data = await loop.sock_recv(sock, 1024)