Skip to content

Commit 4f6b32d

Browse files
Finalize PyEval_AcquireLock() and PyEval_AcquireThread() properly
1 parent 62dfd7d commit 4f6b32d

File tree

2 files changed

+47
-12
lines changed

2 files changed

+47
-12
lines changed

Doc/c-api/init.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,7 @@ code, or when embedding the Python interpreter:
863863
check if the interpreter is in process of being finalized before calling
864864
this function to avoid unwanted termination.
865865
866+
866867
.. c:function:: PyThreadState* PyThreadState_Get()
867868
868869
Return the current thread state. The global interpreter lock must be held.
@@ -1080,9 +1081,27 @@ All of the following functions must be called after :c:func:`Py_Initialize`.
10801081
*tstate*, which should not be *NULL*. The lock must have been created earlier.
10811082
If this thread already has the lock, deadlock ensues.
10821083
1084+
.. note::
1085+
Calling this function from a thread when the runtime is finalizing
1086+
will terminate the thread, even if the thread was not created by Python.
1087+
You can use :c:func:`_Py_IsFinalizing` or :func:`sys.is_finalizing` to
1088+
check if the interpreter is in process of being finalized before calling
1089+
this function to avoid unwanted termination.
1090+
1091+
.. versionchanged:: 3.8
1092+
Updated to be consistent with :c:func:`PyEval_RestoreThread`,
1093+
:c:func:`Py_END_ALLOW_THREADS`, and :c:func:`PyGILState_Ensure`,
1094+
and terminate the current thread if called while the interpreter is finalizing.
1095+
10831096
:c:func:`PyEval_RestoreThread` is a higher-level function which is always
10841097
available (even when threads have not been initialized).
10851098
1099+
.. note::
1100+
Calling this function from a thread when the runtime is finalizing
1101+
will terminate the thread, even if the thread was not created by Python.
1102+
You can use :c:func:`_Py_IsFinalizing` or :func:`sys.is_finalizing` to
1103+
check if the interpreter is in process of being finalized before calling
1104+
this function to avoid unwanted termination.
10861105
10871106
.. c:function:: void PyEval_ReleaseThread(PyThreadState *tstate)
10881107
@@ -1106,6 +1125,18 @@ All of the following functions must be called after :c:func:`Py_Initialize`.
11061125
:c:func:`PyEval_RestoreThread` or :c:func:`PyEval_AcquireThread`
11071126
instead.
11081127
1128+
.. note::
1129+
Calling this function from a thread when the runtime is finalizing
1130+
will terminate the thread, even if the thread was not created by Python.
1131+
You can use :c:func:`_Py_IsFinalizing` or :func:`sys.is_finalizing` to
1132+
check if the interpreter is in process of being finalized before calling
1133+
this function to avoid unwanted termination.
1134+
1135+
.. versionchanged:: 3.8
1136+
Updated to be consistent with :c:func:`PyEval_RestoreThread`,
1137+
:c:func:`Py_END_ALLOW_THREADS`, and :c:func:`PyGILState_Ensure`,
1138+
and terminate the current thread if called while the interpreter is finalizing.
1139+
11091140
11101141
.. c:function:: void PyEval_ReleaseLock()
11111142

Python/ceval.c

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ static PyObject * special_lookup(PyObject *, _Py_Identifier *);
7676
static int check_args_iterable(PyObject *func, PyObject *vararg);
7777
static void format_kwargs_error(PyObject *func, PyObject *kwargs);
7878
static void format_awaitable_error(PyTypeObject *, int);
79+
static inline void exit_thread_if_finalizing(PyThreadState *);
7980

8081
#define NAME_ERROR_MSG \
8182
"name '%.200s' is not defined"
@@ -210,6 +211,7 @@ PyEval_AcquireLock(void)
210211
if (tstate == NULL)
211212
Py_FatalError("PyEval_AcquireLock: current thread state is NULL");
212213
take_gil(tstate);
214+
exit_thread_if_finalizing(tstate);
213215
}
214216

215217
void
@@ -230,6 +232,7 @@ PyEval_AcquireThread(PyThreadState *tstate)
230232
/* Check someone has called PyEval_InitThreads() to create the lock */
231233
assert(gil_created());
232234
take_gil(tstate);
235+
exit_thread_if_finalizing(tstate);
233236
if (PyThreadState_Swap(tstate) != NULL)
234237
Py_FatalError(
235238
"PyEval_AcquireThread: non-NULL old thread state");
@@ -298,12 +301,7 @@ PyEval_RestoreThread(PyThreadState *tstate)
298301

299302
int err = errno;
300303
take_gil(tstate);
301-
/* _Py_Finalizing is protected by the GIL */
302-
if (_Py_IsFinalizing() && !_Py_CURRENTLY_FINALIZING(tstate)) {
303-
drop_gil(tstate);
304-
PyThread_exit_thread();
305-
Py_UNREACHABLE();
306-
}
304+
exit_thread_if_finalizing(tstate);
307305
errno = err;
308306

309307
PyThreadState_Swap(tstate);
@@ -1083,12 +1081,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
10831081
take_gil(tstate);
10841082

10851083
/* Check if we should make a quick exit. */
1086-
if (_Py_IsFinalizing() &&
1087-
!_Py_CURRENTLY_FINALIZING(tstate))
1088-
{
1089-
drop_gil(tstate);
1090-
PyThread_exit_thread();
1091-
}
1084+
exit_thread_if_finalizing(tstate);
10921085

10931086
if (PyThreadState_Swap(tstate) != NULL)
10941087
Py_FatalError("ceval: orphan tstate");
@@ -5199,6 +5192,17 @@ format_awaitable_error(PyTypeObject *type, int prevopcode)
51995192
}
52005193
}
52015194

5195+
static inline void
5196+
exit_thread_if_finalizing(PyThreadState *tstate)
5197+
{
5198+
/* _Py_Finalizing is protected by the GIL */
5199+
if (_Py_IsFinalizing() && !_Py_CURRENTLY_FINALIZING(tstate)) {
5200+
drop_gil(tstate);
5201+
PyThread_exit_thread();
5202+
Py_UNREACHABLE();
5203+
}
5204+
}
5205+
52025206
static PyObject *
52035207
unicode_concatenate(PyObject *v, PyObject *w,
52045208
PyFrameObject *f, const _Py_CODEUNIT *next_instr)

0 commit comments

Comments
 (0)