summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Gross <colesbury@gmail.com>2023-12-07 19:11:45 (GMT)
committerGitHub <noreply@github.com>2023-12-07 19:11:45 (GMT)
commitdb460735af7503984d1b7d878069722db44b11e8 (patch)
tree5f8579b85da5f0902f5d14c8711de77256156aba
parentbf0beae6a05f3266606a21e22a4d803abbb8d731 (diff)
downloadcpython-db460735af7503984d1b7d878069722db44b11e8.zip
cpython-db460735af7503984d1b7d878069722db44b11e8.tar.gz
cpython-db460735af7503984d1b7d878069722db44b11e8.tar.bz2
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.
-rw-r--r--Include/internal/pycore_interp.h5
-rw-r--r--Include/internal/pycore_runtime_init.h7
-rw-r--r--Include/internal/pycore_tstate.h26
-rw-r--r--Makefile.pre.in1
-rw-r--r--PCbuild/pythoncore.vcxproj1
-rw-r--r--PCbuild/pythoncore.vcxproj.filters3
-rw-r--r--Python/pystate.c28
7 files changed, 54 insertions, 17 deletions
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index 498db8b..2a68319 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -29,6 +29,7 @@ extern "C" {
#include "pycore_list.h" // struct _Py_list_state
#include "pycore_object_state.h" // struct _py_object_state
#include "pycore_obmalloc.h" // struct _obmalloc_state
+#include "pycore_tstate.h" // _PyThreadStateImpl
#include "pycore_tuple.h" // struct _Py_tuple_state
#include "pycore_typeobject.h" // struct types_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_static_objects static_objects;
- /* the initial PyInterpreterState.threads.head */
- PyThreadState _initial_thread;
+ /* the initial PyInterpreterState.threads.head */
+ _PyThreadStateImpl _initial_thread;
Py_ssize_t _interactive_src_count;
};
diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h
index fa5d811..d324a94 100644
--- a/Include/internal/pycore_runtime_init.h
+++ b/Include/internal/pycore_runtime_init.h
@@ -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 \
diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h
new file mode 100644
index 0000000..17f3e86
--- /dev/null
+++ b/Include/internal/pycore_tstate.h
@@ -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 */
diff --git a/Makefile.pre.in b/Makefile.pre.in
index db85b11..f57894a 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1874,6 +1874,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/internal/pycore_token.h \
$(srcdir)/Include/internal/pycore_traceback.h \
$(srcdir)/Include/internal/pycore_tracemalloc.h \
+ $(srcdir)/Include/internal/pycore_tstate.h \
$(srcdir)/Include/internal/pycore_tuple.h \
$(srcdir)/Include/internal/pycore_typeobject.h \
$(srcdir)/Include/internal/pycore_typevarobject.h \
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index be1b98d..278f1f5 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -285,6 +285,7 @@
<ClInclude Include="..\Include\internal\pycore_token.h" />
<ClInclude Include="..\Include\internal\pycore_traceback.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_typeobject.h" />
<ClInclude Include="..\Include\internal\pycore_typevarobject.h" />
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index 4f0da8f..c9b34c6 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -780,6 +780,9 @@
<ClInclude Include="..\Include\internal\pycore_tracemalloc.h">
<Filter>Include\internal</Filter>
</ClInclude>
+ <ClInclude Include="..\Include\internal\pycore_tstate.h">
+ <Filter>Include\internal</Filter>
+ </ClInclude>
<ClInclude Include="..\Include\internal\pycore_tuple.h">
<Filter>Include\internal</Filter>
</ClInclude>
diff --git a/Python/pystate.c b/Python/pystate.c
index 6196b15..c759916 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -1353,20 +1353,19 @@ allocate_chunk(int size_in_bytes, _PyStackChunk* previous)
return res;
}
-static PyThreadState *
+static _PyThreadStateImpl *
alloc_threadstate(void)
{
- return PyMem_RawCalloc(1, sizeof(PyThreadState));
+ return PyMem_RawCalloc(1, sizeof(_PyThreadStateImpl));
}
static void
-free_threadstate(PyThreadState *tstate)
+free_threadstate(_PyThreadStateImpl *tstate)
{
// The initial thread state of the interpreter is allocated
// 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.
- tstate = &tstate->interp->_initial_thread;
memcpy(tstate,
&initial._main_interpreter._initial_thread,
sizeof(*tstate));
@@ -1385,9 +1384,10 @@ free_threadstate(PyThreadState *tstate)
*/
static void
-init_threadstate(PyThreadState *tstate,
+init_threadstate(_PyThreadStateImpl *_tstate,
PyInterpreterState *interp, uint64_t id, int whence)
{
+ PyThreadState *tstate = (PyThreadState *)_tstate;
if (tstate->_status.initialized) {
Py_FatalError("thread state already initialized");
}
@@ -1444,13 +1444,13 @@ add_threadstate(PyInterpreterState *interp, PyThreadState *tstate,
static PyThreadState *
new_threadstate(PyInterpreterState *interp, int whence)
{
- PyThreadState *tstate;
+ _PyThreadStateImpl *tstate;
_PyRuntimeState *runtime = interp->runtime;
// 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
// reentrancy problem (deadlock). So for now we always allocate before
// taking the interpreters lock. See GH-96071.
- PyThreadState *new_tstate = alloc_threadstate();
+ _PyThreadStateImpl *new_tstate = alloc_threadstate();
int used_newtstate;
if (new_tstate == NULL) {
return NULL;
@@ -1482,14 +1482,14 @@ new_threadstate(PyInterpreterState *interp, int whence)
}
init_threadstate(tstate, interp, id, whence);
- add_threadstate(interp, tstate, old_head);
+ add_threadstate(interp, (PyThreadState *)tstate, old_head);
HEAD_UNLOCK(runtime);
if (!used_newtstate) {
// Must be called with lock unlocked to avoid re-entrancy deadlock.
PyMem_RawFree(new_tstate);
}
- return tstate;
+ return (PyThreadState *)tstate;
}
PyThreadState *
@@ -1678,7 +1678,7 @@ zapthreads(PyInterpreterState *interp)
while ((tstate = interp->threads.head) != NULL) {
tstate_verify_not_active(tstate);
tstate_delete_common(tstate);
- free_threadstate(tstate);
+ free_threadstate((_PyThreadStateImpl *)tstate);
}
}
@@ -1689,7 +1689,7 @@ PyThreadState_Delete(PyThreadState *tstate)
_Py_EnsureTstateNotNULL(tstate);
tstate_verify_not_active(tstate);
tstate_delete_common(tstate);
- free_threadstate(tstate);
+ free_threadstate((_PyThreadStateImpl *)tstate);
}
@@ -1701,7 +1701,7 @@ _PyThreadState_DeleteCurrent(PyThreadState *tstate)
tstate_delete_common(tstate);
current_fast_clear(tstate->interp->runtime);
_PyEval_ReleaseLock(tstate->interp, NULL);
- free_threadstate(tstate);
+ free_threadstate((_PyThreadStateImpl *)tstate);
}
void
@@ -1751,7 +1751,7 @@ _PyThreadState_DeleteExcept(PyThreadState *tstate)
for (p = list; p; p = next) {
next = p->next;
PyThreadState_Clear(p);
- free_threadstate(p);
+ free_threadstate((_PyThreadStateImpl *)p);
}
}