Skip to content

Commit 178861b

Browse files
authored
gh-111623: Add Support for Cross-interpreter tuples (gh-111628)
1 parent 70afb8d commit 178861b

6 files changed

+134
-2
lines changed

Lib/test/test__xxsubinterpreters.py

+28
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ def test_default_shareables(self):
105105
True,
106106
False,
107107
100.0,
108+
(1, ('spam', 'eggs')),
108109
]
109110
for obj in shareables:
110111
with self.subTest(obj):
@@ -195,6 +196,33 @@ def test_bool(self):
195196
def test_float(self):
196197
self._assert_values([0.0, 1.1, -1.0, 0.12345678, -0.12345678])
197198

199+
def test_tuple(self):
200+
self._assert_values([(), (1,), ("hello", "world", ), (1, True, "hello")])
201+
# Test nesting
202+
self._assert_values([
203+
((1,),),
204+
((1, 2), (3, 4)),
205+
((1, 2), (3, 4), (5, 6)),
206+
])
207+
208+
def test_tuples_containing_non_shareable_types(self):
209+
non_shareables = [
210+
Exception(),
211+
object(),
212+
]
213+
for s in non_shareables:
214+
value = tuple([0, 1.0, s])
215+
with self.subTest(repr(value)):
216+
# XXX Assert the NotShareableError when it is exported
217+
with self.assertRaises(ValueError):
218+
_testinternalcapi.get_crossinterp_data(value)
219+
# Check nested as well
220+
value = tuple([0, 1., (s,)])
221+
with self.subTest("nested " + repr(value)):
222+
# XXX Assert the NotShareableError when it is exported
223+
with self.assertRaises(ValueError):
224+
_testinternalcapi.get_crossinterp_data(value)
225+
198226

199227
class ModuleTests(TestBase):
200228

Lib/test/test_interpreters.py

+2
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,8 @@ def test_default_shareables(self):
781781
True,
782782
False,
783783
100.0,
784+
(),
785+
(1, ('spam', 'eggs'), True),
784786
]
785787
for obj in shareables:
786788
with self.subTest(obj):
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
Added support for sharing of bool type with interpreters API.
1+
Add support for sharing of True and False between interpreters using the cross-interpreter
2+
API. Patch by Anthony Shaw.
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
Added support for sharing of float type with interpreters API.
1+
Add support for sharing floats between interpreters using the cross-interpreter
2+
API. Patch by Anthony Shaw.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add support for sharing tuples between interpreters using the cross-interpreter
2+
API. Patch by Anthony Shaw.

Python/crossinterp.c

+98
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,99 @@ _bool_shared(PyThreadState *tstate, PyObject *obj,
713713
return 0;
714714
}
715715

716+
struct _shared_tuple_data {
717+
Py_ssize_t len;
718+
_PyCrossInterpreterData **data;
719+
};
720+
721+
static PyObject *
722+
_new_tuple_object(_PyCrossInterpreterData *data)
723+
{
724+
struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(data->data);
725+
PyObject *tuple = PyTuple_New(shared->len);
726+
if (tuple == NULL) {
727+
return NULL;
728+
}
729+
730+
for (Py_ssize_t i = 0; i < shared->len; i++) {
731+
PyObject *item = _PyCrossInterpreterData_NewObject(shared->data[i]);
732+
if (item == NULL){
733+
Py_DECREF(tuple);
734+
return NULL;
735+
}
736+
PyTuple_SET_ITEM(tuple, i, item);
737+
}
738+
return tuple;
739+
}
740+
741+
static void
742+
_tuple_shared_free(void* data)
743+
{
744+
struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(data);
745+
#ifndef NDEBUG
746+
int64_t interpid = PyInterpreterState_GetID(_PyInterpreterState_GET());
747+
#endif
748+
for (Py_ssize_t i = 0; i < shared->len; i++) {
749+
if (shared->data[i] != NULL) {
750+
assert(shared->data[i]->interpid == interpid);
751+
_PyCrossInterpreterData_Release(shared->data[i]);
752+
PyMem_RawFree(shared->data[i]);
753+
shared->data[i] = NULL;
754+
}
755+
}
756+
PyMem_Free(shared->data);
757+
PyMem_RawFree(shared);
758+
}
759+
760+
static int
761+
_tuple_shared(PyThreadState *tstate, PyObject *obj,
762+
_PyCrossInterpreterData *data)
763+
{
764+
Py_ssize_t len = PyTuple_GET_SIZE(obj);
765+
if (len < 0) {
766+
return -1;
767+
}
768+
struct _shared_tuple_data *shared = PyMem_RawMalloc(sizeof(struct _shared_tuple_data));
769+
if (shared == NULL){
770+
PyErr_NoMemory();
771+
return -1;
772+
}
773+
774+
shared->len = len;
775+
shared->data = (_PyCrossInterpreterData **) PyMem_Calloc(shared->len, sizeof(_PyCrossInterpreterData *));
776+
if (shared->data == NULL) {
777+
PyErr_NoMemory();
778+
return -1;
779+
}
780+
781+
for (Py_ssize_t i = 0; i < shared->len; i++) {
782+
_PyCrossInterpreterData *data = _PyCrossInterpreterData_New();
783+
if (data == NULL) {
784+
goto error; // PyErr_NoMemory already set
785+
}
786+
PyObject *item = PyTuple_GET_ITEM(obj, i);
787+
788+
int res = -1;
789+
if (!_Py_EnterRecursiveCallTstate(tstate, " while sharing a tuple")) {
790+
res = _PyObject_GetCrossInterpreterData(item, data);
791+
_Py_LeaveRecursiveCallTstate(tstate);
792+
}
793+
if (res < 0) {
794+
PyMem_RawFree(data);
795+
goto error;
796+
}
797+
shared->data[i] = data;
798+
}
799+
_PyCrossInterpreterData_Init(
800+
data, tstate->interp, shared, obj, _new_tuple_object);
801+
data->free = _tuple_shared_free;
802+
return 0;
803+
804+
error:
805+
_tuple_shared_free(shared);
806+
return -1;
807+
}
808+
716809
static void
717810
_register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry)
718811
{
@@ -745,6 +838,11 @@ _register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry)
745838
if (_xidregistry_add_type(xidregistry, &PyFloat_Type, _float_shared) != 0) {
746839
Py_FatalError("could not register float for cross-interpreter sharing");
747840
}
841+
842+
// tuple
843+
if (_xidregistry_add_type(xidregistry, &PyTuple_Type, _tuple_shared) != 0) {
844+
Py_FatalError("could not register tuple for cross-interpreter sharing");
845+
}
748846
}
749847

750848
/* registry lifecycle */

0 commit comments

Comments
 (0)