diff options
Diffstat (limited to 'Python')
-rw-r--r-- | Python/ceval_gil.c | 26 | ||||
-rw-r--r-- | Python/pylifecycle.c | 2 | ||||
-rw-r--r-- | Python/thread_nt.h | 8 | ||||
-rw-r--r-- | Python/thread_pthread.h | 15 |
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 /* |