Message348328
In some cases `asyncio.wait_for` can lead to socket leak.
Condensed example:
```python
async def _create_connection(timeout=60, ssl_obj):
loop = asyncio.get_event_loop()
connector = loop.create_connection(MyEchoClientProtocol, '127.0.0.1', 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:
```python
try:
tr, pr = await asyncio.wait_for(connector, timeout=timeout, loop=loop)
return tr, pr
except asyncio.CancelledError as e:
connector.add_done_callback(_done_callback)
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:17 | Nikita Ilyasov | set | recipients:
+ Nikita Ilyasov, asvetlov, yselivanov |
2019-07-23 12:16:17 | Nikita Ilyasov | set | messageid: <1563884177.4.0.19779936427.issue37658@roundup.psfhosted.org> |
2019-07-23 12:16:17 | Nikita Ilyasov | link | issue37658 messages |
2019-07-23 12:16:17 | Nikita Ilyasov | create | |
|