Skip to content

Race condition leading to infinite loop in asyncio/base_events.py #122296

Closed as not planned
@TheTechromancer

Description

@TheTechromancer

Bug report

Bug description:

Thanks for taking the time to look at this issue.

Recently I've been running into a rare and insidious race condition that can result in a deadlock. The condition triggers when asyncio debugging is enabled (PYTHONASYNCIODEBUG=1), and a slow callback is encountered:

if dt >= self.slow_callback_duration:
logger.warning('Executing %s took %.3f seconds',
_format_handle(handle), dt)

We first encountered the issue during our test runs, where it would occur probably approximately 1 out of 4000 times the above message was printed.

The deadlock is actually some form of "livelock", where the interpreter appears to be frozen, but CPU usage is stuck at 100%. The stack trace looks similar to a recursion bug; however it doesn't seem to descend far enough to trigger a RecursionError, so it survives as an infinite loop.

It took me weeks to track it down, and in the end I was only able to get the stack trace by attaching to the frozen python process with gdb:

  File "/usr/local/lib/python3.9/asyncio/base_events.py", line 634, in run_until_complete
    self.run_forever()
  File "/usr/local/lib/python3.9/asyncio/base_events.py", line 601, in run_forever
    self._run_once()
  File "/usr/local/lib/python3.9/asyncio/base_events.py", line 1901, in _run_once
    _format_handle(handle), dt)
  File "/usr/local/lib/python3.9/asyncio/base_events.py", line 78, in _format_handle
    return repr(cb.__self__)
  File "/usr/local/lib/python3.9/asyncio/base_tasks.py", line 21, in _task_repr_info
    info.insert(3, f'wait_for={task._fut_waiter!r}')
  File "/usr/local/lib/python3.9/asyncio/base_futures.py", line 76, in _future_repr_info
    info.append(_format_callbacks(future._callbacks))
  File "/usr/local/lib/python3.9/asyncio/base_futures.py", line 37, in _format_callbacks
    cb = '{}, {}'.format(format_cb(cb[0][0]), format_cb(cb[1][0]))
  File "/usr/local/lib/python3.9/asyncio/base_futures.py", line 32, in format_cb
    return format_helpers._format_callback_source(callback, ())
  File "/usr/local/lib/python3.9/asyncio/format_helpers.py", line 23, in _format_callback_source
    func_repr = _format_callback(func, args, None)
  File "/usr/local/lib/python3.9/asyncio/format_helpers.py", line 47, in _format_callback
    return _format_callback(func.func, func.args, func.keywords, suffix)
  File "/usr/local/lib/python3.9/asyncio/format_helpers.py", line 56, in _format_callback
    func_repr += _format_args_and_kwargs(args, kwargs)
  File "/usr/local/lib/python3.9/asyncio/format_helpers.py", line 40, in _format_args_and_kwargs
    items.extend(f'{k}={reprlib.repr(v)}' for k, v in kwargs.items())
  File "/usr/local/lib/python3.9/asyncio/format_helpers.py", line 40, in <genexpr>
    items.extend(f'{k}={reprlib.repr(v)}' for k, v in kwargs.items())
  File "/usr/local/lib/python3.9/reprlib.py", line 52, in repr
    return self.repr1(x, self.maxlevel)
  File "/usr/local/lib/python3.9/reprlib.py", line 60, in repr1
    return getattr(self, 'repr_' + typename)(x, level)
  File "/usr/local/lib/python3.9/reprlib.py", line 103, in repr_deque
    return self._repr_iterable(x, level, 'deque([', '])', self.maxdeque)
  File "/usr/local/lib/python3.9/reprlib.py", line 71, in _repr_iterable
    pieces = [repr1(elem, newlevel) for elem in islice(x, maxiter)]
  File "/usr/local/lib/python3.9/reprlib.py", line 71, in <listcomp>
    pieces = [repr1(elem, newlevel) for elem in islice(x, maxiter)]
  File "/usr/local/lib/python3.9/reprlib.py", line 62, in repr1
    return self.repr_instance(x, level)
  File "/usr/local/lib/python3.9/reprlib.py", line 139, in repr_instance
    s = builtins.repr(x)
  File "/usr/local/lib/python3.9/collections/__init__.py", line 458, in __repr__
    return self.__class__.__name__ + repr_fmt % self

    ...repeats ~40 times
    
    return self.__class__.__name__ + repr_fmt % self
  File "/usr/local/lib/python3.9/asyncio/base_futures.py", line 76, in _future_repr_info
    info.append(_format_callbacks(future._callbacks))
  File "/usr/local/lib/python3.9/asyncio/base_futures.py", line 37, in _format_callbacks
    cb = '{}, {}'.format(format_cb(cb[0][0]), format_cb(cb[1][0]))
  File "/usr/local/lib/python3.9/asyncio/base_futures.py", line 32, in format_cb
    return format_helpers._format_callback_source(callback, ())
  File "/usr/local/lib/python3.9/asyncio/format_helpers.py", line 23, in _format_callback_source
    func_repr = _format_callback(func, args, None)
  File "/usr/local/lib/python3.9/asyncio/format_helpers.py", line 47, in _format_callback
    return _format_callback(func.func, func.args, func.keywords, suffix)
  File "/usr/local/lib/python3.9/asyncio/format_helpers.py", line 56, in _format_callback
    func_repr += _format_args_and_kwargs(args, kwargs)
  File "/usr/local/lib/python3.9/asyncio/format_helpers.py", line 40, in _format_args_and_kwargs
    items.extend(f'{k}={reprlib.repr(v)}' for k, v in kwargs.items())
  File "/usr/local/lib/python3.9/asyncio/format_helpers.py", line 40, in <genexpr>
    items.extend(f'{k}={reprlib.repr(v)}' for k, v in kwargs.items())
  File "/usr/local/lib/python3.9/reprlib.py", line 52, in repr
    return self.repr1(x, self.maxlevel)
  File "/usr/local/lib/python3.9/reprlib.py", line 62, in repr1
    return self.repr_instance(x, level)
  File "/usr/local/lib/python3.9/reprlib.py", line 139, in repr_instance
    s = builtins.repr(x)
  File "/usr/local/lib/python3.9/collections/__init__.py", line 458, in __repr__
    return self.__class__.__name__ + repr_fmt % self
  File "<string>", line 1, in <module>

Full traceback:
python_traceback.txt

CPython versions tested on:

3.9

Operating systems tested on:

Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    pendingThe issue will be closed if no feedback is providedtopic-asynciotype-bugAn unexpected behavior, bug, or error

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions