Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
strategy:
matrix:
python-version:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
Expand All @@ -38,6 +39,9 @@ jobs:
- windows-latest
- windows-11-arm
exclude:
# 3.9 not available for windows arm
- os: windows-11-arm
python-version: "3.9"
# 3.10 not available for windows arm
- os: windows-11-arm
python-version: "3.10"
Expand Down
34 changes: 32 additions & 2 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,22 @@
3.3.2 (unreleased)
==================

- Nothing changed yet.
- Restore support for Python 3.9. The ``requires-python`` metadata,
trove classifiers, and CI test matrix have been updated to include
Python 3.9 again. No C/C++ code was removed when 3.9 support was
dropped in 3.3.0, so this is purely a packaging and CI change.
Combined with the safe finalization fix (below), greenlet now works
reliably on Python 3.9 during interpreter shutdown. See `PR 496
<https://github.com/python-greenlet/greenlet/pull/496>`_.
- Fix SIGSEGV and SIGABRT crashes during interpreter shutdown on
Python < 3.11. Active greenlets being deallocated during
``Py_FinalizeEx`` would trigger ``g_switch()`` in a partially-torn-down
interpreter. The fix checks ``_Py_IsFinalizing()`` and uses
``murder_in_place()`` instead, avoiding the crash at the cost of not
running cleanup code inside the greenlet on older Pythons. Also adds
the first interpreter-shutdown tests to the test suite. See `PR 495
<https://github.com/python-greenlet/greenlet/pull/495>`_.
Fixes :issue:`411`.


3.3.1 (2026-01-23)
Expand Down Expand Up @@ -317,7 +332,22 @@ Known Issues
2.0.0 (2022-10-31)
==================

- Nothing changed yet.
- Restore support for Python 3.9. The ``requires-python`` metadata,
trove classifiers, and CI test matrix have been updated to include
Python 3.9 again. No C/C++ code was removed when 3.9 support was
dropped in 3.3.0, so this is purely a packaging and CI change.
Combined with the safe finalization fix (below), greenlet now works
reliably on Python 3.9 during interpreter shutdown. See `PR 496
<https://github.com/python-greenlet/greenlet/pull/496>`_.
- Fix SIGSEGV and SIGABRT crashes during interpreter shutdown on
Python < 3.11. Active greenlets being deallocated during
``Py_FinalizeEx`` would trigger ``g_switch()`` in a partially-torn-down
interpreter. The fix checks ``_Py_IsFinalizing()`` and uses
``murder_in_place()`` instead, avoiding the crash at the cost of not
running cleanup code inside the greenlet on older Pythons. Also adds
the first interpreter-shutdown tests to the test suite. See `PR 495
<https://github.com/python-greenlet/greenlet/pull/495>`_.
Fixes :issue:`411`.


2.0.0rc5 (2022-10-31)
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ classifiers = [
"Programming Language :: C",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
Expand All @@ -41,7 +42,7 @@ keywords = [
"threads",
"cooperative",
]
requires-python = ">=3.10"
requires-python = ">=3.9"
dynamic = ["version"]

[project.urls]
Expand Down
21 changes: 21 additions & 0 deletions src/greenlet/PyGreenlet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,27 @@ green_clear(PyGreenlet* self)
static int
_green_dealloc_kill_started_non_main_greenlet(BorrowedGreenlet self)
{
// During interpreter finalization, we cannot safely throw GreenletExit
// into the greenlet. Doing so calls g_switch(), which performs a stack
// switch and runs Python code via _PyEval_EvalFrameDefault. On Python
// < 3.11, executing Python code in a partially-torn-down interpreter
// leads to SIGSEGV (greenlet 3.x) or SIGABRT (greenlet 2.x).
//
// Python 3.11+ restructured interpreter finalization internals (frame
// representation, data stack management, recursion tracking) so that
// g_switch() during finalization is safe. On older Pythons, we simply
// mark the greenlet dead without throwing, which avoids the crash at
// the cost of not running any cleanup code inside the greenlet.
//
// See: https://github.com/python-greenlet/greenlet/issues/411
// https://github.com/python-greenlet/greenlet/issues/351
#if !GREENLET_PY311
if (_Py_IsFinalizing()) {
self->murder_in_place();
return 1;
}
#endif

/* Hacks hacks hacks copied from instance_dealloc() */
/* Temporarily resurrect the greenlet. */
assert(self.REFCNT() == 0);
Expand Down
20 changes: 20 additions & 0 deletions src/greenlet/TThreadState.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,26 @@ class ThreadState {
return;
}

// During interpreter finalization, Python APIs like
// PyImport_ImportModule are unsafe (the import machinery may
// be partially torn down). On Python < 3.11, perform only the
// minimal cleanup that is safe: clear our strong references so
// we don't leak, but skip the GC-based leak detection.
//
// Python 3.11+ restructured interpreter finalization so that
// these APIs remain safe during shutdown.
#if !GREENLET_PY311
if (_Py_IsFinalizing()) {
this->tracefunc.CLEAR();
if (this->current_greenlet) {
this->current_greenlet->murder_in_place();
this->current_greenlet.CLEAR();
}
this->main_greenlet.CLEAR();
return;
}
#endif

// We should not have an "origin" greenlet; that only exists
// for the temporary time during a switch, which should not
// be in progress as the thread dies.
Expand Down
Loading
Loading