Skip to content

Commit f7723c0

Browse files
committed
Fix a few bugs and add a test.
1 parent fda9886 commit f7723c0

4 files changed

Lines changed: 55 additions & 11 deletions

File tree

Include/internal/pycore_interp_structs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,7 @@ struct _is {
797797
Py_ssize_t countdown;
798798
PyEvent finished;
799799
PyMutex mutex;
800+
int shutting_down;
800801
} finalizing;
801802
} threads;
802803

Modules/_testcapimodule.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2571,6 +2571,26 @@ test_interp_refcount(PyObject *self, PyObject *unused)
25712571
Py_RETURN_NONE;
25722572
}
25732573

2574+
static PyObject *
2575+
test_interp_lookup(PyObject *self, PyObject *unused)
2576+
{
2577+
PyInterpreterState *interp = PyInterpreterState_Get();
2578+
assert(_PyInterpreterState_Refcount(interp) == 0);
2579+
int64_t interp_id = PyInterpreterState_GetID(interp);
2580+
PyInterpreterState *ref = PyInterpreterState_Lookup(interp_id);
2581+
assert(ref == interp);
2582+
assert(_PyInterpreterState_Refcount(interp) == 1);
2583+
PyInterpreterState_Release(ref);
2584+
assert(PyInterpreterState_Lookup(10000) == NULL);
2585+
Py_BEGIN_ALLOW_THREADS;
2586+
ref = PyInterpreterState_Lookup(interp_id);
2587+
assert(ref == interp);
2588+
PyInterpreterState_Release(ref);
2589+
Py_END_ALLOW_THREADS;
2590+
2591+
Py_RETURN_NONE;
2592+
}
2593+
25742594
static PyMethodDef TestMethods[] = {
25752595
{"set_errno", set_errno, METH_VARARGS},
25762596
{"test_config", test_config, METH_NOARGS},
@@ -2666,6 +2686,7 @@ static PyMethodDef TestMethods[] = {
26662686
{"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL},
26672687
{"toggle_reftrace_printer", toggle_reftrace_printer, METH_O},
26682688
{"test_interp_refcount", test_interp_refcount, METH_NOARGS},
2689+
{"test_interp_lookup", test_interp_lookup, METH_NOARGS},
26692690
{NULL, NULL} /* sentinel */
26702691
};
26712692

Python/pylifecycle.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3469,6 +3469,7 @@ wait_for_native_shutdown(PyInterpreterState *interp)
34693469
{
34703470
assert(interp != NULL);
34713471
struct _Py_finalizing_threads *finalizing = &interp->threads.finalizing;
3472+
_Py_atomic_store_int_release(&finalizing->shutting_down, 1);
34723473
PyMutex_Lock(&finalizing->mutex);
34733474
if (_Py_atomic_load_ssize_relaxed(&finalizing->countdown) == 0) {
34743475
// Nothing to do.

Python/pystate.c

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,12 +1373,8 @@ interp_look_up_id(_PyRuntimeState *runtime, int64_t requested_id)
13731373
return NULL;
13741374
}
13751375

1376-
/* Return the interpreter state with the given ID.
1377-
1378-
Fail with RuntimeError if the interpreter is not found. */
1379-
13801376
PyInterpreterState *
1381-
_PyInterpreterState_LookUpID(int64_t requested_id)
1377+
_PyInterpreterState_LookUpIDNoErr(int64_t requested_id)
13821378
{
13831379
PyInterpreterState *interp = NULL;
13841380
if (requested_id >= 0) {
@@ -1387,6 +1383,18 @@ _PyInterpreterState_LookUpID(int64_t requested_id)
13871383
interp = interp_look_up_id(runtime, requested_id);
13881384
HEAD_UNLOCK(runtime);
13891385
}
1386+
return interp;
1387+
}
1388+
1389+
/* Return the interpreter state with the given ID.
1390+
1391+
Fail with RuntimeError if the interpreter is not found. */
1392+
1393+
PyInterpreterState *
1394+
_PyInterpreterState_LookUpID(int64_t requested_id)
1395+
{
1396+
assert(_PyThreadState_GET() != NULL);
1397+
PyInterpreterState *interp = _PyInterpreterState_LookUpIDNoErr(requested_id);
13901398
if (interp == NULL && !PyErr_Occurred()) {
13911399
PyErr_Format(PyExc_InterpreterNotFoundError,
13921400
"unrecognized interpreter ID %lld", requested_id);
@@ -1807,13 +1815,23 @@ PyThreadState_Clear(PyThreadState *tstate)
18071815
static void
18081816
decrement_stoptheworld_countdown(struct _stoptheworld_state *stw);
18091817

1818+
static int
1819+
shutting_down_natives(PyInterpreterState *interp)
1820+
{
1821+
assert(interp != NULL);
1822+
return _Py_atomic_load_int_relaxed(&interp->threads.finalizing.shutting_down);
1823+
}
1824+
18101825
static void
18111826
decrement_daemon_count(PyInterpreterState *interp)
18121827
{
18131828
assert(interp != NULL);
18141829
struct _Py_finalizing_threads *finalizing = &interp->threads.finalizing;
1815-
if (_Py_atomic_add_ssize(&finalizing->countdown, -1) == 1) {
1830+
Py_ssize_t old = _Py_atomic_add_ssize(&finalizing->countdown, -1);
1831+
if (old == 1 && shutting_down_natives(interp)) {
18161832
_PyEvent_Notify(&finalizing->finished);
1833+
} else if (old <= 0) {
1834+
Py_FatalError("interpreter has negative reference count");
18171835
}
18181836
}
18191837

@@ -3074,6 +3092,9 @@ _PyThreadState_CheckConsistency(PyThreadState *tstate)
30743092
int
30753093
_PyThreadState_MustExit(PyThreadState *tstate)
30763094
{
3095+
if (!tstate->daemon) {
3096+
return 0;
3097+
}
30773098
int state = _Py_atomic_load_int_relaxed(&tstate->state);
30783099
return state == _Py_THREAD_SHUTTING_DOWN;
30793100
}
@@ -3221,19 +3242,19 @@ PyInterpreterState_Hold(void)
32213242
PyInterpreterState *
32223243
PyInterpreterState_Lookup(int64_t interp_id)
32233244
{
3224-
PyInterpreterState *interp = _PyInterpreterState_LookUpID(interp_id);
3245+
PyInterpreterState *interp = _PyInterpreterState_LookUpIDNoErr(interp_id);
32253246
if (interp == NULL) {
32263247
return NULL;
32273248
}
32283249
HEAD_LOCK(&_PyRuntime); // Prevent deletion
3229-
struct _Py_finalizing_threads finalizing = interp->threads.finalizing;
3230-
PyMutex *mutex = &finalizing.mutex;
3250+
struct _Py_finalizing_threads *finalizing = &interp->threads.finalizing;
3251+
PyMutex *mutex = &finalizing->mutex;
32313252
PyMutex_Lock(mutex); // Synchronize TOCTOU with the event flag
3232-
if (_PyEvent_IsSet(&finalizing.finished)) {
3253+
if (_PyEvent_IsSet(&finalizing->finished)) {
32333254
/* Interpreter has already finished threads */
32343255
interp = NULL;
32353256
} else {
3236-
_Py_atomic_add_ssize(&finalizing.countdown, 1);
3257+
_Py_atomic_add_ssize(&finalizing->countdown, 1);
32373258
}
32383259
PyMutex_Unlock(mutex);
32393260
HEAD_UNLOCK(&_PyRuntime);

0 commit comments

Comments
 (0)