Skip to content

Commit db46073

Browse files
authored
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.
1 parent bf0beae commit db46073

File tree

7 files changed

+54
-17
lines changed

7 files changed

+54
-17
lines changed

Include/internal/pycore_interp.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ extern "C" {
2929
#include "pycore_list.h" // struct _Py_list_state
3030
#include "pycore_object_state.h" // struct _py_object_state
3131
#include "pycore_obmalloc.h" // struct _obmalloc_state
32+
#include "pycore_tstate.h" // _PyThreadStateImpl
3233
#include "pycore_tuple.h" // struct _Py_tuple_state
3334
#include "pycore_typeobject.h" // struct types_state
3435
#include "pycore_unicodeobject.h" // struct _Py_unicode_state
@@ -210,8 +211,8 @@ struct _is {
210211
struct _Py_interp_cached_objects cached_objects;
211212
struct _Py_interp_static_objects static_objects;
212213

213-
/* the initial PyInterpreterState.threads.head */
214-
PyThreadState _initial_thread;
214+
/* the initial PyInterpreterState.threads.head */
215+
_PyThreadStateImpl _initial_thread;
215216
Py_ssize_t _interactive_src_count;
216217
};
217218

Include/internal/pycore_runtime_init.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,12 @@ extern PyTypeObject _PyExc_MemoryError;
186186
}, \
187187
}, \
188188
}, \
189-
._initial_thread = _PyThreadState_INIT, \
189+
._initial_thread = _PyThreadStateImpl_INIT, \
190+
}
191+
192+
#define _PyThreadStateImpl_INIT \
193+
{ \
194+
.base = _PyThreadState_INIT, \
190195
}
191196

192197
#define _PyThreadState_INIT \

Include/internal/pycore_tstate.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#ifndef Py_INTERNAL_TSTATE_H
2+
#define Py_INTERNAL_TSTATE_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#ifndef Py_BUILD_CORE
8+
# error "this header requires Py_BUILD_CORE define"
9+
#endif
10+
11+
12+
// Every PyThreadState is actually allocated as a _PyThreadStateImpl. The
13+
// PyThreadState fields are exposed as part of the C API, although most fields
14+
// are intended to be private. The _PyThreadStateImpl fields not exposed.
15+
typedef struct _PyThreadStateImpl {
16+
// semi-public fields are in PyThreadState.
17+
PyThreadState base;
18+
19+
// TODO: add private fields here
20+
} _PyThreadStateImpl;
21+
22+
23+
#ifdef __cplusplus
24+
}
25+
#endif
26+
#endif /* !Py_INTERNAL_TSTATE_H */

Makefile.pre.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1874,6 +1874,7 @@ PYTHON_HEADERS= \
18741874
$(srcdir)/Include/internal/pycore_token.h \
18751875
$(srcdir)/Include/internal/pycore_traceback.h \
18761876
$(srcdir)/Include/internal/pycore_tracemalloc.h \
1877+
$(srcdir)/Include/internal/pycore_tstate.h \
18771878
$(srcdir)/Include/internal/pycore_tuple.h \
18781879
$(srcdir)/Include/internal/pycore_typeobject.h \
18791880
$(srcdir)/Include/internal/pycore_typevarobject.h \

PCbuild/pythoncore.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@
285285
<ClInclude Include="..\Include\internal\pycore_token.h" />
286286
<ClInclude Include="..\Include\internal\pycore_traceback.h" />
287287
<ClInclude Include="..\Include\internal\pycore_tracemalloc.h" />
288+
<ClInclude Include="..\Include\internal\pycore_tstate.h" />
288289
<ClInclude Include="..\Include\internal\pycore_tuple.h" />
289290
<ClInclude Include="..\Include\internal\pycore_typeobject.h" />
290291
<ClInclude Include="..\Include\internal\pycore_typevarobject.h" />

PCbuild/pythoncore.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,9 @@
780780
<ClInclude Include="..\Include\internal\pycore_tracemalloc.h">
781781
<Filter>Include\internal</Filter>
782782
</ClInclude>
783+
<ClInclude Include="..\Include\internal\pycore_tstate.h">
784+
<Filter>Include\internal</Filter>
785+
</ClInclude>
783786
<ClInclude Include="..\Include\internal\pycore_tuple.h">
784787
<Filter>Include\internal</Filter>
785788
</ClInclude>

Python/pystate.c

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,20 +1353,19 @@ allocate_chunk(int size_in_bytes, _PyStackChunk* previous)
13531353
return res;
13541354
}
13551355

1356-
static PyThreadState *
1356+
static _PyThreadStateImpl *
13571357
alloc_threadstate(void)
13581358
{
1359-
return PyMem_RawCalloc(1, sizeof(PyThreadState));
1359+
return PyMem_RawCalloc(1, sizeof(_PyThreadStateImpl));
13601360
}
13611361

13621362
static void
1363-
free_threadstate(PyThreadState *tstate)
1363+
free_threadstate(_PyThreadStateImpl *tstate)
13641364
{
13651365
// The initial thread state of the interpreter is allocated
13661366
// as part of the interpreter state so should not be freed.
1367-
if (tstate == &tstate->interp->_initial_thread) {
1367+
if (tstate == &tstate->base.interp->_initial_thread) {
13681368
// Restore to _PyThreadState_INIT.
1369-
tstate = &tstate->interp->_initial_thread;
13701369
memcpy(tstate,
13711370
&initial._main_interpreter._initial_thread,
13721371
sizeof(*tstate));
@@ -1385,9 +1384,10 @@ free_threadstate(PyThreadState *tstate)
13851384
*/
13861385

13871386
static void
1388-
init_threadstate(PyThreadState *tstate,
1387+
init_threadstate(_PyThreadStateImpl *_tstate,
13891388
PyInterpreterState *interp, uint64_t id, int whence)
13901389
{
1390+
PyThreadState *tstate = (PyThreadState *)_tstate;
13911391
if (tstate->_status.initialized) {
13921392
Py_FatalError("thread state already initialized");
13931393
}
@@ -1444,13 +1444,13 @@ add_threadstate(PyInterpreterState *interp, PyThreadState *tstate,
14441444
static PyThreadState *
14451445
new_threadstate(PyInterpreterState *interp, int whence)
14461446
{
1447-
PyThreadState *tstate;
1447+
_PyThreadStateImpl *tstate;
14481448
_PyRuntimeState *runtime = interp->runtime;
14491449
// We don't need to allocate a thread state for the main interpreter
14501450
// (the common case), but doing it later for the other case revealed a
14511451
// reentrancy problem (deadlock). So for now we always allocate before
14521452
// taking the interpreters lock. See GH-96071.
1453-
PyThreadState *new_tstate = alloc_threadstate();
1453+
_PyThreadStateImpl *new_tstate = alloc_threadstate();
14541454
int used_newtstate;
14551455
if (new_tstate == NULL) {
14561456
return NULL;
@@ -1482,14 +1482,14 @@ new_threadstate(PyInterpreterState *interp, int whence)
14821482
}
14831483

14841484
init_threadstate(tstate, interp, id, whence);
1485-
add_threadstate(interp, tstate, old_head);
1485+
add_threadstate(interp, (PyThreadState *)tstate, old_head);
14861486

14871487
HEAD_UNLOCK(runtime);
14881488
if (!used_newtstate) {
14891489
// Must be called with lock unlocked to avoid re-entrancy deadlock.
14901490
PyMem_RawFree(new_tstate);
14911491
}
1492-
return tstate;
1492+
return (PyThreadState *)tstate;
14931493
}
14941494

14951495
PyThreadState *
@@ -1678,7 +1678,7 @@ zapthreads(PyInterpreterState *interp)
16781678
while ((tstate = interp->threads.head) != NULL) {
16791679
tstate_verify_not_active(tstate);
16801680
tstate_delete_common(tstate);
1681-
free_threadstate(tstate);
1681+
free_threadstate((_PyThreadStateImpl *)tstate);
16821682
}
16831683
}
16841684

@@ -1689,7 +1689,7 @@ PyThreadState_Delete(PyThreadState *tstate)
16891689
_Py_EnsureTstateNotNULL(tstate);
16901690
tstate_verify_not_active(tstate);
16911691
tstate_delete_common(tstate);
1692-
free_threadstate(tstate);
1692+
free_threadstate((_PyThreadStateImpl *)tstate);
16931693
}
16941694

16951695

@@ -1701,7 +1701,7 @@ _PyThreadState_DeleteCurrent(PyThreadState *tstate)
17011701
tstate_delete_common(tstate);
17021702
current_fast_clear(tstate->interp->runtime);
17031703
_PyEval_ReleaseLock(tstate->interp, NULL);
1704-
free_threadstate(tstate);
1704+
free_threadstate((_PyThreadStateImpl *)tstate);
17051705
}
17061706

17071707
void
@@ -1751,7 +1751,7 @@ _PyThreadState_DeleteExcept(PyThreadState *tstate)
17511751
for (p = list; p; p = next) {
17521752
next = p->next;
17531753
PyThreadState_Clear(p);
1754-
free_threadstate(p);
1754+
free_threadstate((_PyThreadStateImpl *)p);
17551755
}
17561756
}
17571757

0 commit comments

Comments
 (0)