Skip to content

Commit 588364c

Browse files
committed
Add thread state references to PyThreadState_Ensure() and
PyThreadState_Release() Per the new updates to PEP-788.
1 parent 3ffe9d0 commit 588364c

4 files changed

Lines changed: 33 additions & 25 deletions

File tree

Include/cpython/pystate.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,6 @@ struct _ts {
213213
/* Number of nested PyThreadState_Ensure() calls on this thread state */
214214
Py_ssize_t counter;
215215

216-
/* Thread state that was active before PyThreadState_Ensure() was called. */
217-
PyThreadState *prior_tstate;
218-
219216
/* Should this thread state be deleted upon calling
220217
PyThreadState_Release() (with the counter at 1)?
221218
@@ -311,6 +308,10 @@ PyAPI_FUNC(void) PyInterpreterWeakRef_Close(PyInterpreterWeakRef wref);
311308
} while (0)
312309

313310

314-
PyAPI_FUNC(int) PyThreadState_Ensure(PyInterpreterRef interp_ref);
311+
/* Thread references */
312+
313+
typedef uintptr_t PyThreadRef;
314+
315+
PyAPI_FUNC(int) PyThreadState_Ensure(PyInterpreterRef interp_ref, PyThreadRef *thread_ref);
315316

316-
PyAPI_FUNC(void) PyThreadState_Release(void);
317+
PyAPI_FUNC(void) PyThreadState_Release(PyThreadRef thread_ref);

Modules/_testcapimodule.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2608,17 +2608,18 @@ test_thread_state_ensure_nested(PyObject *self, PyObject *unused)
26082608
PyInterpreterRef ref = get_strong_ref();
26092609
PyThreadState *save_tstate = PyThreadState_Swap(NULL);
26102610
assert(PyGILState_GetThisThreadState() == save_tstate);
2611+
PyThreadRef refs[10];
26112612

26122613
for (int i = 0; i < 10; ++i) {
26132614
// Test reactivation of the detached tstate.
2614-
if (PyThreadState_Ensure(ref) < 0) {
2615+
if (PyThreadState_Ensure(ref, &refs[i]) < 0) {
26152616
PyInterpreterRef_Close(ref);
26162617
return PyErr_NoMemory();
26172618
}
26182619

26192620
// No new thread state should've been created.
26202621
assert(PyThreadState_Get() == save_tstate);
2621-
PyThreadState_Release();
2622+
PyThreadState_Release(refs[i]);
26222623
}
26232624

26242625
assert(PyThreadState_GetUnchecked() == NULL);
@@ -2627,7 +2628,7 @@ test_thread_state_ensure_nested(PyObject *self, PyObject *unused)
26272628
// If the (detached) gilstate matches the interpreter, then it shouldn't
26282629
// create a new thread state.
26292630
for (int i = 0; i < 10; ++i) {
2630-
if (PyThreadState_Ensure(ref) < 0) {
2631+
if (PyThreadState_Ensure(ref, &refs[i]) < 0) {
26312632
// This will technically leak other thread states, but it doesn't
26322633
// matter because this is a test.
26332634
PyInterpreterRef_Close(ref);
@@ -2639,7 +2640,7 @@ test_thread_state_ensure_nested(PyObject *self, PyObject *unused)
26392640

26402641
for (int i = 0; i < 10; ++i) {
26412642
assert(PyThreadState_Get() == save_tstate);
2642-
PyThreadState_Release();
2643+
PyThreadState_Release(refs[i]);
26432644
}
26442645

26452646
assert(PyThreadState_GetUnchecked() == NULL);
@@ -2672,7 +2673,9 @@ test_thread_state_ensure_crossinterp(PyObject *self, PyObject *unused)
26722673
interp = interpreters.create()
26732674
interp.exec(some_func)
26742675
*/
2675-
if (PyThreadState_Ensure(ref) < 0) {
2676+
PyThreadRef thread_ref;
2677+
PyThreadRef other_thread_ref;
2678+
if (PyThreadState_Ensure(ref, &thread_ref) < 0) {
26762679
PyInterpreterRef_Close(ref);
26772680
return PyErr_NoMemory();
26782681
}
@@ -2683,16 +2686,16 @@ test_thread_state_ensure_crossinterp(PyObject *self, PyObject *unused)
26832686
assert(PyGILState_GetThisThreadState() == ensured_tstate);
26842687

26852688
// Now though, we should reactivate the thread state
2686-
if (PyThreadState_Ensure(ref) < 0) {
2689+
if (PyThreadState_Ensure(ref, &other_thread_ref) < 0) {
26872690
PyInterpreterRef_Close(ref);
26882691
return PyErr_NoMemory();
26892692
}
26902693

26912694
assert(PyThreadState_Get() == ensured_tstate);
2692-
PyThreadState_Release();
2695+
PyThreadState_Release(other_thread_ref);
26932696

26942697
// Ensure that we're restoring the prior thread state
2695-
PyThreadState_Release();
2698+
PyThreadState_Release(thread_ref);
26962699
assert(PyThreadState_Get() == interp_tstate);
26972700
assert(PyGILState_GetThisThreadState() == interp_tstate);
26982701

Programs/_testembed.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2332,19 +2332,20 @@ static void
23322332
do_tstate_ensure(void *arg)
23332333
{
23342334
ThreadData *data = (ThreadData *)arg;
2335-
int res = PyThreadState_Ensure(data->ref);
2335+
PyThreadRef refs[4];
2336+
int res = PyThreadState_Ensure(data->ref, &refs[0]);
23362337
assert(res == 0);
2337-
PyThreadState_Ensure(data->ref);
2338-
PyThreadState_Ensure(data->ref);
2338+
PyThreadState_Ensure(data->ref, &refs[1]);
2339+
PyThreadState_Ensure(data->ref, &refs[2]);
23392340
PyGILState_STATE gstate = PyGILState_Ensure();
2340-
PyThreadState_Ensure(data->ref);
2341+
PyThreadState_Ensure(data->ref, &refs[3]);
23412342
res = PyRun_SimpleString(THREAD_CODE);
2342-
PyThreadState_Release();
2343+
PyThreadState_Release(refs[3]);
23432344
PyGILState_Release(gstate);
2344-
PyThreadState_Release();
2345-
PyThreadState_Release();
2345+
PyThreadState_Release(refs[2]);
2346+
PyThreadState_Release(refs[1]);
23462347
assert(res == 0);
2347-
PyThreadState_Release();
2348+
PyThreadState_Release(refs[0]);
23482349
PyInterpreterRef_Close(data->ref);
23492350
data->done = 1;
23502351
}

Python/pystate.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3319,10 +3319,11 @@ PyInterpreterRef_Main(PyInterpreterRef *strong_ptr)
33193319
}
33203320

33213321
int
3322-
PyThreadState_Ensure(PyInterpreterRef interp_ref)
3322+
PyThreadState_Ensure(PyInterpreterRef interp_ref, PyThreadRef *thread_ref)
33233323
{
33243324
PyInterpreterState *interp = ref_as_interp(interp_ref);
33253325
PyThreadState *attached_tstate = current_fast_get();
3326+
*thread_ref = 0;
33263327
if (attached_tstate != NULL && attached_tstate->interp == interp) {
33273328
/* Yay! We already have an attached thread state that matches. */
33283329
++attached_tstate->ensure.counter;
@@ -3347,7 +3348,7 @@ PyThreadState_Ensure(PyInterpreterRef interp_ref)
33473348
fresh_tstate->ensure.delete_on_release = 1;
33483349

33493350
if (attached_tstate != NULL) {
3350-
fresh_tstate->ensure.prior_tstate = PyThreadState_Swap(fresh_tstate);
3351+
*thread_ref = (PyThreadRef)PyThreadState_Swap(fresh_tstate);
33513352
} else {
33523353
_PyThreadState_Attach(fresh_tstate);
33533354
}
@@ -3356,15 +3357,17 @@ PyThreadState_Ensure(PyInterpreterRef interp_ref)
33563357
}
33573358

33583359
void
3359-
PyThreadState_Release(void)
3360+
PyThreadState_Release(PyThreadRef thread_ref)
33603361
{
33613362
PyThreadState *tstate = current_fast_get();
33623363
_Py_EnsureTstateNotNULL(tstate);
33633364
Py_ssize_t remaining = --tstate->ensure.counter;
33643365
if (remaining < 0) {
33653366
Py_FatalError("PyThreadState_Release() called more times than PyThreadState_Ensure()");
33663367
}
3367-
PyThreadState *to_restore = tstate->ensure.prior_tstate;
3368+
// The thread reference might be NULL
3369+
assert(thread_ref >= 0);
3370+
PyThreadState *to_restore = (PyThreadState *)thread_ref;
33683371
if (remaining == 0) {
33693372
if (tstate->ensure.delete_on_release) {
33703373
PyThreadState_Clear(tstate);

0 commit comments

Comments
 (0)