Skip to content

Commit df85859

Browse files
committed
Add _PyThreadState_UncheckedGet()
Issue #26154: Add a new private _PyThreadState_UncheckedGet() function which gets the current thread state, but don't call Py_FatalError() if it is NULL. Python 3.5.1 removed the _PyThreadState_Current symbol from the Python C API to no more expose complex and private atomic types. Atomic types depends on the compiler or can even depend on compiler options. The new function _PyThreadState_UncheckedGet() allows to get the variable value without having to care of the exact implementation of atomic types. Changes: * Replace direct usage of the _PyThreadState_Current variable with a call to _PyThreadState_UncheckedGet(). * In pystate.c, replace direct usage of the _PyThreadState_Current variable with the PyThreadState_GET() macro for readability. * Document also PyThreadState_Get() in pystate.h
1 parent 1fe3547 commit df85859

File tree

7 files changed

+42
-26
lines changed

7 files changed

+42
-26
lines changed

Include/pystate.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,17 @@ PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void);
168168
PyAPI_FUNC(void) _PyGILState_Reinit(void);
169169
#endif
170170

171+
/* Return the current thread state. The global interpreter lock must be held.
172+
* When the current thread state is NULL, this issues a fatal error (so that
173+
* the caller needn't check for NULL). */
171174
PyAPI_FUNC(PyThreadState *) PyThreadState_Get(void);
175+
176+
#ifdef WITH_THREAD
177+
/* Similar to PyThreadState_Get(), but don't issue a fatal error
178+
* if it is NULL. */
179+
PyAPI_FUNC(PyThreadState *) _PyThreadState_UncheckedGet(void);
180+
#endif
181+
172182
PyAPI_FUNC(PyThreadState *) PyThreadState_Swap(PyThreadState *);
173183
PyAPI_FUNC(PyObject *) PyThreadState_GetDict(void);
174184
PyAPI_FUNC(int) PyThreadState_SetAsyncExc(long, PyObject *);

Misc/NEWS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ Release date: tba
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #26154: Add a new private _PyThreadState_UncheckedGet() function to get
14+
the current Python thread state, but don't issue a fatal error if it is NULL.
15+
This new function must be used instead of accessing directly the
16+
_PyThreadState_Current variable. The variable is no more exposed since
17+
Python 3.5.1 to hide the exact implementation of atomic C types, to avoid
18+
compiler issues.
19+
1320
- Issue #25731: Fix set and deleting __new__ on a class.
1421

1522
- Issue #22995: [UPDATE] Comment out the one of the pickleability tests in

Modules/faulthandler.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ faulthandler_thread(void *unused)
490490
assert(st == PY_LOCK_FAILURE);
491491

492492
/* get the thread holding the GIL, NULL if no thread hold the GIL */
493-
current = (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current);
493+
current = _PyThreadState_UncheckedGet();
494494

495495
_Py_write_noraise(thread.fd, thread.header, (int)thread.header_len);
496496

Objects/dictobject.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,8 +1064,7 @@ PyDict_GetItem(PyObject *op, PyObject *key)
10641064
Let's just hope that no exception occurs then... This must be
10651065
_PyThreadState_Current and not PyThreadState_GET() because in debug
10661066
mode, the latter complains if tstate is NULL. */
1067-
tstate = (PyThreadState*)_Py_atomic_load_relaxed(
1068-
&_PyThreadState_Current);
1067+
tstate = _PyThreadState_UncheckedGet();
10691068
if (tstate != NULL && tstate->curexc_type != NULL) {
10701069
/* preserve the existing exception */
10711070
PyObject *err_type, *err_value, *err_tb;
@@ -1102,8 +1101,7 @@ _PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
11021101
Let's just hope that no exception occurs then... This must be
11031102
_PyThreadState_Current and not PyThreadState_GET() because in debug
11041103
mode, the latter complains if tstate is NULL. */
1105-
tstate = (PyThreadState*)_Py_atomic_load_relaxed(
1106-
&_PyThreadState_Current);
1104+
tstate = _PyThreadState_UncheckedGet();
11071105
if (tstate != NULL && tstate->curexc_type != NULL) {
11081106
/* preserve the existing exception */
11091107
PyObject *err_type, *err_value, *err_tb;

Python/errors.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,7 @@ PyErr_SetString(PyObject *exception, const char *string)
152152
PyObject *
153153
PyErr_Occurred(void)
154154
{
155-
/* If there is no thread state, PyThreadState_GET calls
156-
Py_FatalError, which calls PyErr_Occurred. To avoid the
157-
resulting infinite loop, we inline PyThreadState_GET here and
158-
treat no thread as no error. */
159-
PyThreadState *tstate =
160-
((PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current));
161-
155+
PyThreadState *tstate = _PyThreadState_UncheckedGet();
162156
return tstate == NULL ? NULL : tstate->curexc_type;
163157
}
164158

Python/pystate.c

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33

44
#include "Python.h"
55

6+
#ifndef Py_BUILD_CORE
7+
/* ensure that PyThreadState_GET() is a macro, not an alias to
8+
* PyThreadState_Get() */
9+
# error "pystate.c must be compiled with Py_BUILD_CORE defined"
10+
#endif
11+
612
/* --------------------------------------------------------------------------
713
CAUTION
814
@@ -423,7 +429,7 @@ tstate_delete_common(PyThreadState *tstate)
423429
void
424430
PyThreadState_Delete(PyThreadState *tstate)
425431
{
426-
if (tstate == (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current))
432+
if (tstate == PyThreadState_GET())
427433
Py_FatalError("PyThreadState_Delete: tstate is still current");
428434
#ifdef WITH_THREAD
429435
if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate)
@@ -437,8 +443,7 @@ PyThreadState_Delete(PyThreadState *tstate)
437443
void
438444
PyThreadState_DeleteCurrent()
439445
{
440-
PyThreadState *tstate = (PyThreadState*)_Py_atomic_load_relaxed(
441-
&_PyThreadState_Current);
446+
PyThreadState *tstate = PyThreadState_GET();
442447
if (tstate == NULL)
443448
Py_FatalError(
444449
"PyThreadState_DeleteCurrent: no current tstate");
@@ -488,11 +493,17 @@ _PyThreadState_DeleteExcept(PyThreadState *tstate)
488493
}
489494

490495

496+
PyThreadState *
497+
_PyThreadState_UncheckedGet(void)
498+
{
499+
return PyThreadState_GET();
500+
}
501+
502+
491503
PyThreadState *
492504
PyThreadState_Get(void)
493505
{
494-
PyThreadState *tstate = (PyThreadState*)_Py_atomic_load_relaxed(
495-
&_PyThreadState_Current);
506+
PyThreadState *tstate = PyThreadState_GET();
496507
if (tstate == NULL)
497508
Py_FatalError("PyThreadState_Get: no current thread");
498509

@@ -503,8 +514,7 @@ PyThreadState_Get(void)
503514
PyThreadState *
504515
PyThreadState_Swap(PyThreadState *newts)
505516
{
506-
PyThreadState *oldts = (PyThreadState*)_Py_atomic_load_relaxed(
507-
&_PyThreadState_Current);
517+
PyThreadState *oldts = PyThreadState_GET();
508518

509519
_Py_atomic_store_relaxed(&_PyThreadState_Current, newts);
510520
/* It should not be possible for more than one thread state
@@ -535,8 +545,7 @@ PyThreadState_Swap(PyThreadState *newts)
535545
PyObject *
536546
PyThreadState_GetDict(void)
537547
{
538-
PyThreadState *tstate = (PyThreadState*)_Py_atomic_load_relaxed(
539-
&_PyThreadState_Current);
548+
PyThreadState *tstate = PyThreadState_GET();
540549
if (tstate == NULL)
541550
return NULL;
542551

@@ -682,7 +691,7 @@ PyThreadState_IsCurrent(PyThreadState *tstate)
682691
{
683692
/* Must be the tstate for this thread */
684693
assert(PyGILState_GetThisThreadState()==tstate);
685-
return tstate == (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current);
694+
return tstate == PyThreadState_GET();
686695
}
687696

688697
/* Internal initialization/finalization functions called by
@@ -774,9 +783,7 @@ PyGILState_GetThisThreadState(void)
774783
int
775784
PyGILState_Check(void)
776785
{
777-
/* can't use PyThreadState_Get() since it will assert that it has the GIL */
778-
PyThreadState *tstate = (PyThreadState*)_Py_atomic_load_relaxed(
779-
&_PyThreadState_Current);
786+
PyThreadState *tstate = PyThreadState_GET();
780787
return tstate && (tstate == PyGILState_GetThisThreadState());
781788
}
782789

Python/sysmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1397,7 +1397,7 @@ PySys_AddXOption(const wchar_t *s)
13971397
Py_XDECREF(name);
13981398
Py_XDECREF(value);
13991399
/* No return value, therefore clear error state if possible */
1400-
if (_Py_atomic_load_relaxed(&_PyThreadState_Current))
1400+
if (_PyThreadState_UncheckedGet())
14011401
PyErr_Clear();
14021402
}
14031403

0 commit comments

Comments
 (0)