MagicStack / MagicStack/uvloop

BusError in uvloop within loop extract_stack for nested async_generators

Offen
#611 0 Kommentare 2 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Vorherrschende Sprache
Cython
Sterne
11.9k
Forks
616
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

  • uvloop version:
  • 0.19.0 (tested 0.17.0 to 0.19.0)
  • Python version: 3.12.4
  • Platform: OSX Ventura 13.4/ Darwin 22.5.0
  • Can you reproduce the bug with PYTHONASYNCIODEBUG in env?:
  • This bug only occurs if -X dev or PYTHONDEVMODE=1 or PYTHONASYNCIODEBUG=1 or loop.set_debug(True)
  • Does uvloop behave differently from vanilla asyncio? How?:
  • Vanilla asyncio and uvloop with loop.set_debug(False) do not crash in nested async_generator .asend calls

I have a pattern called DeferredExecutor which basically allows one to write:

class Foo:
        @database(transaction=True, iterable=False)
        async def save(self, *args, **kwargs):
               # /snip
              rows = yield DeferredQuery("some sql")
              assert isinstance(rows, tuple)
              yield Return(...)

And have it be wrapped by a decorator that will call the async_generator, run the deferred queries against a DB and .asend( the result back into the generator, which will throw an StopAsyncIteration at the conclusion of the generator, and allows me to catch it and end the generators execution.

I am getting my database access object library to run on Python 3.12 (jumping from Python 3.7).

However, on Python 3.12, a particular integration test that uses a particularly reduced implementation:

class Tenant:
    @database(iterable=False, transaction=True)
    async def invite_users(cls, *tenant_user_pairs, instance=None):
        invites = (instance or cls).create_invites_for(*tenant_user_pairs)
        gen_event_loop = Invite.save.raw_iterable(*invites)
        result = None
        while True:
            result = await gen_event_loop.asend(result)
            if isinstance(result, Return):
                break
            result = yield result
        yield result # StopAsyncIteration gets thrown here

causes uvloop to crash with a Bus Error on OSX but only when loop.set_debug(True) (which is autoset via the environment variables or interpreter devmode)

Regular asyncio, debug or not, works just fine. If I patch the EventLoopPolicy to.set_debug(False) like:

def patch_uvloop():
    if uvloop_version_info < (99, 99, 99) and platform.system() == 'Darwin':
        cls = uvloop.EventLoopPolicy
        if hasattr(cls, '__patched__'):
            return cls
        class EventLoopPolicy(cls):
            __patched__ = True
            #  ARJ: so uvloop will crash the entire interpreter sometimes
            # and the crash originates on OSX. Disable debug mode
            # (which keeps it from touching uvloop's extract_stack() which 
            # explodes badly)

            def _loop_factory(self):
                loop = super()._loop_factory()
                loop.set_debug(False)
                return loop
        uvloop.EventLoopPolicy = EventLoopPolicy

asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

Then the crashes never happen.

I do have lldb/gdb and the patience to recompile things with more debugging information (provided I know -how-) but a careful inspection of the following stack trace shows that it explodes after going into the extract_stack function of uvloop:

This does remind me of https://github.com/python/cpython/issues/94694

I wonder if the extract_stack function is tolerant of encountering negative numbers in the offset extraction of the trace.

Stack report:
stack.txt

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Reproduziere den Fall mit verschachteltem async_generator unter Python 3.12.4 und macOS bei aktiviertem uvloop-Debugmodus unter Verwendung des bereitgestellten Stack-Reports als Referenz. Untersuche uvloop/handles/handle.pyx und uvloop/cbhandles.pyx im Umfeld von extract_stack und ermittle die Ursache des Bus Error. Verifiziere anschließend, dass der Absturz behoben ist, ohne den Debugmodus zu deaktivieren.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
backend
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
35/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.