Skip to content

Commit b05b711

Browse files
bpo-33608: Use _Py_AddPendingCall() in _PyCrossInterpreterData_Release(). (gh-12024)
1 parent cfe172d commit b05b711

File tree

1 file changed

+42
-39
lines changed

1 file changed

+42
-39
lines changed

Python/pystate.c

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -328,28 +328,39 @@ PyInterpreterState_GetID(PyInterpreterState *interp)
328328
}
329329

330330

331-
PyInterpreterState *
332-
_PyInterpreterState_LookUpID(PY_INT64_T requested_id)
331+
static PyInterpreterState *
332+
interp_look_up_id(PY_INT64_T requested_id)
333333
{
334-
if (requested_id < 0)
335-
goto error;
336-
337334
PyInterpreterState *interp = PyInterpreterState_Head();
338335
while (interp != NULL) {
339336
PY_INT64_T id = PyInterpreterState_GetID(interp);
340-
if (id < 0)
337+
if (id < 0) {
341338
return NULL;
342-
if (requested_id == id)
339+
}
340+
if (requested_id == id) {
343341
return interp;
342+
}
344343
interp = PyInterpreterState_Next(interp);
345344
}
346-
347-
error:
348-
PyErr_Format(PyExc_RuntimeError,
349-
"unrecognized interpreter ID %lld", requested_id);
350345
return NULL;
351346
}
352347

348+
PyInterpreterState *
349+
_PyInterpreterState_LookUpID(PY_INT64_T requested_id)
350+
{
351+
PyInterpreterState *interp = NULL;
352+
if (requested_id >= 0) {
353+
HEAD_UNLOCK();
354+
interp = interp_look_up_id(requested_id);
355+
HEAD_UNLOCK();
356+
}
357+
if (interp == NULL && !PyErr_Occurred()) {
358+
PyErr_Format(PyExc_RuntimeError,
359+
"unrecognized interpreter ID %lld", requested_id);
360+
}
361+
return interp;
362+
}
363+
353364

354365
int
355366
_PyInterpreterState_IDInitref(PyInterpreterState *interp)
@@ -1280,38 +1291,16 @@ _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data)
12801291
return 0;
12811292
}
12821293

1283-
static void
1294+
static int
12841295
_release_xidata(void *arg)
12851296
{
12861297
_PyCrossInterpreterData *data = (_PyCrossInterpreterData *)arg;
12871298
if (data->free != NULL) {
12881299
data->free(data->data);
12891300
}
12901301
Py_XDECREF(data->obj);
1291-
}
1292-
1293-
static void
1294-
_call_in_interpreter(PyInterpreterState *interp,
1295-
void (*func)(void *), void *arg)
1296-
{
1297-
/* We would use Py_AddPendingCall() if it weren't specific to the
1298-
* main interpreter (see bpo-33608). In the meantime we take a
1299-
* naive approach.
1300-
*/
1301-
PyThreadState *save_tstate = NULL;
1302-
if (interp != _PyInterpreterState_Get()) {
1303-
// XXX Using the "head" thread isn't strictly correct.
1304-
PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
1305-
// XXX Possible GILState issues?
1306-
save_tstate = PyThreadState_Swap(tstate);
1307-
}
1308-
1309-
func(arg);
1310-
1311-
// Switch back.
1312-
if (save_tstate != NULL) {
1313-
PyThreadState_Swap(save_tstate);
1314-
}
1302+
PyMem_Free(data);
1303+
return 0;
13151304
}
13161305

13171306
void
@@ -1322,7 +1311,7 @@ _PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
13221311
return;
13231312
}
13241313

1325-
// Switch to the original interpreter.
1314+
// Get the original interpreter.
13261315
PyInterpreterState *interp = _PyInterpreterState_LookUpID(data->interp);
13271316
if (interp == NULL) {
13281317
// The intepreter was already destroyed.
@@ -1331,10 +1320,24 @@ _PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
13311320
}
13321321
return;
13331322
}
1323+
// XXX There's an ever-so-slight race here...
1324+
if (interp->finalizing) {
1325+
// XXX Someone leaked some memory...
1326+
return;
1327+
}
13341328

13351329
// "Release" the data and/or the object.
1336-
// XXX Use _Py_AddPendingCall().
1337-
_call_in_interpreter(interp, _release_xidata, data);
1330+
_PyCrossInterpreterData *copied = PyMem_Malloc(sizeof(_PyCrossInterpreterData));
1331+
if (copied == NULL) {
1332+
PyErr_SetString(PyExc_MemoryError,
1333+
"Not enough memory to preserve cross-interpreter data");
1334+
PyErr_Print();
1335+
return;
1336+
}
1337+
memcpy(copied, data, sizeof(_PyCrossInterpreterData));
1338+
if (_Py_AddPendingCall(interp, 0, _release_xidata, copied) != 0) {
1339+
// XXX Queue full or couldn't get lock. Try again somehow?
1340+
}
13381341
}
13391342

13401343
PyObject *

0 commit comments

Comments
 (0)