mirror of
				https://github.com/python/cpython.git
				synced 2025-10-30 21:21:22 +00:00 
			
		
		
		
	 8cc5aa47ee
			
		
	
	
		8cc5aa47ee
		
			
		
	
	
	
	
		
			
			Instead of surprise crashes and memory corruption, we now hang threads that attempt to re-enter the Python interpreter after Python runtime finalization has started. These are typically daemon threads (our long standing mis-feature) but could also be threads spawned by extension modules that then try to call into Python. This marks the `PyThread_exit_thread` public C API as deprecated as there is no plausible safe way to accomplish that on any supported platform in the face of things like C++ code with finalizers anywhere on a thread's stack. Doing this was the least bad option. Co-authored-by: Gregory P. Smith <greg@krypto.org>
		
			
				
	
	
		
			172 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			172 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #ifndef Py_INTERNAL_PYTHREAD_H
 | |
| #define Py_INTERNAL_PYTHREAD_H
 | |
| #ifdef __cplusplus
 | |
| extern "C" {
 | |
| #endif
 | |
| 
 | |
| #ifndef Py_BUILD_CORE
 | |
| #  error "this header requires Py_BUILD_CORE define"
 | |
| #endif
 | |
| 
 | |
| #include "dynamic_annotations.h" // _Py_ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX
 | |
| #include "pycore_llist.h"        // struct llist_node
 | |
| 
 | |
| // Get _POSIX_THREADS and _POSIX_SEMAPHORES macros if available
 | |
| #if (defined(HAVE_UNISTD_H) && !defined(_POSIX_THREADS) \
 | |
|                             && !defined(_POSIX_SEMAPHORES))
 | |
| #  include <unistd.h>             // _POSIX_THREADS, _POSIX_SEMAPHORES
 | |
| #endif
 | |
| #if (defined(HAVE_PTHREAD_H) && !defined(_POSIX_THREADS) \
 | |
|                              && !defined(_POSIX_SEMAPHORES))
 | |
|    // This means pthreads are not implemented in libc headers, hence the macro
 | |
|    // not present in <unistd.h>. But they still can be implemented as an
 | |
|    // external library (e.g. gnu pth in pthread emulation)
 | |
| #  include <pthread.h>            // _POSIX_THREADS, _POSIX_SEMAPHORES
 | |
| #endif
 | |
| #if !defined(_POSIX_THREADS) && defined(__hpux) && defined(_SC_THREADS)
 | |
|    // Check if we're running on HP-UX and _SC_THREADS is defined. If so, then
 | |
|    // enough of the POSIX threads package is implemented to support Python
 | |
|    // threads.
 | |
|    //
 | |
|    // This is valid for HP-UX 11.23 running on an ia64 system. If needed, add
 | |
|    // a check of __ia64 to verify that we're running on an ia64 system instead
 | |
|    // of a pa-risc system.
 | |
| #  define _POSIX_THREADS
 | |
| #endif
 | |
| 
 | |
| 
 | |
| #if defined(_POSIX_THREADS) || defined(HAVE_PTHREAD_STUBS)
 | |
| #  define _USE_PTHREADS
 | |
| #endif
 | |
| 
 | |
| #if defined(_USE_PTHREADS) && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
 | |
| // monotonic is supported statically.  It doesn't mean it works on runtime.
 | |
| #  define CONDATTR_MONOTONIC
 | |
| #endif
 | |
| 
 | |
| 
 | |
| #if defined(HAVE_PTHREAD_STUBS)
 | |
| #include "cpython/pthread_stubs.h"  // PTHREAD_KEYS_MAX
 | |
| #include <stdbool.h>                // bool
 | |
| 
 | |
| // pthread_key
 | |
| struct py_stub_tls_entry {
 | |
|     bool in_use;
 | |
|     void *value;
 | |
| };
 | |
| #endif
 | |
| 
 | |
| struct _pythread_runtime_state {
 | |
|     int initialized;
 | |
| 
 | |
| #ifdef _USE_PTHREADS
 | |
|     // This matches when thread_pthread.h is used.
 | |
|     struct {
 | |
|         /* NULL when pthread_condattr_setclock(CLOCK_MONOTONIC) is not supported. */
 | |
|         pthread_condattr_t *ptr;
 | |
| # ifdef CONDATTR_MONOTONIC
 | |
|     /* The value to which condattr_monotonic is set. */
 | |
|         pthread_condattr_t val;
 | |
| # endif
 | |
|     } _condattr_monotonic;
 | |
| 
 | |
| #endif  // USE_PTHREADS
 | |
| 
 | |
| #if defined(HAVE_PTHREAD_STUBS)
 | |
|     struct {
 | |
|         struct py_stub_tls_entry tls_entries[PTHREAD_KEYS_MAX];
 | |
|     } stubs;
 | |
| #endif
 | |
| 
 | |
|     // Linked list of ThreadHandles
 | |
|     struct llist_node handles;
 | |
| };
 | |
| 
 | |
| #define _pythread_RUNTIME_INIT(pythread) \
 | |
|     { \
 | |
|         .handles = LLIST_INIT(pythread.handles), \
 | |
|     }
 | |
| 
 | |
| #ifdef HAVE_FORK
 | |
| /* Private function to reinitialize a lock at fork in the child process.
 | |
|    Reset the lock to the unlocked state.
 | |
|    Return 0 on success, return -1 on error. */
 | |
| extern int _PyThread_at_fork_reinit(PyThread_type_lock *lock);
 | |
| extern void _PyThread_AfterFork(struct _pythread_runtime_state *state);
 | |
| #endif  /* HAVE_FORK */
 | |
| 
 | |
| 
 | |
| // unset: -1 seconds, in nanoseconds
 | |
| #define PyThread_UNSET_TIMEOUT ((PyTime_t)(-1 * 1000 * 1000 * 1000))
 | |
| 
 | |
| // Exported for the _interpchannels module.
 | |
| PyAPI_FUNC(int) PyThread_ParseTimeoutArg(
 | |
|     PyObject *arg,
 | |
|     int blocking,
 | |
|     PY_TIMEOUT_T *timeout);
 | |
| 
 | |
| /* Helper to acquire an interruptible lock with a timeout.  If the lock acquire
 | |
|  * is interrupted, signal handlers are run, and if they raise an exception,
 | |
|  * PY_LOCK_INTR is returned.  Otherwise, PY_LOCK_ACQUIRED or PY_LOCK_FAILURE
 | |
|  * are returned, depending on whether the lock can be acquired within the
 | |
|  * timeout.
 | |
|  */
 | |
| // Exported for the _interpchannels module.
 | |
| PyAPI_FUNC(PyLockStatus) PyThread_acquire_lock_timed_with_retries(
 | |
|     PyThread_type_lock,
 | |
|     PY_TIMEOUT_T microseconds);
 | |
| 
 | |
| typedef unsigned long long PyThread_ident_t;
 | |
| typedef Py_uintptr_t PyThread_handle_t;
 | |
| 
 | |
| #define PY_FORMAT_THREAD_IDENT_T "llu"
 | |
| #define Py_PARSE_THREAD_IDENT_T "K"
 | |
| 
 | |
| PyAPI_FUNC(PyThread_ident_t) PyThread_get_thread_ident_ex(void);
 | |
| 
 | |
| /* Thread joining APIs.
 | |
|  *
 | |
|  * These APIs have a strict contract:
 | |
|  *  - Either PyThread_join_thread or PyThread_detach_thread must be called
 | |
|  *    exactly once with the given handle.
 | |
|  *  - Calling neither PyThread_join_thread nor PyThread_detach_thread results
 | |
|  *    in a resource leak until the end of the process.
 | |
|  *  - Any other usage, such as calling both PyThread_join_thread and
 | |
|  *    PyThread_detach_thread, or calling them more than once (including
 | |
|  *    simultaneously), results in undefined behavior.
 | |
|  */
 | |
| PyAPI_FUNC(int) PyThread_start_joinable_thread(void (*func)(void *),
 | |
|                                                void *arg,
 | |
|                                                PyThread_ident_t* ident,
 | |
|                                                PyThread_handle_t* handle);
 | |
| /*
 | |
|  * Join a thread started with `PyThread_start_joinable_thread`.
 | |
|  * This function cannot be interrupted. It returns 0 on success,
 | |
|  * a non-zero value on failure.
 | |
|  */
 | |
| PyAPI_FUNC(int) PyThread_join_thread(PyThread_handle_t);
 | |
| /*
 | |
|  * Detach a thread started with `PyThread_start_joinable_thread`, such
 | |
|  * that its resources are released as soon as it exits.
 | |
|  * This function cannot be interrupted. It returns 0 on success,
 | |
|  * a non-zero value on failure.
 | |
|  */
 | |
| PyAPI_FUNC(int) PyThread_detach_thread(PyThread_handle_t);
 | |
| /*
 | |
|  * Hangs the thread indefinitely without exiting it.
 | |
|  *
 | |
|  * gh-87135: There is no safe way to exit a thread other than returning
 | |
|  * normally from its start function.  This is used during finalization in lieu
 | |
|  * of actually exiting the thread.  Since the program is expected to terminate
 | |
|  * soon anyway, it does not matter if the thread stack stays around until then.
 | |
|  *
 | |
|  * This is unfortunate for embedders who may not be terminating their process
 | |
|  * when they're done with the interpreter, but our C API design does not allow
 | |
|  * for safely exiting threads attempting to re-enter Python post finalization.
 | |
|  */
 | |
| void _Py_NO_RETURN PyThread_hang_thread(void);
 | |
| 
 | |
| #ifdef __cplusplus
 | |
| }
 | |
| #endif
 | |
| #endif /* !Py_INTERNAL_PYTHREAD_H */
 |