classification
Title: In some cases asyncio.wait_for can lead to socket leak.
Type: resource usage Stage: patch review
Components: asyncio Versions: Python 3.7, Python 3.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Elvis.Pranskevichus, Nikita Ilyasov, aaliddell, asvetlov, chris.jerdonek, lukasz.langa, miss-islington, yselivanov
Priority: normal Keywords: patch

Created on 2019-07-23 12:16 by Nikita Ilyasov, last changed 2020-10-24 21:22 by chris.jerdonek.

Files
File name Uploaded Description Edit
example.py Nikita Ilyasov, 2019-07-23 12:16 script that can reproduce the problem
Pull Requests
URL Status Linked Edit
PR 21894 merged Elvis.Pranskevichus, 2020-08-15 22:23
PR 21964 merged miss-islington, 2020-08-26 16:43
PR 21965 merged miss-islington, 2020-08-26 16:43
PR 21969 closed Elvis.Pranskevichus, 2020-08-26 19:06
Messages (6)
msg348328 - (view) Author: Nikita Ilyasov (Nikita Ilyasov) Date: 2019-07-23 12:16
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
msg348329 - (view) Author: Nikita Ilyasov (Nikita Ilyasov) Date: 2019-07-23 12:19
server example: https://gist.github.com/hexrain/bc92aa70eebc229365f0ce4bcccf7fc4
msg375937 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2020-08-26 16:42
New changeset a2118a14627256197bddcf4fcecad4c264c1e39d by Elvis Pranskevichus in branch 'master':
bpo-37658: Fix asyncio.wait_for() to respect waited task status (#21894)
https://github.com/python/cpython/commit/a2118a14627256197bddcf4fcecad4c264c1e39d
msg375942 - (view) Author: Łukasz Langa (lukasz.langa) * (Python committer) Date: 2020-08-26 17:15
New changeset 9de6be4e2ae605a1deb6fa72d5c5f66b07817e4c by Miss Islington (bot) in branch '3.9':
bpo-37658: Fix asyncio.wait_for() to respect waited task status (GH-21894) (GH-21964)
https://github.com/python/cpython/commit/9de6be4e2ae605a1deb6fa72d5c5f66b07817e4c
msg375947 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2020-08-26 18:26
New changeset 6e1954cd8286e083e7f8d09516d91b6b15769a4e by Miss Islington (bot) in branch '3.8':
bpo-37658: Fix asyncio.wait_for() to respect waited task status (GH-21894) (#21965)
https://github.com/python/cpython/commit/6e1954cd8286e083e7f8d09516d91b6b15769a4e
msg379542 - (view) Author: Chris Jerdonek (chris.jerdonek) * (Python committer) Date: 2020-10-24 21:22
Issue #42130 that was recently filed appears related to this change.
History
Date User Action Args
2020-10-24 21:22:53chris.jerdoneksetnosy: + chris.jerdonek
messages: + msg379542
2020-08-26 19:06:35Elvis.Pranskevichussetpull_requests: + pull_request21078
2020-08-26 18:26:37yselivanovsetmessages: + msg375947
2020-08-26 17:15:38lukasz.langasetnosy: + lukasz.langa
messages: + msg375942
2020-08-26 16:43:21miss-islingtonsetpull_requests: + pull_request21075
2020-08-26 16:43:13miss-islingtonsetnosy: + miss-islington
pull_requests: + pull_request21074
2020-08-26 16:42:51yselivanovsetmessages: + msg375937
2020-08-16 14:25:32aaliddellsetnosy: + aaliddell
2020-08-15 22:23:50Elvis.Pranskevichussetkeywords: + patch
nosy: + Elvis.Pranskevichus

pull_requests: + pull_request21013
stage: patch review
2019-07-23 12:19:39Nikita Ilyasovsetmessages: + msg348329
2019-07-23 12:16:17Nikita Ilyasovcreate