wait_for(coro, timeout=0) memleak
| BPO | 37042 |
|---|---|
| Nosy | @asvetlov, @1st1, @ixje |
| Files |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee = None
closed_at = None
created_at = <Date 2019-05-25.12:09:54.889>
labels = ['expert-asyncio', '3.7', 'performance']
title = 'wait_for(coro, timeout=0) memleak'
updated_at = <Date 2019-05-25.12:34:53.128>
user = 'https://github.com/ixje'
bugs.python.org fields:
activity = <Date 2019-05-25.12:34:53.128>
actor = 'ixje'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['asyncio']
creation = <Date 2019-05-25.12:09:54.889>
creator = 'ixje'
dependencies = []
files = ['48356', '48357']
hgrepos = []
issue_num = 37042
keywords = []
message_count = 6.0
messages = ['343470', '343472', '343473', '343475', '343476', '343477']
nosy_count = 3.0
nosy_names = ['asvetlov', 'yselivanov', 'ixje']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = 'resource usage'
url = 'https://bugs.python.org/issue37042'
versions = ['Python 3.7']
I have a networked process that looks somewhat like this in its most basic form
import asyncio
shutting_down = False
async def read_message(reader, timeout=30):
async def _read(reader: asyncio.StreamReader):
try:
d = await reader.readexactly(24)
# do something with the data
print("I'm never called")
except:
return None
try:
return await asyncio.wait_for(_read(reader), timeout)
except Exception:
return None
async def my_service():
reader, writer = await asyncio.open_connection('127.0.0.1', 20333)
while not shutting_down:
m = await read_message(reader, timeout=0)
if m is None:
continue
# else process message
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.create_task(my_service())
loop.run_forever()
read_message() has a default timeout of 30, but I thought setting it to 0 (instead of None) would be equal to blocking. This bleeds 16GB of memory in ~3 minutes. A minimal example is provided.
I manually applied the patch of https://bugs.python.org/issue36613 to a self compiled build of 3.7.3 (https://github.com/python/cpython/commit/ef4ec6ed12d6c6200a85068f60483723298b6ff4) on Ubuntu 18.04 and that did not solve the problem.
Thanks for the report.
If the problem is in asyncio.wait_for() function the real network code can be stripped for the leakage example and replaced with await asyncio.sleep().
Would you try to boil down the snippet by converting it into a code that I can execute on my laptop to reproduce the problem?
Hi Andrew, There is an attached minimal example (that differs from the code given in the first comment). I couldn't attach 2 files. So I pasted the code of one file to showcase how we could run into the issue, then a minimal reproducible example without network code in the attachment.
Nice, thanks!
This is the consumption I'm seeing.
Perhaps also worth mentioning is that when we supply None as timeout value in the wait_for() in the minimal sample, then it still reports 60MB memory usage. Seems pretty steep for doing basically nothing but looping around.
I am unable to reproduce the leak on main branch. I profiled with memray and the rss usage is constant.

@kumaraditya303 were you able to reproduce it with 3.7?
I tried myself - indeed I can reproduce it on 3.7 but no on main (on a MAC).