See More

/* -*- mode: c++; indent-tabs-mode: nil -*- */ /* python313_internals.h Qore Programming Language Copyright 2020 - 2026 Qore Technologies, s.r.o. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _QORE_PYTHON313_INTERNALS_H #define _QORE_PYTHON313_INTERNALS_H #include // Python 3.13 uses global recursion limits instead of per-thread inline int PyThreadState_GetRecursionLimit(PyThreadState* state) { (void)state; // unused in Python 3.13+ return Py_GetRecursionLimit(); } inline void PyThreadState_UpdateRecursionLimit(PyThreadState* state, int new_limit) { (void)state; // unused in Python 3.13+ Py_SetRecursionLimit(new_limit); } // Python 3.13 GIL Tracking // ======================== // In Python 3.13, we use our own thread-local tracking for GIL ownership. // This mirrors the approach used in Python 3.14 GIL-enabled internals. // // Python-Created Threads: // Python-created threads (e.g., threading.Thread) that call back into Qore will not // have our tracking initialized. To handle this, we track whether our GIL tracking // has ever been initialized for this thread (_qore_tss_initialized). // Thread-local state to track current thread state for GIL ownership // Using inline thread_local ensures a single instance shared across all compilation units (C++17) inline thread_local PyThreadState* _qore_tss_tstate = nullptr; // Track whether we've ever initialized our GIL tracking for this thread. // This distinguishes between: // - Qore threads that released the GIL: _qore_tss_initialized=true, _qore_tss_tstate=nullptr // - Python-created threads with GIL: _qore_tss_initialized=false, _qore_tss_tstate=nullptr inline thread_local bool _qore_tss_initialized = false; // Track whether the current thread holds the GIL (independent of tstate tracking) // This is needed because QorePythonReleaseGilHelper uses this to track GIL state inline thread_local bool _qore_gil_held = false; // Get the current thread state from our thread-local tracking DLLLOCAL static inline PyThreadState* _qore_PyRuntimeGILState_GetThreadState() { return _qore_tss_tstate; } // Set this thread's state in thread-local storage // NOTE: Only set _qore_tss_initialized when actually acquiring the GIL (state != nullptr). // When releasing the GIL (state = nullptr), we keep initialized=true to indicate // this is a Qore-managed thread that released the GIL (vs a Python-created thread). // NOTE: This function does NOT update _qore_gil_held - that's only done by // _qore_acquire_thread_state and _qore_release_thread_state which actually change GIL ownership. DLLLOCAL static inline void _qore_PyGILState_SetThisThreadState(PyThreadState* state) { _qore_tss_tstate = state; if (state != nullptr) { _qore_tss_initialized = true; } } // GIL status check - use _qore_gil_held which is set by _qore_acquire_thread_state() // and cleared by _qore_release_thread_state(). This is more accurate than checking // _qore_tss_tstate since the thread state can be set for tracking purposes before // the GIL is actually acquired. // IMPORTANT: PyGILState_Check() is unreliable in Python 3.13 after GIL transitions // with sub-interpreters, so we must use our own tracking. DLLLOCAL static inline bool _qore_PyCeval_GetGilLockedStatus() { return _qore_gil_held; } // Check if this might be a Python-created thread that already has the GIL. // This handles the case where a Python threading.Thread calls back into Qore. // Returns the thread state if this is a Python-created thread with the GIL, nullptr otherwise. DLLLOCAL static inline PyThreadState* _qore_check_python_created_thread_gil() { // If our tracking is already initialized, this is a Qore-managed thread if (_qore_tss_initialized) { return nullptr; } // Check if Python thinks this thread has the GIL // For Python-created threads, the TSS will be set correctly by Python PyThreadState* tss_state = PyGILState_GetThisThreadState(); if (tss_state != nullptr && PyGILState_Check()) { // This is a Python-created thread that has the GIL // Initialize our tracking with Python's state _qore_tss_tstate = tss_state; _qore_tss_initialized = true; _qore_gil_held = true; // Track that we hold the GIL return tss_state; } return nullptr; } // Get the thread state that holds the GIL // In Python 3.13, PyGILState_Check() and TSS can be inconsistent during GIL transitions // with sub-interpreters. Use our own tracking for reliability. DLLLOCAL static inline PyThreadState* _qore_PyCeval_GetThreadState() { return _qore_tss_tstate; } // Swap thread state for ceval purposes - use our thread-local tracking DLLLOCAL static inline PyThreadState* _qore_PyCeval_SwapThreadState(PyThreadState* new_state) { PyThreadState* old = _qore_tss_tstate; _qore_tss_tstate = new_state; return old; } #define _QORE_PYTHON_REENABLE_GIL_CHECK /* no-op in Python 3.13 - check is always enabled */ // In GIL-enabled mode, use standard PyThreadState_Swap #define _QORE_PYTHREAD_STATE_SWAP(new_state) PyThreadState_Swap(new_state) // gilstate_counter access macros - these are available in GIL-enabled Python #define _QORE_GILSTATE_COUNTER_INC(tstate) (++(tstate)->gilstate_counter) #define _QORE_GILSTATE_COUNTER_DEC(tstate) (--(tstate)->gilstate_counter) #define _QORE_GILSTATE_COUNTER_GET(tstate) ((tstate)->gilstate_counter) #define _QORE_GILSTATE_COUNTER_ASSERT_ONE(tstate) assert((tstate)->gilstate_counter == 1) // _PyGILState_GetInterpreterStateUnsafe was an internal function // Use PyInterpreterState_Main() as a safe replacement inline PyInterpreterState* _PyGILState_GetInterpreterStateUnsafe() { return PyInterpreterState_Main(); } // Thread state management functions for GIL-enabled Python DLLLOCAL static inline bool _qore_has_thread_state_attached() { return PyGILState_Check(); } DLLLOCAL static inline void _qore_acquire_thread_state(PyThreadState* tstate) { PyEval_AcquireThread(tstate); // Set thread-local tracking AFTER acquiring the GIL to ensure consistency. // PyEval_AcquireThread() either succeeds or aborts (fatal error), so if we // reach this point, we definitely hold the GIL. _qore_tss_tstate = tstate; _qore_tss_initialized = true; _qore_gil_held = true; } DLLLOCAL static inline void _qore_release_thread_state(PyThreadState* tstate) { PyEval_ReleaseThread(tstate); _qore_tss_tstate = nullptr; _qore_gil_held = false; } #endif