Skip to content

Commit 50b4857

Browse files
authored
bpo-35081: Add _PyThreadState_GET() internal macro (GH-10266)
If Py_BUILD_CORE is defined, the PyThreadState_GET() macro access _PyRuntime which comes from the internal pycore_state.h header. Public headers must not require internal headers. Move PyThreadState_GET() and _PyInterpreterState_GET_UNSAFE() from Include/pystate.h to Include/internal/pycore_state.h, and rename PyThreadState_GET() to _PyThreadState_GET() there. The PyThreadState_GET() macro of pystate.h is now redefined when pycore_state.h is included, to use the fast _PyThreadState_GET(). Changes: * Add _PyThreadState_GET() macro * Replace "PyThreadState_GET()->interp" with _PyInterpreterState_GET_UNSAFE() * Replace PyThreadState_GET() with _PyThreadState_GET() in internal C files (compiled with Py_BUILD_CORE defined), but keep PyThreadState_GET() in the public header files. * _testcapimodule.c: replace PyThreadState_GET() with PyThreadState_Get(); the module is not compiled with Py_BUILD_CORE defined. * pycore_state.h now requires Py_BUILD_CORE to be defined.
1 parent 27e2d1f commit 50b4857

20 files changed

+144
-104
lines changed

Include/internal/pycore_state.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
extern "C" {
55
#endif
66

7+
#ifndef Py_BUILD_CORE
8+
# error "Py_BUILD_CORE must be defined to include this header"
9+
#endif
10+
711
#include "pystate.h"
812
#include "pythread.h"
913

@@ -214,6 +218,36 @@ PyAPI_FUNC(_PyInitError) _PyRuntime_Initialize(void);
214218
(_PyRuntime.finalizing == tstate)
215219

216220

221+
/* Variable and macro for in-line access to current thread
222+
and interpreter state */
223+
224+
/* Get the current Python thread state.
225+
226+
Efficient macro reading directly the 'gilstate.tstate_current' atomic
227+
variable. The macro is unsafe: it does not check for error and it can
228+
return NULL.
229+
230+
The caller must hold the GIL.
231+
232+
See also PyThreadState_Get() and PyThreadState_GET(). */
233+
#define _PyThreadState_GET() \
234+
((PyThreadState*)_Py_atomic_load_relaxed(&_PyRuntime.gilstate.tstate_current))
235+
236+
/* Redefine PyThreadState_GET() as an alias to _PyThreadState_GET() */
237+
#undef PyThreadState_GET
238+
#define PyThreadState_GET() _PyThreadState_GET()
239+
240+
/* Get the current interpreter state.
241+
242+
The macro is unsafe: it does not check for error and it can return NULL.
243+
244+
The caller must hold the GIL.
245+
246+
See also _PyInterpreterState_Get()
247+
and _PyGILState_GetInterpreterStateUnsafe(). */
248+
#define _PyInterpreterState_GET_UNSAFE() (_PyThreadState_GET()->interp)
249+
250+
217251
/* Other */
218252

219253
PyAPI_FUNC(_PyInitError) _PyInterpreterState_Enable(_PyRuntimeState *);

Include/pystate.h

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -245,15 +245,17 @@ typedef struct _ts {
245245
PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_New(void);
246246
PyAPI_FUNC(void) PyInterpreterState_Clear(PyInterpreterState *);
247247
PyAPI_FUNC(void) PyInterpreterState_Delete(PyInterpreterState *);
248+
248249
#if !defined(Py_LIMITED_API)
250+
/* Get the current interpreter state.
251+
252+
Issue a fatal error if there no current Python thread state or no current
253+
interpreter. It cannot return NULL.
254+
255+
The caller must hold the GIL.*/
249256
PyAPI_FUNC(PyInterpreterState *) _PyInterpreterState_Get(void);
250257
#endif
251-
#ifdef Py_BUILD_CORE
252-
/* Macro which should only be used for performance critical code.
253-
Need "#include "pycore_state.h". See also _PyInterpreterState_Get()
254-
and _PyGILState_GetInterpreterStateUnsafe(). */
255-
# define _PyInterpreterState_GET_UNSAFE() (PyThreadState_GET()->interp)
256-
#endif
258+
257259
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000
258260
/* New in 3.7 */
259261
PyAPI_FUNC(int64_t) PyInterpreterState_GetID(PyInterpreterState *);
@@ -286,11 +288,27 @@ PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void);
286288
PyAPI_FUNC(void) _PyGILState_Reinit(void);
287289
#endif /* !Py_LIMITED_API */
288290

289-
/* Return the current thread state. The global interpreter lock must be held.
290-
* When the current thread state is NULL, this issues a fatal error (so that
291-
* the caller needn't check for NULL). */
291+
/* Get the current thread state.
292+
293+
When the current thread state is NULL, this issues a fatal error (so that
294+
the caller needn't check for NULL).
295+
296+
The caller must hold the GIL.
297+
298+
See also PyThreadState_GET() and _PyThreadState_GET(). */
292299
PyAPI_FUNC(PyThreadState *) PyThreadState_Get(void);
293300

301+
/* Get the current Python thread state.
302+
303+
Macro using PyThreadState_Get() or _PyThreadState_GET() depending if
304+
pycore_state.h is included or not (this header redefines the macro).
305+
306+
If PyThreadState_Get() is used, issue a fatal error if the current thread
307+
state is NULL.
308+
309+
See also PyThreadState_Get() and _PyThreadState_GET(). */
310+
#define PyThreadState_GET() PyThreadState_Get()
311+
294312
#ifndef Py_LIMITED_API
295313
/* Similar to PyThreadState_Get(), but don't issue a fatal error
296314
* if it is NULL. */
@@ -301,18 +319,6 @@ PyAPI_FUNC(PyThreadState *) PyThreadState_Swap(PyThreadState *);
301319
PyAPI_FUNC(PyObject *) PyThreadState_GetDict(void);
302320
PyAPI_FUNC(int) PyThreadState_SetAsyncExc(unsigned long, PyObject *);
303321

304-
305-
/* Variable and macro for in-line access to current thread state */
306-
307-
/* Assuming the current thread holds the GIL, this is the
308-
PyThreadState for the current thread. */
309-
#ifdef Py_BUILD_CORE
310-
# define PyThreadState_GET() \
311-
((PyThreadState*)_Py_atomic_load_relaxed(&_PyRuntime.gilstate.tstate_current))
312-
#else
313-
# define PyThreadState_GET() PyThreadState_Get()
314-
#endif
315-
316322
typedef
317323
enum {PyGILState_LOCKED, PyGILState_UNLOCKED}
318324
PyGILState_STATE;
@@ -366,11 +372,11 @@ PyAPI_FUNC(PyThreadState *) PyGILState_GetThisThreadState(void);
366372
The function returns 1 if _PyGILState_check_enabled is non-zero. */
367373
PyAPI_FUNC(int) PyGILState_Check(void);
368374

369-
/* Unsafe function to get the single PyInterpreterState used by this process'
370-
GILState implementation.
375+
/* Get the single PyInterpreterState used by this process' GILState
376+
implementation.
371377
372-
Return NULL before _PyGILState_Init() is called and after _PyGILState_Fini()
373-
is called.
378+
This function doesn't check for error. Return NULL before _PyGILState_Init()
379+
is called and after _PyGILState_Fini() is called.
374380
375381
See also _PyInterpreterState_Get() and _PyInterpreterState_GET_UNSAFE(). */
376382
PyAPI_FUNC(PyInterpreterState *) _PyGILState_GetInterpreterStateUnsafe(void);

Modules/_testcapimodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4160,7 +4160,7 @@ test_PyTime_AsMicroseconds(PyObject *self, PyObject *args)
41604160
static PyObject*
41614161
get_recursion_depth(PyObject *self, PyObject *args)
41624162
{
4163-
PyThreadState *tstate = PyThreadState_GET();
4163+
PyThreadState *tstate = PyThreadState_Get();
41644164

41654165
/* subtract one to ignore the frame of the get_recursion_depth() call */
41664166
return PyLong_FromLong(tstate->recursion_depth - 1);

Objects/call.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ function_code_fastcall(PyCodeObject *co, PyObject *const *args, Py_ssize_t nargs
258258
PyObject *globals)
259259
{
260260
PyFrameObject *f;
261-
PyThreadState *tstate = PyThreadState_GET();
261+
PyThreadState *tstate = _PyThreadState_GET();
262262
PyObject **fastlocals;
263263
Py_ssize_t i;
264264
PyObject *result;

Objects/dictobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,9 +1314,9 @@ PyDict_GetItem(PyObject *op, PyObject *key)
13141314
/* We can arrive here with a NULL tstate during initialization: try
13151315
running "python -Wi" for an example related to string interning.
13161316
Let's just hope that no exception occurs then... This must be
1317-
PyThreadState_GET() and not PyThreadState_Get() because the latter
1317+
_PyThreadState_GET() and not PyThreadState_Get() because the latter
13181318
abort Python if tstate is NULL. */
1319-
tstate = PyThreadState_GET();
1319+
tstate = _PyThreadState_GET();
13201320
if (tstate != NULL && tstate->curexc_type != NULL) {
13211321
/* preserve the existing exception */
13221322
PyObject *err_type, *err_value, *err_tb;

Objects/genobject.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ gen_dealloc(PyGenObject *gen)
151151
static PyObject *
152152
gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
153153
{
154-
PyThreadState *tstate = PyThreadState_GET();
154+
PyThreadState *tstate = _PyThreadState_GET();
155155
PyFrameObject *f = gen->gi_frame;
156156
PyObject *result;
157157

@@ -1157,7 +1157,7 @@ PyCoro_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
11571157
return NULL;
11581158
}
11591159

1160-
PyThreadState *tstate = PyThreadState_GET();
1160+
PyThreadState *tstate = _PyThreadState_GET();
11611161
int origin_depth = tstate->coroutine_origin_tracking_depth;
11621162

11631163
if (origin_depth == 0) {
@@ -1267,7 +1267,7 @@ async_gen_init_hooks(PyAsyncGenObject *o)
12671267

12681268
o->ag_hooks_inited = 1;
12691269

1270-
tstate = PyThreadState_GET();
1270+
tstate = _PyThreadState_GET();
12711271

12721272
finalizer = tstate->async_gen_finalizer;
12731273
if (finalizer) {

Objects/object.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2136,7 +2136,7 @@ _PyTrash_deposit_object(PyObject *op)
21362136
void
21372137
_PyTrash_thread_deposit_object(PyObject *op)
21382138
{
2139-
PyThreadState *tstate = PyThreadState_GET();
2139+
PyThreadState *tstate = _PyThreadState_GET();
21402140
_PyObject_ASSERT(op, PyObject_IS_GC(op));
21412141
_PyObject_ASSERT(op, !_PyObject_GC_IS_TRACKED(op));
21422142
_PyObject_ASSERT(op, op->ob_refcnt == 0);
@@ -2174,7 +2174,7 @@ _PyTrash_destroy_chain(void)
21742174
void
21752175
_PyTrash_thread_destroy_chain(void)
21762176
{
2177-
PyThreadState *tstate = PyThreadState_GET();
2177+
PyThreadState *tstate = _PyThreadState_GET();
21782178
/* We need to increase trash_delete_nesting here, otherwise,
21792179
_PyTrash_thread_destroy_chain will be called recursively
21802180
and then possibly crash. An example that may crash without

Objects/odictobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1355,7 +1355,7 @@ static PyGetSetDef odict_getset[] = {
13551355
static void
13561356
odict_dealloc(PyODictObject *self)
13571357
{
1358-
PyThreadState *tstate = PyThreadState_GET();
1358+
PyThreadState *tstate = _PyThreadState_GET();
13591359

13601360
PyObject_GC_UnTrack(self);
13611361
Py_TRASHCAN_SAFE_BEGIN(self)

Objects/typeobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,7 +1115,7 @@ subtype_dealloc(PyObject *self)
11151115
{
11161116
PyTypeObject *type, *base;
11171117
destructor basedealloc;
1118-
PyThreadState *tstate = PyThreadState_GET();
1118+
PyThreadState *tstate = _PyThreadState_GET();
11191119
int has_finalizer;
11201120

11211121
/* Extract the type; we expect it to be a heap type */
@@ -7678,7 +7678,7 @@ super_init(PyObject *self, PyObject *args, PyObject *kwds)
76787678
PyFrameObject *f;
76797679
PyCodeObject *co;
76807680
Py_ssize_t i, n;
7681-
f = PyThreadState_GET()->frame;
7681+
f = _PyThreadState_GET()->frame;
76827682
if (f == NULL) {
76837683
PyErr_SetString(PyExc_RuntimeError,
76847684
"super(): no current frame");

Parser/myreadline.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
301301
char *rv, *res;
302302
size_t len;
303303

304-
if (_PyOS_ReadlineTState == PyThreadState_GET()) {
304+
if (_PyOS_ReadlineTState == _PyThreadState_GET()) {
305305
PyErr_SetString(PyExc_RuntimeError,
306306
"can't re-enter readline");
307307
return NULL;
@@ -316,7 +316,7 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
316316
_PyOS_ReadlineLock = PyThread_allocate_lock();
317317
}
318318

319-
_PyOS_ReadlineTState = PyThreadState_GET();
319+
_PyOS_ReadlineTState = _PyThreadState_GET();
320320
Py_BEGIN_ALLOW_THREADS
321321
PyThread_acquire_lock(_PyOS_ReadlineLock, 1);
322322

0 commit comments

Comments
 (0)