-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpython313_internals.h
More file actions
166 lines (139 loc) · 7.07 KB
/
Copy pathpython313_internals.h
File metadata and controls
166 lines (139 loc) · 7.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/* -*- 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 <fileobject.h>
// 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