summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
Diffstat (limited to 'Python')
-rw-r--r--Python/ceval_gil.c26
-rw-r--r--Python/pylifecycle.c2
-rw-r--r--Python/thread_nt.h8
-rw-r--r--Python/thread_pthread.h15
4 files changed, 39 insertions, 12 deletions
diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c
index 1d9381d..4c9f59f 100644
--- a/Python/ceval_gil.c
+++ b/Python/ceval_gil.c
@@ -7,6 +7,7 @@
#include "pycore_pylifecycle.h" // _PyErr_Print()
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
#include "pycore_pystats.h" // _Py_PrintSpecializationStats()
+#include "pycore_pythread.h" // PyThread_hang_thread()
/*
Notes about the implementation:
@@ -277,10 +278,9 @@ drop_gil(PyInterpreterState *interp, PyThreadState *tstate, int final_release)
/* Take the GIL.
The function saves errno at entry and restores its value at exit.
+ It may hang rather than return if the interpreter has been finalized.
- tstate must be non-NULL.
-
- Returns 1 if the GIL was acquired, or 0 if not. */
+ tstate must be non-NULL. */
static void
take_gil(PyThreadState *tstate)
{
@@ -293,12 +293,18 @@ take_gil(PyThreadState *tstate)
if (_PyThreadState_MustExit(tstate)) {
/* bpo-39877: If Py_Finalize() has been called and tstate is not the
- thread which called Py_Finalize(), exit immediately the thread.
+ thread which called Py_Finalize(), this thread cannot continue.
This code path can be reached by a daemon thread after Py_Finalize()
completes. In this case, tstate is a dangling pointer: points to
- PyThreadState freed memory. */
- PyThread_exit_thread();
+ PyThreadState freed memory.
+
+ This used to call a *thread_exit API, but that was not safe as it
+ lacks stack unwinding and local variable destruction important to
+ C++. gh-87135: The best that can be done is to hang the thread as
+ the public APIs calling this have no error reporting mechanism (!).
+ */
+ PyThread_hang_thread();
}
assert(_PyThreadState_CheckConsistency(tstate));
@@ -342,7 +348,9 @@ take_gil(PyThreadState *tstate)
if (drop_requested) {
_Py_unset_eval_breaker_bit(holder_tstate, _PY_GIL_DROP_REQUEST_BIT);
}
- PyThread_exit_thread();
+ // gh-87135: hang the thread as *thread_exit() is not a safe
+ // API. It lacks stack unwind and local variable destruction.
+ PyThread_hang_thread();
}
assert(_PyThreadState_CheckConsistency(tstate));
@@ -383,7 +391,7 @@ take_gil(PyThreadState *tstate)
if (_PyThreadState_MustExit(tstate)) {
/* bpo-36475: If Py_Finalize() has been called and tstate is not
- the thread which called Py_Finalize(), exit immediately the
+ the thread which called Py_Finalize(), gh-87135: hang the
thread.
This code path can be reached by a daemon thread which was waiting
@@ -393,7 +401,7 @@ take_gil(PyThreadState *tstate)
/* tstate could be a dangling pointer, so don't pass it to
drop_gil(). */
drop_gil(interp, NULL, 1);
- PyThread_exit_thread();
+ PyThread_hang_thread();
}
assert(_PyThreadState_CheckConsistency(tstate));
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index d9e89ed..ebeee4f 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -2020,7 +2020,7 @@ _Py_Finalize(_PyRuntimeState *runtime)
/* Ensure that remaining threads are detached */
_PyEval_StopTheWorldAll(runtime);
- /* Remaining daemon threads will automatically exit
+ /* Remaining daemon threads will be trapped in PyThread_hang_thread
when they attempt to take the GIL (ex: PyEval_RestoreThread()). */
_PyInterpreterState_SetFinalizing(tstate->interp, tstate);
_PyRuntimeState_SetFinalizing(runtime, tstate);
diff --git a/Python/thread_nt.h b/Python/thread_nt.h
index 4256581..3a01a7f 100644
--- a/Python/thread_nt.h
+++ b/Python/thread_nt.h
@@ -291,6 +291,14 @@ PyThread_exit_thread(void)
_endthreadex(0);
}
+void _Py_NO_RETURN
+PyThread_hang_thread(void)
+{
+ while (1) {
+ SleepEx(INFINITE, TRUE);
+ }
+}
+
/*
* Lock support. It has to be implemented as semaphores.
* I [Dag] tried to implement it with mutex but I could find a way to
diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h
index f588b46..c010b3a 100644
--- a/Python/thread_pthread.h
+++ b/Python/thread_pthread.h
@@ -16,6 +16,7 @@
#undef destructor
#endif
#include <signal.h>
+#include <unistd.h> /* pause(), also getthrid() on OpenBSD */
#if defined(__linux__)
# include <sys/syscall.h> /* syscall(SYS_gettid) */
@@ -23,8 +24,6 @@
# include <pthread_np.h> /* pthread_getthreadid_np() */
#elif defined(__FreeBSD_kernel__)
# include <sys/syscall.h> /* syscall(SYS_thr_self) */
-#elif defined(__OpenBSD__)
-# include <unistd.h> /* getthrid() */
#elif defined(_AIX)
# include <sys/thread.h> /* thread_self() */
#elif defined(__NetBSD__)
@@ -419,6 +418,18 @@ PyThread_exit_thread(void)
#endif
}
+void _Py_NO_RETURN
+PyThread_hang_thread(void)
+{
+ while (1) {
+#if defined(__wasi__)
+ sleep(9999999); // WASI doesn't have pause() ?!
+#else
+ pause();
+#endif
+ }
+}
+
#ifdef USE_SEMAPHORES
/*