Skip to content

Commit

Permalink
pythongh-111623: Add Support for Cross-interpreter tuples (pythongh-1…
Browse files Browse the repository at this point in the history
  • Loading branch information
tonybaloney authored and aisk committed Feb 11, 2024
1 parent e886d15 commit 1972f02
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 2 deletions.
28 changes: 28 additions & 0 deletions Lib/test/test__xxsubinterpreters.py
Expand Up @@ -105,6 +105,7 @@ def test_default_shareables(self):
True,
False,
100.0,
(1, ('spam', 'eggs')),
]
for obj in shareables:
with self.subTest(obj):
Expand Down Expand Up @@ -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):

Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_interpreters.py
Expand Up @@ -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):
Expand Down
@@ -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.
@@ -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.
@@ -0,0 +1,2 @@
Add support for sharing tuples between interpreters using the cross-interpreter
API. Patch by Anthony Shaw.
98 changes: 98 additions & 0 deletions Python/crossinterp.c
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 */
Expand Down

0 comments on commit 1972f02

Please sign in to comment.