Author Nikita Ilyasov
Recipients Nikita Ilyasov, asvetlov, yselivanov
Date 2019-07-23.12:16:16
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <>
In some cases `asyncio.wait_for` can lead to socket leak.

Condensed example:

async def _create_connection(timeout=60, ssl_obj):
    loop = asyncio.get_event_loop()
    connector = loop.create_connection(MyEchoClientProtocol, '', 5000, ssl=ssl_obj)
    connector = asyncio.ensure_future(connector)
    tr, pr = await asyncio.wait_for(connector, timeout=timeout, loop=loop)
    return tr, pr

async def main():
    res = await asyncio.wait_for(_acquire_impl(), timeout=timeout, loop=loop)


If my understanding is correct `wait_for` should work in exactly 2 ways

1. the inner task is completed and the outer task will receive the result – transport and protocol in this case
2. The inner task is cancelled and no connection was established

I provided source code for client and server so the problem can be easily reproduced on your system.

certificate and key can be easily generated with `minica`

I found out that if I catch `CancelledError` and add a `done_callback` to the inner task, like so:

        tr, pr = await asyncio.wait_for(connector, timeout=timeout, loop=loop)
        return tr, pr
    except asyncio.CancelledError as e:
        raise e
then inside of `_done_callback` I can access the transport and protocol object and close the transport manually to prevent leaking.

I run `netstat -a | grep 5000 | grep ESTAB | awk '{ print $5 }' | sort | uniq -c | grep 5000` after the script is done and there are many unclosed connections.

The output depends on your hardware so you might need to tweak the timeout parameter
Date User Action Args
2019-07-23 12:16:17Nikita Ilyasovsetrecipients: + Nikita Ilyasov, asvetlov, yselivanov
2019-07-23 12:16:17Nikita Ilyasovsetmessageid: <>
2019-07-23 12:16:17Nikita Ilyasovlinkissue37658 messages
2019-07-23 12:16:17Nikita Ilyasovcreate