Skip to content

Deadlock in iter(callable, sentinel) when __eq__ re-enters the same iterator #8011

@doma17

Description

@doma17

Summary

iter(callable, sentinel) can deadlock if the sentinel comparison executes
Python-level __eq__ code and that __eq__ re-enters the same iterator.

This seems related to #6589. The original reproducer there uses the same object
for the callable result and the sentinel, so current main may short-circuit on
identity before calling __eq__. With distinct objects, __eq__ is called and
the deadlock still reproduces.

Reproducer

class Value:
    entered = False
    it = None

    def __eq__(self, other):
        if not self.entered:
            self.entered = True
            try:
                next(self.it)
            except StopIteration:
                pass
        return True


value = Value()
sentinel = object()

itr = iter(lambda: value, sentinel)
value.it = itr

try:
    next(itr)
except StopIteration:
    pass
else:
    raise AssertionError("outer next should stop")

assert value.entered

Expected

CPython exits normally. The inner re-entrant next() exhausts the callable
iterator, and the outer next() also finishes with StopIteration.

With debug prints added, CPython produces:

Python 3.14.5
calling outer next()
__eq__ entered
calling inner next()
__eq__ entered
__eq__ returning True
inner StopIteration
__eq__ returning True
outer StopIteration
entered True

Actual

RustPython main hangs. With debug prints added, the output is:

calling outer next()
__eq__ entered
calling inner next()
RustPython timed out after 5 seconds (hang/deadlock)

It looks like PyCallableIterator::next is holding its status lock while running
sentinel equality. Since equality can run Python code, the inner next() waits
on the same lock held by the outer next().

Python Documentation

iter(callable, sentinel) docs:

https://docs.python.org/3/library/functions.html#iter

CPython callable iterator implementation:

https://github.com/python/cpython/blob/main/Objects/iterobject.c

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions