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:
Sam Gross 2023-12-07 14:11:45 -05:00 committed by GitHub
parent bf0beae6a0
commit db460735af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 54 additions and 17 deletions

View File

@ -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
@ -211,7 +212,7 @@ struct _is {
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;
}; };

View File

@ -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 \

View 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 */

View File

@ -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 \

View File

@ -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" />

View File

@ -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>

View File

@ -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);
} }
} }