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
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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…
x
Reference in New Issue
Block a user