mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	gh-112538: Add internal-only _PyThreadStateImpl "wrapper" for PyThreadState (gh-112560)
Every PyThreadState instance is now actually a _PyThreadStateImpl. It is safe to cast from `PyThreadState*` to `_PyThreadStateImpl*` and back. The _PyThreadStateImpl will contain fields that we do not want to expose in the public C API.
This commit is contained in:
		
							parent
							
								
									bf0beae6a0
								
							
						
					
					
						commit
						db460735af
					
				
					 7 changed files with 54 additions and 17 deletions
				
			
		|  | @ -29,6 +29,7 @@ extern "C" { | ||||||
| #include "pycore_list.h"          // struct _Py_list_state | #include "pycore_list.h"          // struct _Py_list_state | ||||||
| #include "pycore_object_state.h"  // struct _py_object_state | #include "pycore_object_state.h"  // struct _py_object_state | ||||||
| #include "pycore_obmalloc.h"      // struct _obmalloc_state | #include "pycore_obmalloc.h"      // struct _obmalloc_state | ||||||
|  | #include "pycore_tstate.h"        // _PyThreadStateImpl | ||||||
| #include "pycore_tuple.h"         // struct _Py_tuple_state | #include "pycore_tuple.h"         // struct _Py_tuple_state | ||||||
| #include "pycore_typeobject.h"    // struct types_state | #include "pycore_typeobject.h"    // struct types_state | ||||||
| #include "pycore_unicodeobject.h" // struct _Py_unicode_state | #include "pycore_unicodeobject.h" // struct _Py_unicode_state | ||||||
|  | @ -210,8 +211,8 @@ struct _is { | ||||||
|     struct _Py_interp_cached_objects cached_objects; |     struct _Py_interp_cached_objects cached_objects; | ||||||
|     struct _Py_interp_static_objects static_objects; |     struct _Py_interp_static_objects static_objects; | ||||||
| 
 | 
 | ||||||
|    /* the initial PyInterpreterState.threads.head */ |     /* the initial PyInterpreterState.threads.head */ | ||||||
|     PyThreadState _initial_thread; |     _PyThreadStateImpl _initial_thread; | ||||||
|     Py_ssize_t _interactive_src_count; |     Py_ssize_t _interactive_src_count; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -186,7 +186,12 @@ extern PyTypeObject _PyExc_MemoryError; | ||||||
|                 }, \ |                 }, \ | ||||||
|             }, \ |             }, \ | ||||||
|         }, \ |         }, \ | ||||||
|         ._initial_thread = _PyThreadState_INIT, \ |         ._initial_thread = _PyThreadStateImpl_INIT, \ | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | #define _PyThreadStateImpl_INIT \ | ||||||
|  |     { \ | ||||||
|  |         .base = _PyThreadState_INIT, \ | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| #define _PyThreadState_INIT \ | #define _PyThreadState_INIT \ | ||||||
|  |  | ||||||
							
								
								
									
										26
									
								
								Include/internal/pycore_tstate.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Include/internal/pycore_tstate.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | #ifndef Py_INTERNAL_TSTATE_H | ||||||
|  | #define Py_INTERNAL_TSTATE_H | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef Py_BUILD_CORE | ||||||
|  | #  error "this header requires Py_BUILD_CORE define" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // Every PyThreadState is actually allocated as a _PyThreadStateImpl. The
 | ||||||
|  | // PyThreadState fields are exposed as part of the C API, although most fields
 | ||||||
|  | // are intended to be private. The _PyThreadStateImpl fields not exposed.
 | ||||||
|  | typedef struct _PyThreadStateImpl { | ||||||
|  |     // semi-public fields are in PyThreadState.
 | ||||||
|  |     PyThreadState base; | ||||||
|  | 
 | ||||||
|  |     // TODO: add private fields here
 | ||||||
|  | } _PyThreadStateImpl; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | #endif /* !Py_INTERNAL_TSTATE_H */ | ||||||
|  | @ -1874,6 +1874,7 @@ PYTHON_HEADERS= \ | ||||||
| 		$(srcdir)/Include/internal/pycore_token.h \ | 		$(srcdir)/Include/internal/pycore_token.h \ | ||||||
| 		$(srcdir)/Include/internal/pycore_traceback.h \ | 		$(srcdir)/Include/internal/pycore_traceback.h \ | ||||||
| 		$(srcdir)/Include/internal/pycore_tracemalloc.h \ | 		$(srcdir)/Include/internal/pycore_tracemalloc.h \ | ||||||
|  | 		$(srcdir)/Include/internal/pycore_tstate.h \ | ||||||
| 		$(srcdir)/Include/internal/pycore_tuple.h \ | 		$(srcdir)/Include/internal/pycore_tuple.h \ | ||||||
| 		$(srcdir)/Include/internal/pycore_typeobject.h \ | 		$(srcdir)/Include/internal/pycore_typeobject.h \ | ||||||
| 		$(srcdir)/Include/internal/pycore_typevarobject.h \ | 		$(srcdir)/Include/internal/pycore_typevarobject.h \ | ||||||
|  |  | ||||||
|  | @ -285,6 +285,7 @@ | ||||||
|     <ClInclude Include="..\Include\internal\pycore_token.h" /> |     <ClInclude Include="..\Include\internal\pycore_token.h" /> | ||||||
|     <ClInclude Include="..\Include\internal\pycore_traceback.h" /> |     <ClInclude Include="..\Include\internal\pycore_traceback.h" /> | ||||||
|     <ClInclude Include="..\Include\internal\pycore_tracemalloc.h" /> |     <ClInclude Include="..\Include\internal\pycore_tracemalloc.h" /> | ||||||
|  |     <ClInclude Include="..\Include\internal\pycore_tstate.h" /> | ||||||
|     <ClInclude Include="..\Include\internal\pycore_tuple.h" /> |     <ClInclude Include="..\Include\internal\pycore_tuple.h" /> | ||||||
|     <ClInclude Include="..\Include\internal\pycore_typeobject.h" /> |     <ClInclude Include="..\Include\internal\pycore_typeobject.h" /> | ||||||
|     <ClInclude Include="..\Include\internal\pycore_typevarobject.h" /> |     <ClInclude Include="..\Include\internal\pycore_typevarobject.h" /> | ||||||
|  |  | ||||||
|  | @ -780,6 +780,9 @@ | ||||||
|     <ClInclude Include="..\Include\internal\pycore_tracemalloc.h"> |     <ClInclude Include="..\Include\internal\pycore_tracemalloc.h"> | ||||||
|       <Filter>Include\internal</Filter> |       <Filter>Include\internal</Filter> | ||||||
|     </ClInclude> |     </ClInclude> | ||||||
|  |     <ClInclude Include="..\Include\internal\pycore_tstate.h"> | ||||||
|  |       <Filter>Include\internal</Filter> | ||||||
|  |     </ClInclude> | ||||||
|     <ClInclude Include="..\Include\internal\pycore_tuple.h"> |     <ClInclude Include="..\Include\internal\pycore_tuple.h"> | ||||||
|       <Filter>Include\internal</Filter> |       <Filter>Include\internal</Filter> | ||||||
|     </ClInclude> |     </ClInclude> | ||||||
|  |  | ||||||
|  | @ -1353,20 +1353,19 @@ allocate_chunk(int size_in_bytes, _PyStackChunk* previous) | ||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static PyThreadState * | static _PyThreadStateImpl * | ||||||
| alloc_threadstate(void) | alloc_threadstate(void) | ||||||
| { | { | ||||||
|     return PyMem_RawCalloc(1, sizeof(PyThreadState)); |     return PyMem_RawCalloc(1, sizeof(_PyThreadStateImpl)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| free_threadstate(PyThreadState *tstate) | free_threadstate(_PyThreadStateImpl *tstate) | ||||||
| { | { | ||||||
|     // The initial thread state of the interpreter is allocated
 |     // The initial thread state of the interpreter is allocated
 | ||||||
|     // as part of the interpreter state so should not be freed.
 |     // as part of the interpreter state so should not be freed.
 | ||||||
|     if (tstate == &tstate->interp->_initial_thread) { |     if (tstate == &tstate->base.interp->_initial_thread) { | ||||||
|         // Restore to _PyThreadState_INIT.
 |         // Restore to _PyThreadState_INIT.
 | ||||||
|         tstate = &tstate->interp->_initial_thread; |  | ||||||
|         memcpy(tstate, |         memcpy(tstate, | ||||||
|                &initial._main_interpreter._initial_thread, |                &initial._main_interpreter._initial_thread, | ||||||
|                sizeof(*tstate)); |                sizeof(*tstate)); | ||||||
|  | @ -1385,9 +1384,10 @@ free_threadstate(PyThreadState *tstate) | ||||||
|   */ |   */ | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| init_threadstate(PyThreadState *tstate, | init_threadstate(_PyThreadStateImpl *_tstate, | ||||||
|                  PyInterpreterState *interp, uint64_t id, int whence) |                  PyInterpreterState *interp, uint64_t id, int whence) | ||||||
| { | { | ||||||
|  |     PyThreadState *tstate = (PyThreadState *)_tstate; | ||||||
|     if (tstate->_status.initialized) { |     if (tstate->_status.initialized) { | ||||||
|         Py_FatalError("thread state already initialized"); |         Py_FatalError("thread state already initialized"); | ||||||
|     } |     } | ||||||
|  | @ -1444,13 +1444,13 @@ add_threadstate(PyInterpreterState *interp, PyThreadState *tstate, | ||||||
| static PyThreadState * | static PyThreadState * | ||||||
| new_threadstate(PyInterpreterState *interp, int whence) | new_threadstate(PyInterpreterState *interp, int whence) | ||||||
| { | { | ||||||
|     PyThreadState *tstate; |     _PyThreadStateImpl *tstate; | ||||||
|     _PyRuntimeState *runtime = interp->runtime; |     _PyRuntimeState *runtime = interp->runtime; | ||||||
|     // We don't need to allocate a thread state for the main interpreter
 |     // We don't need to allocate a thread state for the main interpreter
 | ||||||
|     // (the common case), but doing it later for the other case revealed a
 |     // (the common case), but doing it later for the other case revealed a
 | ||||||
|     // reentrancy problem (deadlock).  So for now we always allocate before
 |     // reentrancy problem (deadlock).  So for now we always allocate before
 | ||||||
|     // taking the interpreters lock.  See GH-96071.
 |     // taking the interpreters lock.  See GH-96071.
 | ||||||
|     PyThreadState *new_tstate = alloc_threadstate(); |     _PyThreadStateImpl *new_tstate = alloc_threadstate(); | ||||||
|     int used_newtstate; |     int used_newtstate; | ||||||
|     if (new_tstate == NULL) { |     if (new_tstate == NULL) { | ||||||
|         return NULL; |         return NULL; | ||||||
|  | @ -1482,14 +1482,14 @@ new_threadstate(PyInterpreterState *interp, int whence) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     init_threadstate(tstate, interp, id, whence); |     init_threadstate(tstate, interp, id, whence); | ||||||
|     add_threadstate(interp, tstate, old_head); |     add_threadstate(interp, (PyThreadState *)tstate, old_head); | ||||||
| 
 | 
 | ||||||
|     HEAD_UNLOCK(runtime); |     HEAD_UNLOCK(runtime); | ||||||
|     if (!used_newtstate) { |     if (!used_newtstate) { | ||||||
|         // Must be called with lock unlocked to avoid re-entrancy deadlock.
 |         // Must be called with lock unlocked to avoid re-entrancy deadlock.
 | ||||||
|         PyMem_RawFree(new_tstate); |         PyMem_RawFree(new_tstate); | ||||||
|     } |     } | ||||||
|     return tstate; |     return (PyThreadState *)tstate; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| PyThreadState * | PyThreadState * | ||||||
|  | @ -1678,7 +1678,7 @@ zapthreads(PyInterpreterState *interp) | ||||||
|     while ((tstate = interp->threads.head) != NULL) { |     while ((tstate = interp->threads.head) != NULL) { | ||||||
|         tstate_verify_not_active(tstate); |         tstate_verify_not_active(tstate); | ||||||
|         tstate_delete_common(tstate); |         tstate_delete_common(tstate); | ||||||
|         free_threadstate(tstate); |         free_threadstate((_PyThreadStateImpl *)tstate); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1689,7 +1689,7 @@ PyThreadState_Delete(PyThreadState *tstate) | ||||||
|     _Py_EnsureTstateNotNULL(tstate); |     _Py_EnsureTstateNotNULL(tstate); | ||||||
|     tstate_verify_not_active(tstate); |     tstate_verify_not_active(tstate); | ||||||
|     tstate_delete_common(tstate); |     tstate_delete_common(tstate); | ||||||
|     free_threadstate(tstate); |     free_threadstate((_PyThreadStateImpl *)tstate); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -1701,7 +1701,7 @@ _PyThreadState_DeleteCurrent(PyThreadState *tstate) | ||||||
|     tstate_delete_common(tstate); |     tstate_delete_common(tstate); | ||||||
|     current_fast_clear(tstate->interp->runtime); |     current_fast_clear(tstate->interp->runtime); | ||||||
|     _PyEval_ReleaseLock(tstate->interp, NULL); |     _PyEval_ReleaseLock(tstate->interp, NULL); | ||||||
|     free_threadstate(tstate); |     free_threadstate((_PyThreadStateImpl *)tstate); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
|  | @ -1751,7 +1751,7 @@ _PyThreadState_DeleteExcept(PyThreadState *tstate) | ||||||
|     for (p = list; p; p = next) { |     for (p = list; p; p = next) { | ||||||
|         next = p->next; |         next = p->next; | ||||||
|         PyThreadState_Clear(p); |         PyThreadState_Clear(p); | ||||||
|         free_threadstate(p); |         free_threadstate((_PyThreadStateImpl *)p); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Sam Gross
						Sam Gross