Skip to content

Commit

Permalink
bpo-36607: Eliminate RuntimeError raised by asyncio.all_tasks() (GH-1…
Browse files Browse the repository at this point in the history
…3971)

If internal tasks weak set is changed by another thread during iteration.

https://bugs.python.org/issue36607
(cherry picked from commit 65aa64f)

Co-authored-by: Andrew Svetlov <andrew.svetlov@gmail.com>
  • Loading branch information
miss-islington and asvetlov authored Jun 11, 2019
1 parent b9ecc0f commit 83abd96
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 6 deletions.
38 changes: 32 additions & 6 deletions Lib/asyncio/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,22 @@ def all_tasks(loop=None):
"""Return a set of all tasks for the loop."""
if loop is None:
loop = events.get_running_loop()
# NB: set(_all_tasks) is required to protect
# from https://bugs.python.org/issue34970 bug
return {t for t in list(_all_tasks)
# Looping over a WeakSet (_all_tasks) isn't safe as it can be updated from another
# thread while we do so. Therefore we cast it to list prior to filtering. The list
# cast itself requires iteration, so we repeat it several times ignoring
# RuntimeErrors (which are not very likely to occur). See issues 34970 and 36607 for
# details.
i = 0
while True:
try:
tasks = list(_all_tasks)
except RuntimeError:
i += 1
if i >= 1000:
raise
else:
break
return {t for t in tasks
if futures._get_loop(t) is loop and not t.done()}


Expand All @@ -54,9 +67,22 @@ def _all_tasks_compat(loop=None):
# method.
if loop is None:
loop = events.get_event_loop()
# NB: set(_all_tasks) is required to protect
# from https://bugs.python.org/issue34970 bug
return {t for t in list(_all_tasks) if futures._get_loop(t) is loop}
# Looping over a WeakSet (_all_tasks) isn't safe as it can be updated from another
# thread while we do so. Therefore we cast it to list prior to filtering. The list
# cast itself requires iteration, so we repeat it several times ignoring
# RuntimeErrors (which are not very likely to occur). See issues 34970 and 36607 for
# details.
i = 0
while True:
try:
tasks = list(_all_tasks)
except RuntimeError:
i += 1
if i >= 1000:
raise
else:
break
return {t for t in tasks if futures._get_loop(t) is loop}


def _set_task_name(task, name):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Eliminate :exc:`RuntimeError` raised by :func:`asyncio.all_tasks()` if
internal tasks weak set is changed by another thread during iteration.

0 comments on commit 83abd96

Please sign in to comment.