From a1990d8cc8064e23e9e29283da2228d411093b84 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Wed, 6 Aug 2025 16:51:16 -0500 Subject: [PATCH] Python3.14/JIT: Save and restore the PyThreadState current_executor object. Fixes #460 --- CHANGES.rst | 3 +++ src/greenlet/TGreenlet.hpp | 6 ++++++ src/greenlet/TPythonState.cpp | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 018eef84..c006d756 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,9 @@ contributed by Michał Górny. Note that while greenlet will BUILD in a free-threaded Python, it will cause the GIL to be allocated and used, and memory may leak. +- Fix an assertion error on debug builds of Python 3.14 when using the + experimental JIT. See :issue:`460 + `_. 3.2.3 (2025-06-05) diff --git a/src/greenlet/TGreenlet.hpp b/src/greenlet/TGreenlet.hpp index aa6d6d5a..e152353c 100644 --- a/src/greenlet/TGreenlet.hpp +++ b/src/greenlet/TGreenlet.hpp @@ -116,6 +116,12 @@ namespace greenlet #endif #if GREENLET_PY314 int py_recursion_depth; + // I think this is only used by the JIT. At least, + // we only got errors not switching it when the JIT was enabled. + // Python/generated_cases.c.h:12469: _PyEval_EvalFrameDefault: + // Assertion `tstate->current_executor == NULL' failed. + // see https://github.com/python-greenlet/greenlet/issues/460 + PyObject* current_executor; #elif GREENLET_PY312 int py_recursion_depth; int c_recursion_depth; diff --git a/src/greenlet/TPythonState.cpp b/src/greenlet/TPythonState.cpp index a7f743cf..8833a807 100644 --- a/src/greenlet/TPythonState.cpp +++ b/src/greenlet/TPythonState.cpp @@ -14,6 +14,7 @@ PythonState::PythonState() #endif #if GREENLET_PY314 ,py_recursion_depth(0) + ,current_executor(nullptr) #elif GREENLET_PY312 ,py_recursion_depth(0) ,c_recursion_depth(0) @@ -136,6 +137,7 @@ void PythonState::operator<<(const PyThreadState *const tstate) noexcept #if GREENLET_PY311 #if GREENLET_PY314 this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; + this->current_executor = tstate->current_executor; #elif GREENLET_PY312 this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; this->c_recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining; @@ -213,6 +215,7 @@ void PythonState::operator>>(PyThreadState *const tstate) noexcept #if GREENLET_PY311 #if GREENLET_PY314 tstate->py_recursion_remaining = tstate->py_recursion_limit - this->py_recursion_depth; + tstate->current_executor = this->current_executor; this->unexpose_frames(); #elif GREENLET_PY312 tstate->py_recursion_remaining = tstate->py_recursion_limit - this->py_recursion_depth; @@ -262,6 +265,7 @@ void PythonState::set_initial_state(const PyThreadState* const tstate) noexcept this->_top_frame = nullptr; #if GREENLET_PY314 this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; + this->current_executor = tstate->current_executor; #elif GREENLET_PY312 this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; // XXX: TODO: Comment from a reviewer: