Skip to content

Commit 23ef89d

Browse files
authored
bpo-39984: _PyThreadState_DeleteCurrent() takes tstate (GH-19051)
* _PyThreadState_DeleteCurrent() now takes tstate rather than runtime. * Add ensure_tstate_not_null() helper to pystate.c. * Add _PyEval_ReleaseLock() function. * _PyThreadState_DeleteCurrent() now calls _PyEval_ReleaseLock(tstate) and frees PyThreadState memory after this call, not before. * PyGILState_Release(): rename "tcur" variable to "tstate".
1 parent d7fabc1 commit 23ef89d

File tree

6 files changed

+47
-32
lines changed

6 files changed

+47
-32
lines changed

Include/internal/pycore_ceval.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ extern PyObject *_PyEval_EvalCode(
5757
extern int _PyEval_ThreadsInitialized(_PyRuntimeState *runtime);
5858
extern PyStatus _PyEval_InitThreads(PyThreadState *tstate);
5959

60+
extern void _PyEval_ReleaseLock(PyThreadState *tstate);
61+
6062

6163
/* --- _Py_EnterRecursiveCall() ----------------------------------------- */
6264

Include/internal/pycore_pylifecycle.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ PyAPI_FUNC(void) _PyErr_Print(PyThreadState *tstate);
104104
PyAPI_FUNC(void) _PyErr_Display(PyObject *file, PyObject *exception,
105105
PyObject *value, PyObject *tb);
106106

107-
PyAPI_FUNC(void) _PyThreadState_DeleteCurrent(struct pyruntimestate *runtime);
107+
PyAPI_FUNC(void) _PyThreadState_DeleteCurrent(PyThreadState *tstate);
108108

109109
#ifdef __cplusplus
110110
}

Lib/test/test_capi.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ def test_no_FatalError_infinite_loop(self):
6262
# This used to cause an infinite loop.
6363
self.assertTrue(err.rstrip().startswith(
6464
b'Fatal Python error: '
65-
b'PyThreadState_Get: no current thread'))
65+
b'PyThreadState_Get: '
66+
b'current thread state is NULL (released GIL?)'))
6667

6768
def test_memoryview_from_NULL_pointer(self):
6869
self.assertRaises(ValueError, _testcapi.make_memoryview_from_NULL_pointer)

Modules/_threadmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ t_bootstrap(void *boot_raw)
10281028
PyMem_DEL(boot_raw);
10291029
tstate->interp->num_threads--;
10301030
PyThreadState_Clear(tstate);
1031-
_PyThreadState_DeleteCurrent(runtime);
1031+
_PyThreadState_DeleteCurrent(tstate);
10321032
PyThread_exit_thread();
10331033
}
10341034

Python/ceval.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ static void
193193
ensure_tstate_not_null(const char *func, PyThreadState *tstate)
194194
{
195195
if (tstate == NULL) {
196-
_Py_FatalErrorFunc(func, "current thread state is NULL");
196+
_Py_FatalErrorFunc(func,
197+
"current thread state is NULL (released GIL?)");
197198
}
198199
}
199200

@@ -313,6 +314,13 @@ PyEval_ReleaseLock(void)
313314
drop_gil(&runtime->ceval, tstate);
314315
}
315316

317+
void
318+
_PyEval_ReleaseLock(PyThreadState *tstate)
319+
{
320+
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
321+
drop_gil(ceval, tstate);
322+
}
323+
316324
void
317325
PyEval_AcquireThread(PyThreadState *tstate)
318326
{

Python/pystate.c

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ extern "C" {
3737
_Py_atomic_store_relaxed(&(gilstate)->tstate_current, \
3838
(uintptr_t)(value))
3939

40+
static void
41+
ensure_tstate_not_null(const char *func, PyThreadState *tstate)
42+
{
43+
if (tstate == NULL) {
44+
_Py_FatalErrorFunc(func,
45+
"current thread state is NULL (released GIL?)");
46+
}
47+
}
48+
49+
4050
/* Forward declarations */
4151
static PyThreadState *_PyGILState_GetThisThreadState(struct _gilstate_runtime_state *gilstate);
4252
static void _PyThreadState_Delete(PyThreadState *tstate, int check_current);
@@ -400,9 +410,7 @@ PyInterpreterState *
400410
PyInterpreterState_Get(void)
401411
{
402412
PyThreadState *tstate = _PyThreadState_GET();
403-
if (tstate == NULL) {
404-
Py_FatalError("no current thread state");
405-
}
413+
ensure_tstate_not_null(__func__, tstate);
406414
PyInterpreterState *interp = tstate->interp;
407415
if (interp == NULL) {
408416
Py_FatalError("no current interpreter");
@@ -819,9 +827,7 @@ tstate_delete_common(PyThreadState *tstate,
819827
struct _gilstate_runtime_state *gilstate)
820828
{
821829
_PyRuntimeState *runtime = tstate->interp->runtime;
822-
if (tstate == NULL) {
823-
Py_FatalError("NULL tstate");
824-
}
830+
ensure_tstate_not_null(__func__, tstate);
825831
PyInterpreterState *interp = tstate->interp;
826832
if (interp == NULL) {
827833
Py_FatalError("NULL interp");
@@ -835,8 +841,6 @@ tstate_delete_common(PyThreadState *tstate,
835841
tstate->next->prev = tstate->prev;
836842
HEAD_UNLOCK(runtime);
837843

838-
PyMem_RawFree(tstate);
839-
840844
if (gilstate->autoInterpreterState &&
841845
PyThread_tss_get(&gilstate->autoTSSkey) == tstate)
842846
{
@@ -855,6 +859,7 @@ _PyThreadState_Delete(PyThreadState *tstate, int check_current)
855859
}
856860
}
857861
tstate_delete_common(tstate, gilstate);
862+
PyMem_RawFree(tstate);
858863
}
859864

860865

@@ -866,22 +871,22 @@ PyThreadState_Delete(PyThreadState *tstate)
866871

867872

868873
void
869-
_PyThreadState_DeleteCurrent(_PyRuntimeState *runtime)
874+
_PyThreadState_DeleteCurrent(PyThreadState *tstate)
870875
{
871-
struct _gilstate_runtime_state *gilstate = &runtime->gilstate;
872-
PyThreadState *tstate = _PyRuntimeGILState_GetThreadState(gilstate);
873-
if (tstate == NULL) {
874-
Py_FatalError("no current tstate");
875-
}
876+
ensure_tstate_not_null(__func__, tstate);
877+
struct _gilstate_runtime_state *gilstate = &tstate->interp->runtime->gilstate;
876878
tstate_delete_common(tstate, gilstate);
877879
_PyRuntimeGILState_SetThreadState(gilstate, NULL);
878-
PyEval_ReleaseLock();
880+
_PyEval_ReleaseLock(tstate);
881+
PyMem_RawFree(tstate);
879882
}
880883

881884
void
882885
PyThreadState_DeleteCurrent(void)
883886
{
884-
_PyThreadState_DeleteCurrent(&_PyRuntime);
887+
struct _gilstate_runtime_state *gilstate = &_PyRuntime.gilstate;
888+
PyThreadState *tstate = _PyRuntimeGILState_GetThreadState(gilstate);
889+
_PyThreadState_DeleteCurrent(tstate);
885890
}
886891

887892

@@ -938,9 +943,7 @@ PyThreadState *
938943
PyThreadState_Get(void)
939944
{
940945
PyThreadState *tstate = _PyThreadState_GET();
941-
if (tstate == NULL) {
942-
Py_FatalError("no current thread");
943-
}
946+
ensure_tstate_not_null(__func__, tstate);
944947
return tstate;
945948
}
946949

@@ -1342,8 +1345,8 @@ void
13421345
PyGILState_Release(PyGILState_STATE oldstate)
13431346
{
13441347
_PyRuntimeState *runtime = &_PyRuntime;
1345-
PyThreadState *tcur = PyThread_tss_get(&runtime->gilstate.autoTSSkey);
1346-
if (tcur == NULL) {
1348+
PyThreadState *tstate = PyThread_tss_get(&runtime->gilstate.autoTSSkey);
1349+
if (tstate == NULL) {
13471350
Py_FatalError("auto-releasing thread-state, "
13481351
"but no thread-state for this thread");
13491352
}
@@ -1353,26 +1356,27 @@ PyGILState_Release(PyGILState_STATE oldstate)
13531356
but while this is very new (April 2003), the extra check
13541357
by release-only users can't hurt.
13551358
*/
1356-
if (!PyThreadState_IsCurrent(tcur)) {
1359+
if (!PyThreadState_IsCurrent(tstate)) {
13571360
Py_FatalError("This thread state must be current when releasing");
13581361
}
1359-
assert(PyThreadState_IsCurrent(tcur));
1360-
--tcur->gilstate_counter;
1361-
assert(tcur->gilstate_counter >= 0); /* illegal counter value */
1362+
assert(PyThreadState_IsCurrent(tstate));
1363+
--tstate->gilstate_counter;
1364+
assert(tstate->gilstate_counter >= 0); /* illegal counter value */
13621365

13631366
/* If we're going to destroy this thread-state, we must
13641367
* clear it while the GIL is held, as destructors may run.
13651368
*/
1366-
if (tcur->gilstate_counter == 0) {
1369+
if (tstate->gilstate_counter == 0) {
13671370
/* can't have been locked when we created it */
13681371
assert(oldstate == PyGILState_UNLOCKED);
1369-
PyThreadState_Clear(tcur);
1372+
PyThreadState_Clear(tstate);
13701373
/* Delete the thread-state. Note this releases the GIL too!
13711374
* It's vital that the GIL be held here, to avoid shutdown
13721375
* races; see bugs 225673 and 1061968 (that nasty bug has a
13731376
* habit of coming back).
13741377
*/
1375-
_PyThreadState_DeleteCurrent(runtime);
1378+
assert(_PyRuntimeGILState_GetThreadState(&runtime->gilstate) == tstate);
1379+
_PyThreadState_DeleteCurrent(tstate);
13761380
}
13771381
/* Release the lock if necessary */
13781382
else if (oldstate == PyGILState_UNLOCKED)

0 commit comments

Comments
 (0)