From 1972f0284a0d2094384a6d79d2f9ffc6a3658f12 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Wed, 8 Nov 2023 02:58:29 +0900 Subject: [PATCH] gh-111623: Add Support for Cross-interpreter tuples (gh-111628) --- Lib/test/test__xxsubinterpreters.py | 28 ++++++ Lib/test/test_interpreters.py | 2 + ...-10-29-11-35-21.gh-issue-111435.ageUWQ.rst | 3 +- ...-10-29-12-33-33.gh-issue-111438.bHTLLl.rst | 3 +- ...-11-02-15-00-57.gh-issue-111623.BZxYc8.rst | 2 + Python/crossinterp.c | 98 +++++++++++++++++++ 6 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-02-15-00-57.gh-issue-111623.BZxYc8.rst diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index ae7dfa19acc5193..97314ddbb55ec83 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -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): diff --git a/Lib/test/test_interpreters.py b/Lib/test/test_interpreters.py index 74f86088b455905..7c030bcf0321cde 100644 --- a/Lib/test/test_interpreters.py +++ b/Lib/test/test_interpreters.py @@ -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): diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-11-35-21.gh-issue-111435.ageUWQ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-29-11-35-21.gh-issue-111435.ageUWQ.rst index 95044dc3b96660a..034615581b6789a 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-11-35-21.gh-issue-111435.ageUWQ.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-29-11-35-21.gh-issue-111435.ageUWQ.rst @@ -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. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-12-33-33.gh-issue-111438.bHTLLl.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-29-12-33-33.gh-issue-111438.bHTLLl.rst index b181977d8d195cd..009ba11ae166830 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-12-33-33.gh-issue-111438.bHTLLl.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-29-12-33-33.gh-issue-111438.bHTLLl.rst @@ -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. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-02-15-00-57.gh-issue-111623.BZxYc8.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-02-15-00-57.gh-issue-111623.BZxYc8.rst new file mode 100644 index 000000000000000..3a75d5e36841b42 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-02-15-00-57.gh-issue-111623.BZxYc8.rst @@ -0,0 +1,2 @@ +Add support for sharing tuples between interpreters using the cross-interpreter +API. Patch by Anthony Shaw. diff --git a/Python/crossinterp.c b/Python/crossinterp.c index de28cb7071740ab..a908f9ae340ee93 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -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 */