Skip to content

Commit d0895f9

Browse files
committed
Add a test for interpreter reference counts.
1 parent 1828b9a commit d0895f9

File tree

3 files changed

+39
-5
lines changed

3 files changed

+39
-5
lines changed

Include/cpython/pystate.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,10 @@ PyAPI_FUNC(void) _PyInterpreterState_SetEvalFrameFunc(
270270
* incremented reference count. PyInterpreterState_Delete() won't delete the
271271
* full interpreter structure until the reference is released by
272272
* PyThreadState_Ensure() or PyInterpreterState_Release(). */
273-
PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_Hold();
273+
PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_Hold(void);
274274

275275
/* Release a reference to an interpreter incremented by PyInterpreterState_Hold() */
276276
PyAPI_FUNC(void) PyInterpreterState_Release(PyInterpreterState *interp);
277+
278+
// Export for '_testcapi' shared extension
279+
PyAPI_FUNC(Py_ssize_t) _PyInterpreterState_Refcount(PyInterpreterState *interp);

Modules/_testcapimodule.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2546,6 +2546,30 @@ toggle_reftrace_printer(PyObject *ob, PyObject *arg)
25462546
Py_RETURN_NONE;
25472547
}
25482548

2549+
static PyObject *
2550+
test_interp_refcount(PyObject *self, PyObject *unused)
2551+
{
2552+
PyThreadState *save = PyThreadState_Get();
2553+
PyThreadState *tstate = Py_NewInterpreter();
2554+
assert(tstate == PyThreadState_Get());
2555+
PyInterpreterState *interp = PyThreadState_GetInterpreter(tstate);
2556+
assert(interp != NULL);
2557+
2558+
assert(_PyInterpreterState_Refcount(interp) == 1);
2559+
PyInterpreterState *held = PyInterpreterState_Hold();
2560+
assert(_PyInterpreterState_Refcount(interp) == 2);
2561+
PyInterpreterState_Release(held);
2562+
assert(_PyInterpreterState_Refcount(interp) == 1);
2563+
2564+
held = PyInterpreterState_Hold();
2565+
Py_EndInterpreter(tstate);
2566+
PyThreadState_Swap(save);
2567+
assert(_PyInterpreterState_Refcount(interp) == 1);
2568+
PyInterpreterState_Release(held);
2569+
2570+
Py_RETURN_NONE;
2571+
}
2572+
25492573
static PyMethodDef TestMethods[] = {
25502574
{"set_errno", set_errno, METH_VARARGS},
25512575
{"test_config", test_config, METH_NOARGS},
@@ -2640,6 +2664,7 @@ static PyMethodDef TestMethods[] = {
26402664
{"test_atexit", test_atexit, METH_NOARGS},
26412665
{"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL},
26422666
{"toggle_reftrace_printer", toggle_reftrace_printer, METH_O},
2667+
{"test_interp_refcount", test_interp_refcount, METH_NOARGS},
26432668
{NULL, NULL} /* sentinel */
26442669
};
26452670

Python/pystate.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3196,6 +3196,15 @@ _Py_GetMainConfig(void)
31963196
return _PyInterpreterState_GetConfig(interp);
31973197
}
31983198

3199+
Py_ssize_t
3200+
_PyInterpreterState_Refcount(PyInterpreterState *interp)
3201+
{
3202+
assert(interp != NULL);
3203+
Py_ssize_t refcount = _Py_atomic_load_ssize_relaxed(&interp->refcount);
3204+
assert(refcount > 0);
3205+
return refcount;
3206+
}
3207+
31993208
PyInterpreterState *
32003209
PyInterpreterState_Hold(void)
32013210
{
@@ -3208,9 +3217,6 @@ PyInterpreterState_Hold(void)
32083217
void
32093218
PyInterpreterState_Release(PyInterpreterState *interp)
32103219
{
3211-
PyThreadState *tstate = PyThreadState_Get();
3212-
if (tstate->interp != interp) {
3213-
Py_FatalError("thread state has the wrong interpreter");
3214-
}
3220+
assert(interp != NULL);
32153221
decref_interpreter(interp);
32163222
}

0 commit comments

Comments
 (0)