]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-111623: Add Support for Cross-interpreter tuples (gh-111628)
authorAnthony Shaw <anthony.p.shaw@gmail.com>
Tue, 7 Nov 2023 17:58:29 +0000 (02:58 +0900)
committerGitHub <noreply@github.com>
Tue, 7 Nov 2023 17:58:29 +0000 (10:58 -0700)
Lib/test/test__xxsubinterpreters.py
Lib/test/test_interpreters.py
Misc/NEWS.d/next/Core and Builtins/2023-10-29-11-35-21.gh-issue-111435.ageUWQ.rst
Misc/NEWS.d/next/Core and Builtins/2023-10-29-12-33-33.gh-issue-111438.bHTLLl.rst
Misc/NEWS.d/next/Core and Builtins/2023-11-02-15-00-57.gh-issue-111623.BZxYc8.rst [new file with mode: 0644]
Python/crossinterp.c

index ae7dfa19acc519381a1835a7319ad37451dacb6f..97314ddbb55ec8364a0b10c7cbe69a2ddbfd7b3d 100644 (file)
@@ -105,6 +105,7 @@ class IsShareableTests(unittest.TestCase):
                 True,
                 False,
                 100.0,
+                (1, ('spam', 'eggs')),
                 ]
         for obj in shareables:
             with self.subTest(obj):
@@ -195,6 +196,33 @@ class ShareableTypeTests(unittest.TestCase):
     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):
 
index 74f86088b455905ba63507434780bc65016624b0..7c030bcf0321cde0bc124cd13615cce2f5f60a65 100644 (file)
@@ -781,6 +781,8 @@ class TestIsShareable(TestBase):
                 True,
                 False,
                 100.0,
+                (),
+                (1, ('spam', 'eggs'), True),
                 ]
         for obj in shareables:
             with self.subTest(obj):
index 95044dc3b96660a25cca066673d573105d0413c6..034615581b6789a1f4bf3e4f1c9d5206fb5fddb7 100644 (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.
index b181977d8d195cd6467fcf5cf2ecf4a0deafa879..009ba11ae166830c5e0cd43acfc8714276aa0cc3 100644 (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.
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 (file)
index 0000000..3a75d5e
--- /dev/null
@@ -0,0 +1,2 @@
+Add support for sharing tuples between interpreters using the cross-interpreter
+API.  Patch by Anthony Shaw.
index de28cb7071740ab00f434ddd7934ff1987895cfa..a908f9ae340ee93a187df36299746381df926966 100644 (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 */