gh-111623: Add Support for Cross-interpreter tuples (gh-111628)

This commit is contained in:
Anthony Shaw 2023-11-08 02:58:29 +09:00 committed by GitHub
parent 70afb8d732
commit 178861b193
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 134 additions and 2 deletions

View file

@ -105,6 +105,7 @@ def test_default_shareables(self):
True,
False,
100.0,
(1, ('spam', 'eggs')),
]
for obj in shareables:
with self.subTest(obj):
@ -195,6 +196,33 @@ def test_bool(self):
def test_float(self):
self._assert_values([0.0, 1.1, -1.0, 0.12345678, -0.12345678])
def test_tuple(self):
self._assert_values([(), (1,), ("hello", "world", ), (1, True, "hello")])
# Test nesting
self._assert_values([
((1,),),
((1, 2), (3, 4)),
((1, 2), (3, 4), (5, 6)),
])
def test_tuples_containing_non_shareable_types(self):
non_shareables = [
Exception(),
object(),
]
for s in non_shareables:
value = tuple([0, 1.0, s])
with self.subTest(repr(value)):
# XXX Assert the NotShareableError when it is exported
with self.assertRaises(ValueError):
_testinternalcapi.get_crossinterp_data(value)
# Check nested as well
value = tuple([0, 1., (s,)])
with self.subTest("nested " + repr(value)):
# XXX Assert the NotShareableError when it is exported
with self.assertRaises(ValueError):
_testinternalcapi.get_crossinterp_data(value)
class ModuleTests(TestBase):

View file

@ -781,6 +781,8 @@ def test_default_shareables(self):
True,
False,
100.0,
(),
(1, ('spam', 'eggs'), True),
]
for obj in shareables:
with self.subTest(obj):

View file

@ -1 +1,2 @@
Added support for sharing of bool type with interpreters API.
Add support for sharing of True and False between interpreters using the cross-interpreter
API. Patch by Anthony Shaw.

View file

@ -1 +1,2 @@
Added support for sharing of float type with interpreters API.
Add support for sharing floats between interpreters using the cross-interpreter
API. Patch by Anthony Shaw.

View file

@ -0,0 +1,2 @@
Add support for sharing tuples between interpreters using the cross-interpreter
API. Patch by Anthony Shaw.

View file

@ -713,6 +713,99 @@ _bool_shared(PyThreadState *tstate, PyObject *obj,
return 0;
}
struct _shared_tuple_data {
Py_ssize_t len;
_PyCrossInterpreterData **data;
};
static PyObject *
_new_tuple_object(_PyCrossInterpreterData *data)
{
struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(data->data);
PyObject *tuple = PyTuple_New(shared->len);
if (tuple == NULL) {
return NULL;
}
for (Py_ssize_t i = 0; i < shared->len; i++) {
PyObject *item = _PyCrossInterpreterData_NewObject(shared->data[i]);
if (item == NULL){
Py_DECREF(tuple);
return NULL;
}
PyTuple_SET_ITEM(tuple, i, item);
}
return tuple;
}
static void
_tuple_shared_free(void* data)
{
struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(data);
#ifndef NDEBUG
int64_t interpid = PyInterpreterState_GetID(_PyInterpreterState_GET());
#endif
for (Py_ssize_t i = 0; i < shared->len; i++) {
if (shared->data[i] != NULL) {
assert(shared->data[i]->interpid == interpid);
_PyCrossInterpreterData_Release(shared->data[i]);
PyMem_RawFree(shared->data[i]);
shared->data[i] = NULL;
}
}
PyMem_Free(shared->data);
PyMem_RawFree(shared);
}
static int
_tuple_shared(PyThreadState *tstate, PyObject *obj,
_PyCrossInterpreterData *data)
{
Py_ssize_t len = PyTuple_GET_SIZE(obj);
if (len < 0) {
return -1;
}
struct _shared_tuple_data *shared = PyMem_RawMalloc(sizeof(struct _shared_tuple_data));
if (shared == NULL){
PyErr_NoMemory();
return -1;
}
shared->len = len;
shared->data = (_PyCrossInterpreterData **) PyMem_Calloc(shared->len, sizeof(_PyCrossInterpreterData *));
if (shared->data == NULL) {
PyErr_NoMemory();
return -1;
}
for (Py_ssize_t i = 0; i < shared->len; i++) {
_PyCrossInterpreterData *data = _PyCrossInterpreterData_New();
if (data == NULL) {
goto error; // PyErr_NoMemory already set
}
PyObject *item = PyTuple_GET_ITEM(obj, i);
int res = -1;
if (!_Py_EnterRecursiveCallTstate(tstate, " while sharing a tuple")) {
res = _PyObject_GetCrossInterpreterData(item, data);
_Py_LeaveRecursiveCallTstate(tstate);
}
if (res < 0) {
PyMem_RawFree(data);
goto error;
}
shared->data[i] = data;
}
_PyCrossInterpreterData_Init(
data, tstate->interp, shared, obj, _new_tuple_object);
data->free = _tuple_shared_free;
return 0;
error:
_tuple_shared_free(shared);
return -1;
}
static void
_register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry)
{
@ -745,6 +838,11 @@ _register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry)
if (_xidregistry_add_type(xidregistry, &PyFloat_Type, _float_shared) != 0) {
Py_FatalError("could not register float for cross-interpreter sharing");
}
// tuple
if (_xidregistry_add_type(xidregistry, &PyTuple_Type, _tuple_shared) != 0) {
Py_FatalError("could not register tuple for cross-interpreter sharing");
}
}
/* registry lifecycle */