mirror of
https://github.com/python/cpython.git
synced 2025-10-21 22:22:48 +00:00
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:
parent
ab02262cd0
commit
0547a981ae
4 changed files with 659 additions and 317 deletions
|
@ -394,7 +394,7 @@ struct _xid {
|
||||||
|
|
||||||
PyAPI_FUNC(int) _PyObject_GetCrossInterpreterData(PyObject *, _PyCrossInterpreterData *);
|
PyAPI_FUNC(int) _PyObject_GetCrossInterpreterData(PyObject *, _PyCrossInterpreterData *);
|
||||||
PyAPI_FUNC(PyObject *) _PyCrossInterpreterData_NewObject(_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 *);
|
PyAPI_FUNC(int) _PyObject_CheckCrossInterpreterData(PyObject *);
|
||||||
|
|
||||||
|
|
|
@ -386,7 +386,6 @@ class ShareableTypeTests(unittest.TestCase):
|
||||||
self._assert_values([
|
self._assert_values([
|
||||||
b'spam',
|
b'spam',
|
||||||
9999,
|
9999,
|
||||||
self.cid,
|
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_bytes(self):
|
def test_bytes(self):
|
||||||
|
@ -1213,6 +1212,18 @@ class ChannelIDTests(TestBase):
|
||||||
self.assertFalse(cid1 != cid2)
|
self.assertFalse(cid1 != cid2)
|
||||||
self.assertTrue(cid1 != cid3)
|
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):
|
class ChannelTests(TestBase):
|
||||||
|
|
||||||
|
@ -1545,6 +1556,19 @@ class ChannelTests(TestBase):
|
||||||
self.assertEqual(obj5, b'eggs')
|
self.assertEqual(obj5, b'eggs')
|
||||||
self.assertIs(obj6, default)
|
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):
|
def test_run_string_arg_unresolved(self):
|
||||||
cid = interpreters.channel_create()
|
cid = interpreters.channel_create()
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1865,7 +1865,7 @@ _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data)
|
||||||
// Fill in the blanks and validate the result.
|
// Fill in the blanks and validate the result.
|
||||||
data->interp = interp->id;
|
data->interp = interp->id;
|
||||||
if (_check_xidata(tstate, data) != 0) {
|
if (_check_xidata(tstate, data) != 0) {
|
||||||
_PyCrossInterpreterData_Release(data);
|
(void)_PyCrossInterpreterData_Release(data);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1878,8 +1878,8 @@ _release_xidata(void *arg)
|
||||||
_PyCrossInterpreterData *data = (_PyCrossInterpreterData *)arg;
|
_PyCrossInterpreterData *data = (_PyCrossInterpreterData *)arg;
|
||||||
if (data->free != NULL) {
|
if (data->free != NULL) {
|
||||||
data->free(data->data);
|
data->free(data->data);
|
||||||
data->data = NULL;
|
|
||||||
}
|
}
|
||||||
|
data->data = NULL;
|
||||||
Py_CLEAR(data->obj);
|
Py_CLEAR(data->obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1910,27 +1910,29 @@ _call_in_interpreter(struct _gilstate_runtime_state *gilstate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
int
|
||||||
_PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
|
_PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
|
||||||
{
|
{
|
||||||
if (data->data == NULL && data->obj == NULL) {
|
if (data->free == NULL && data->obj == NULL) {
|
||||||
// Nothing to release!
|
// Nothing to release!
|
||||||
return;
|
data->data = NULL;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch to the original interpreter.
|
// Switch to the original interpreter.
|
||||||
PyInterpreterState *interp = _PyInterpreterState_LookUpID(data->interp);
|
PyInterpreterState *interp = _PyInterpreterState_LookUpID(data->interp);
|
||||||
if (interp == NULL) {
|
if (interp == NULL) {
|
||||||
// The interpreter was already destroyed.
|
// The interpreter was already destroyed.
|
||||||
if (data->free != NULL) {
|
// This function shouldn't have been called.
|
||||||
// XXX Someone leaked some memory...
|
// XXX Someone leaked some memory...
|
||||||
}
|
assert(PyErr_Occurred());
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Release" the data and/or the object.
|
// "Release" the data and/or the object.
|
||||||
struct _gilstate_runtime_state *gilstate = &_PyRuntime.gilstate;
|
struct _gilstate_runtime_state *gilstate = &_PyRuntime.gilstate;
|
||||||
_call_in_interpreter(gilstate, interp, _release_xidata, data);
|
_call_in_interpreter(gilstate, interp, _release_xidata, data);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue