uvloop icon indicating copy to clipboard operation
uvloop copied to clipboard

uvloop closes underlying sockets that were established outside upon end of asyncio.run()

Open Programmierus opened this issue 3 years ago • 1 comments

This seem to be version/OS unrelated. Sample:

import asyncio
import socket
import uvloop

b = bytes('{"msg": "test"}', 'utf-8')


async def main_async(sock: socket.socket):
    _, writer = await asyncio.open_unix_connection(sock=sock)
    writer.write(b)
    await writer.drain()


if __name__ == '__main__':
    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.connect('data/sockets/echo_server.sock')
    sock.sendall(b)
    uvloop.install()
    asyncio.run(main_async(sock))  # sock gets wrongly closed on asyncio.run() termination
    sock.sendall(b)  # Exception here OSError: [Errno 9] Bad file descriptor

Last line result in exception "OSError: [Errno 9] Bad file descriptor" because sock gets closed right after asyncio.run() terminates. It is wrong behavior, because sock was created outside of asyncio loop and has to be left untouched. The issue doesn't occur using regular asyncio loop.

Programmierus avatar Mar 22 '22 08:03 Programmierus

To work around this issue, you might consider a few alternative approaches:

  1. Use Regular asyncio Event Loop: As you mentioned, the issue doesn't occur with the regular asyncio loop. If this is acceptable for your use case, you can consider sticking with the regular asyncio event loop instead of using uvloop.

  2. Wrap socket in a Context Manager: You can create a custom context manager that wraps the socket and ensures it's not prematurely closed. Here's an example of how you could do this:

import asyncio
import socket
import uvloop
from contextlib import contextmanager

b = bytes('{"msg": "test"}', 'utf-8')

@contextmanager
def open_socket():
    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.connect('data/sockets/echo_server.sock')
    yield sock
    sock.close()

async def main_async(sock: socket.socket):
    _, writer = await asyncio.open_unix_connection(sock=sock)
    writer.write(b)
    await writer.drain()

if __name__ == '__main__':
    uvloop.install()
    with open_socket() as sock:
        asyncio.run(main_async(sock))
        sock.sendall(b)  # Should not result in "OSError: [Errno 9] Bad file descriptor"

Using a context manager ensures that the socket is properly closed when you're done with it, avoiding potential issues with the socket's lifecycle.

  1. Check for Updates: Sometimes, issues like this can be due to specific versions of libraries or platforms. Make sure you're using the latest versions of uvloop, asyncio, and other relevant libraries. If the issue is a known bug, there might be a fix available in newer versions.

ljluestc avatar Aug 17 '23 04:08 ljluestc