gh-99741: Clean Up the _xxsubinterpreters Module (gh-99940)

This cleanup up resolves a few subtle bugs and makes the implementation for multi-phase init much cleaner.

https://github.com/python/cpython/issues/99741
This commit is contained in:
Eric Snow 2022-12-02 11:36:57 -07:00 committed by GitHub
parent ab02262cd0
commit 0547a981ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 659 additions and 317 deletions

View file

@ -394,7 +394,7 @@ struct _xid {
PyAPI_FUNC(int) _PyObject_GetCrossInterpreterData(PyObject *, _PyCrossInterpreterData *);
PyAPI_FUNC(PyObject *) _PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *);
PyAPI_FUNC(void) _PyCrossInterpreterData_Release(_PyCrossInterpreterData *);
PyAPI_FUNC(int) _PyCrossInterpreterData_Release(_PyCrossInterpreterData *);
PyAPI_FUNC(int) _PyObject_CheckCrossInterpreterData(PyObject *);

View file

@ -386,7 +386,6 @@ def test_types(self):
self._assert_values([
b'spam',
9999,
self.cid,
])
def test_bytes(self):
@ -1213,6 +1212,18 @@ def test_equality(self):
self.assertFalse(cid1 != cid2)
self.assertTrue(cid1 != cid3)
def test_shareable(self):
chan = interpreters.channel_create()
obj = interpreters.channel_create()
interpreters.channel_send(chan, obj)
got = interpreters.channel_recv(chan)
self.assertEqual(got, obj)
self.assertIs(type(got), type(obj))
# XXX Check the following in the channel tests?
#self.assertIsNot(got, obj)
class ChannelTests(TestBase):
@ -1545,6 +1556,19 @@ def test_recv_default(self):
self.assertEqual(obj5, b'eggs')
self.assertIs(obj6, default)
def test_recv_sending_interp_destroyed(self):
cid = interpreters.channel_create()
interp = interpreters.create()
interpreters.run_string(interp, dedent(f"""
import _xxsubinterpreters as _interpreters
_interpreters.channel_send({cid}, b'spam')
"""))
interpreters.destroy(interp)
with self.assertRaisesRegex(RuntimeError,
'unrecognized interpreter ID'):
interpreters.channel_recv(cid)
def test_run_string_arg_unresolved(self):
cid = interpreters.channel_create()
interp = interpreters.create()

File diff suppressed because it is too large Load diff

View file

@ -1865,7 +1865,7 @@ _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data)
// Fill in the blanks and validate the result.
data->interp = interp->id;
if (_check_xidata(tstate, data) != 0) {
_PyCrossInterpreterData_Release(data);
(void)_PyCrossInterpreterData_Release(data);
return -1;
}
@ -1878,8 +1878,8 @@ _release_xidata(void *arg)
_PyCrossInterpreterData *data = (_PyCrossInterpreterData *)arg;
if (data->free != NULL) {
data->free(data->data);
data->data = NULL;
}
data->data = NULL;
Py_CLEAR(data->obj);
}
@ -1910,27 +1910,29 @@ _call_in_interpreter(struct _gilstate_runtime_state *gilstate,
}
}
void
int
_PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
{
if (data->data == NULL && data->obj == NULL) {
if (data->free == NULL && data->obj == NULL) {
// Nothing to release!
return;
data->data = NULL;
return 0;
}
// Switch to the original interpreter.
PyInterpreterState *interp = _PyInterpreterState_LookUpID(data->interp);
if (interp == NULL) {
// The interpreter was already destroyed.
if (data->free != NULL) {
// XXX Someone leaked some memory...
}
return;
// This function shouldn't have been called.
// XXX Someone leaked some memory...
assert(PyErr_Occurred());
return -1;
}
// "Release" the data and/or the object.
struct _gilstate_runtime_state *gilstate = &_PyRuntime.gilstate;
_call_in_interpreter(gilstate, interp, _release_xidata, data);
return 0;
}
PyObject *