You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This callback caused greenlet APIs to become unavailable far too soon
during interpreter shutdown. Now they remain available while all
``atexit`` callbacks run; using greenlet APIs from atexit callbacks
registered in any order is a valid thing to do and happens naturally
with gevent monkey-patching.
A careful, thorough reading of the CPython documentation and the
CPython source code for all supported versions of Python (3.10+)
indicates that any gating needing to be done is correctly handled with
``Py_IsFinalizing``.
The comments in PR python-greenlet#499 that added the callback were wrong: it's not
possible to access partially torn-down state during ``atexit``. And
the tests provided in that PR did not demonstrate any crashes (they
pass with or without the ``atexit`` callback).
CPython shuts things down in the following order:
1. Attempt to wait for all non-daemon threads to finish.
2. Invoke any pending calls.
3. Invoke ``atexit`` callbacks. At this point, it is guaranteed that
the interpreter is still fully operational, the import machinery
still works, etc. All such callbacks can successfully use greenlet
APIs.
4. Finalize any sub-intepreters in newer versions. greenlet doesn't
support sub-interpreters, so this is inconsequential.
5. Detach any remaining threads in newer versions.
6. Set ``Py_IsFinalizing`` to true. Any other threads still remaining
will no longer be able to run Python code. All cleanup operations
continue in this thread. At this point, ``getcurrent`` will start
returning ``None`` or raising an exception (C API).
7. Garbage collect threads (active objects on the call stack).
8. Run cyclic garbage collection.
9. Only now does the interpreter begin to tear down module state,
beginning by clearing out module dictionaries and allowing
finalizers/weakref to be cleared. Up to and through the beginning
of this process, greenlet APIs are safe to call, and greenlet
objects can be used (switched/thrown). At some point, this may
become untrue, but all unreachable greenlet objects (which should
be anything not stashed away in a C extension). greenlet can't do
anything about extant objects that may still have methods called on
them, but it can prevent getting access to implicit objects that
may be getting torn down: that's why getcurrent behaves the way it
does (any exceptions generated during at least steps 7, 8, 9 are
"unraisable" and just get printed). Note that the
CPython documentation specifically calls out the fact that modules
may be finalized in any order, so modules that rely on other
modules MUST be coded defensively.
The long and short is that greenlet can't do anything reasonable to
protect other modules from accessing state that may be torn
down (``atexit`` is too soon; a PyCapsule destructor may be too late
or never get fired; module ``m_clear`` and ``m_free`` functions may
never get called). It's up to other C modules to check for interpreter
finalization and be aware that any other C modules they use may no
longer be valid at that point.
0 commit comments