summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/cpython/pystate.h77
-rw-r--r--Include/internal/pycore_ceval.h10
-rw-r--r--Include/internal/pycore_crossinterp.h139
-rw-r--r--Include/internal/pycore_interp.h23
-rw-r--r--Include/internal/pycore_runtime.h1
-rw-r--r--Lib/test/test__xxsubinterpreters.py12
-rw-r--r--Makefile.pre.in2
-rw-r--r--Modules/_testcapimodule.c54
-rw-r--r--Modules/_testinternalcapi.c54
-rw-r--r--Modules/_xxinterpchannelsmodule.c1
-rw-r--r--Modules/_xxsubinterpretersmodule.c1
-rw-r--r--Objects/abstract.c1
-rw-r--r--PCbuild/_freeze_module.vcxproj1
-rw-r--r--PCbuild/_freeze_module.vcxproj.filters3
-rw-r--r--PCbuild/pythoncore.vcxproj2
-rw-r--r--PCbuild/pythoncore.vcxproj.filters6
-rw-r--r--Python/crossinterp.c627
-rw-r--r--Python/pystate.c595
18 files changed, 849 insertions, 760 deletions
diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h
index 995f02e..ec99f90 100644
--- a/Include/cpython/pystate.h
+++ b/Include/cpython/pystate.h
@@ -258,80 +258,3 @@ PyAPI_FUNC(_PyFrameEvalFunction) _PyInterpreterState_GetEvalFrameFunc(
PyAPI_FUNC(void) _PyInterpreterState_SetEvalFrameFunc(
PyInterpreterState *interp,
_PyFrameEvalFunction eval_frame);
-
-
-/* cross-interpreter data */
-
-// _PyCrossInterpreterData is similar to Py_buffer as an effectively
-// opaque struct that holds data outside the object machinery. This
-// is necessary to pass safely between interpreters in the same process.
-typedef struct _xid _PyCrossInterpreterData;
-
-typedef PyObject *(*xid_newobjectfunc)(_PyCrossInterpreterData *);
-typedef void (*xid_freefunc)(void *);
-
-struct _xid {
- // data is the cross-interpreter-safe derivation of a Python object
- // (see _PyObject_GetCrossInterpreterData). It will be NULL if the
- // new_object func (below) encodes the data.
- void *data;
- // obj is the Python object from which the data was derived. This
- // is non-NULL only if the data remains bound to the object in some
- // way, such that the object must be "released" (via a decref) when
- // the data is released. In that case the code that sets the field,
- // likely a registered "crossinterpdatafunc", is responsible for
- // ensuring it owns the reference (i.e. incref).
- PyObject *obj;
- // interp is the ID of the owning interpreter of the original
- // object. It corresponds to the active interpreter when
- // _PyObject_GetCrossInterpreterData() was called. This should only
- // be set by the cross-interpreter machinery.
- //
- // We use the ID rather than the PyInterpreterState to avoid issues
- // with deleted interpreters. Note that IDs are never re-used, so
- // each one will always correspond to a specific interpreter
- // (whether still alive or not).
- int64_t interpid;
- // new_object is a function that returns a new object in the current
- // interpreter given the data. The resulting object (a new
- // reference) will be equivalent to the original object. This field
- // is required.
- xid_newobjectfunc new_object;
- // free is called when the data is released. If it is NULL then
- // nothing will be done to free the data. For some types this is
- // okay (e.g. bytes) and for those types this field should be set
- // to NULL. However, for most the data was allocated just for
- // cross-interpreter use, so it must be freed when
- // _PyCrossInterpreterData_Release is called or the memory will
- // leak. In that case, at the very least this field should be set
- // to PyMem_RawFree (the default if not explicitly set to NULL).
- // The call will happen with the original interpreter activated.
- xid_freefunc free;
-};
-
-PyAPI_FUNC(void) _PyCrossInterpreterData_Init(
- _PyCrossInterpreterData *data,
- PyInterpreterState *interp, void *shared, PyObject *obj,
- xid_newobjectfunc new_object);
-PyAPI_FUNC(int) _PyCrossInterpreterData_InitWithSize(
- _PyCrossInterpreterData *,
- PyInterpreterState *interp, const size_t, PyObject *,
- xid_newobjectfunc);
-PyAPI_FUNC(void) _PyCrossInterpreterData_Clear(
- PyInterpreterState *, _PyCrossInterpreterData *);
-
-PyAPI_FUNC(int) _PyObject_GetCrossInterpreterData(PyObject *, _PyCrossInterpreterData *);
-PyAPI_FUNC(PyObject *) _PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *);
-PyAPI_FUNC(int) _PyCrossInterpreterData_Release(_PyCrossInterpreterData *);
-PyAPI_FUNC(int) _PyCrossInterpreterData_ReleaseAndRawFree(_PyCrossInterpreterData *);
-
-PyAPI_FUNC(int) _PyObject_CheckCrossInterpreterData(PyObject *);
-
-/* cross-interpreter data registry */
-
-typedef int (*crossinterpdatafunc)(PyThreadState *tstate, PyObject *,
- _PyCrossInterpreterData *);
-
-PyAPI_FUNC(int) _PyCrossInterpreterData_RegisterClass(PyTypeObject *, crossinterpdatafunc);
-PyAPI_FUNC(int) _PyCrossInterpreterData_UnregisterClass(PyTypeObject *);
-PyAPI_FUNC(crossinterpdatafunc) _PyCrossInterpreterData_Lookup(PyObject *);
diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h
index d8afee9..339ced3 100644
--- a/Include/internal/pycore_ceval.h
+++ b/Include/internal/pycore_ceval.h
@@ -54,16 +54,6 @@ PyAPI_FUNC(int) _PyEval_AddPendingCall(
void *arg,
int flags);
-typedef int (*_Py_simple_func)(void *);
-extern int _Py_CallInInterpreter(
- PyInterpreterState *interp,
- _Py_simple_func func,
- void *arg);
-extern int _Py_CallInInterpreterAndRawFree(
- PyInterpreterState *interp,
- _Py_simple_func func,
- void *arg);
-
extern void _PyEval_SignalAsyncExc(PyInterpreterState *interp);
#ifdef HAVE_FORK
extern PyStatus _PyEval_ReInitThreads(PyThreadState *tstate);
diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h
new file mode 100644
index 0000000..59e4cd9
--- /dev/null
+++ b/Include/internal/pycore_crossinterp.h
@@ -0,0 +1,139 @@
+#ifndef Py_INTERNAL_CROSSINTERP_H
+#define Py_INTERNAL_CROSSINTERP_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef Py_BUILD_CORE
+# error "this header requires Py_BUILD_CORE define"
+#endif
+
+
+/***************************/
+/* cross-interpreter calls */
+/***************************/
+
+typedef int (*_Py_simple_func)(void *);
+extern int _Py_CallInInterpreter(
+ PyInterpreterState *interp,
+ _Py_simple_func func,
+ void *arg);
+extern int _Py_CallInInterpreterAndRawFree(
+ PyInterpreterState *interp,
+ _Py_simple_func func,
+ void *arg);
+
+
+/**************************/
+/* cross-interpreter data */
+/**************************/
+
+typedef struct _xid _PyCrossInterpreterData;
+typedef PyObject *(*xid_newobjectfunc)(_PyCrossInterpreterData *);
+typedef void (*xid_freefunc)(void *);
+
+// _PyCrossInterpreterData is similar to Py_buffer as an effectively
+// opaque struct that holds data outside the object machinery. This
+// is necessary to pass safely between interpreters in the same process.
+struct _xid {
+ // data is the cross-interpreter-safe derivation of a Python object
+ // (see _PyObject_GetCrossInterpreterData). It will be NULL if the
+ // new_object func (below) encodes the data.
+ void *data;
+ // obj is the Python object from which the data was derived. This
+ // is non-NULL only if the data remains bound to the object in some
+ // way, such that the object must be "released" (via a decref) when
+ // the data is released. In that case the code that sets the field,
+ // likely a registered "crossinterpdatafunc", is responsible for
+ // ensuring it owns the reference (i.e. incref).
+ PyObject *obj;
+ // interp is the ID of the owning interpreter of the original
+ // object. It corresponds to the active interpreter when
+ // _PyObject_GetCrossInterpreterData() was called. This should only
+ // be set by the cross-interpreter machinery.
+ //
+ // We use the ID rather than the PyInterpreterState to avoid issues
+ // with deleted interpreters. Note that IDs are never re-used, so
+ // each one will always correspond to a specific interpreter
+ // (whether still alive or not).
+ int64_t interpid;
+ // new_object is a function that returns a new object in the current
+ // interpreter given the data. The resulting object (a new
+ // reference) will be equivalent to the original object. This field
+ // is required.
+ xid_newobjectfunc new_object;
+ // free is called when the data is released. If it is NULL then
+ // nothing will be done to free the data. For some types this is
+ // okay (e.g. bytes) and for those types this field should be set
+ // to NULL. However, for most the data was allocated just for
+ // cross-interpreter use, so it must be freed when
+ // _PyCrossInterpreterData_Release is called or the memory will
+ // leak. In that case, at the very least this field should be set
+ // to PyMem_RawFree (the default if not explicitly set to NULL).
+ // The call will happen with the original interpreter activated.
+ xid_freefunc free;
+};
+
+PyAPI_FUNC(_PyCrossInterpreterData *) _PyCrossInterpreterData_New(void);
+PyAPI_FUNC(void) _PyCrossInterpreterData_Free(_PyCrossInterpreterData *data);
+
+
+/* defining cross-interpreter data */
+
+PyAPI_FUNC(void) _PyCrossInterpreterData_Init(
+ _PyCrossInterpreterData *data,
+ PyInterpreterState *interp, void *shared, PyObject *obj,
+ xid_newobjectfunc new_object);
+PyAPI_FUNC(int) _PyCrossInterpreterData_InitWithSize(
+ _PyCrossInterpreterData *,
+ PyInterpreterState *interp, const size_t, PyObject *,
+ xid_newobjectfunc);
+PyAPI_FUNC(void) _PyCrossInterpreterData_Clear(
+ PyInterpreterState *, _PyCrossInterpreterData *);
+
+
+/* using cross-interpreter data */
+
+PyAPI_FUNC(int) _PyObject_CheckCrossInterpreterData(PyObject *);
+PyAPI_FUNC(int) _PyObject_GetCrossInterpreterData(PyObject *, _PyCrossInterpreterData *);
+PyAPI_FUNC(PyObject *) _PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *);
+PyAPI_FUNC(int) _PyCrossInterpreterData_Release(_PyCrossInterpreterData *);
+PyAPI_FUNC(int) _PyCrossInterpreterData_ReleaseAndRawFree(_PyCrossInterpreterData *);
+
+
+/* cross-interpreter data registry */
+
+// For now we use a global registry of shareable classes. An
+// alternative would be to add a tp_* slot for a class's
+// crossinterpdatafunc. It would be simpler and more efficient.
+
+typedef int (*crossinterpdatafunc)(PyThreadState *tstate, PyObject *,
+ _PyCrossInterpreterData *);
+
+struct _xidregitem;
+
+struct _xidregitem {
+ struct _xidregitem *prev;
+ struct _xidregitem *next;
+ /* This can be a dangling pointer, but only if weakref is set. */
+ PyTypeObject *cls;
+ /* This is NULL for builtin types. */
+ PyObject *weakref;
+ size_t refcount;
+ crossinterpdatafunc getdata;
+};
+
+struct _xidregistry {
+ PyThread_type_lock mutex;
+ struct _xidregitem *head;
+};
+
+PyAPI_FUNC(int) _PyCrossInterpreterData_RegisterClass(PyTypeObject *, crossinterpdatafunc);
+PyAPI_FUNC(int) _PyCrossInterpreterData_UnregisterClass(PyTypeObject *);
+PyAPI_FUNC(crossinterpdatafunc) _PyCrossInterpreterData_Lookup(PyObject *);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_INTERNAL_CROSSINTERP_H */
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index fc27aad..a067a60 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -15,6 +15,7 @@ extern "C" {
#include "pycore_ceval_state.h" // struct _ceval_state
#include "pycore_code.h" // struct callable_cache
#include "pycore_context.h" // struct _Py_context_state
+#include "pycore_crossinterp.h" // struct _xidregistry
#include "pycore_dict_state.h" // struct _Py_dict_state
#include "pycore_dtoa.h" // struct _dtoa_state
#include "pycore_exceptions.h" // struct _Py_exc_state
@@ -41,28 +42,6 @@ struct _Py_long_state {
/* cross-interpreter data registry */
-/* For now we use a global registry of shareable classes. An
- alternative would be to add a tp_* slot for a class's
- crossinterpdatafunc. It would be simpler and more efficient. */
-
-struct _xidregitem;
-
-struct _xidregitem {
- struct _xidregitem *prev;
- struct _xidregitem *next;
- /* This can be a dangling pointer, but only if weakref is set. */
- PyTypeObject *cls;
- /* This is NULL for builtin types. */
- PyObject *weakref;
- size_t refcount;
- crossinterpdatafunc getdata;
-};
-
-struct _xidregistry {
- PyThread_type_lock mutex;
- struct _xidregitem *head;
-};
-
/* interpreter state */
diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h
index ec026d4..320e5bb 100644
--- a/Include/internal/pycore_runtime.h
+++ b/Include/internal/pycore_runtime.h
@@ -10,6 +10,7 @@ extern "C" {
#include "pycore_atexit.h" // struct _atexit_runtime_state
#include "pycore_ceval_state.h" // struct _ceval_runtime_state
+#include "pycore_crossinterp.h" // struct _xidregistry
#include "pycore_faulthandler.h" // struct _faulthandler_runtime_state
#include "pycore_floatobject.h" // struct _Py_float_runtime_state
#include "pycore_import.h" // struct _import_runtime_state
diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py
index e3c917a..f0b9101 100644
--- a/Lib/test/test__xxsubinterpreters.py
+++ b/Lib/test/test__xxsubinterpreters.py
@@ -7,7 +7,7 @@ from textwrap import dedent
import threading
import unittest
-import _testcapi
+import _testinternalcapi
from test import support
from test.support import import_helper
from test.support import script_helper
@@ -146,8 +146,8 @@ class ShareableTypeTests(unittest.TestCase):
def _assert_values(self, values):
for obj in values:
with self.subTest(obj):
- xid = _testcapi.get_crossinterp_data(obj)
- got = _testcapi.restore_crossinterp_data(xid)
+ xid = _testinternalcapi.get_crossinterp_data(obj)
+ got = _testinternalcapi.restore_crossinterp_data(xid)
self.assertEqual(got, obj)
self.assertIs(type(got), type(obj))
@@ -155,8 +155,8 @@ class ShareableTypeTests(unittest.TestCase):
def test_singletons(self):
for obj in [None]:
with self.subTest(obj):
- xid = _testcapi.get_crossinterp_data(obj)
- got = _testcapi.restore_crossinterp_data(xid)
+ xid = _testinternalcapi.get_crossinterp_data(obj)
+ got = _testinternalcapi.restore_crossinterp_data(xid)
# XXX What about between interpreters?
self.assertIs(got, obj)
@@ -187,7 +187,7 @@ class ShareableTypeTests(unittest.TestCase):
for i in ints:
with self.subTest(i):
with self.assertRaises(OverflowError):
- _testcapi.get_crossinterp_data(i)
+ _testinternalcapi.get_crossinterp_data(i)
class ModuleTests(TestBase):
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 733c685..f2b252c 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -409,6 +409,7 @@ PYTHON_OBJS= \
Python/codecs.o \
Python/compile.o \
Python/context.o \
+ Python/crossinterp.o \
Python/dynamic_annotations.o \
Python/errors.o \
Python/executor.o \
@@ -1800,6 +1801,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/internal/pycore_complexobject.h \
$(srcdir)/Include/internal/pycore_condvar.h \
$(srcdir)/Include/internal/pycore_context.h \
+ $(srcdir)/Include/internal/pycore_crossinterp.h \
$(srcdir)/Include/internal/pycore_dict.h \
$(srcdir)/Include/internal/pycore_dict_state.h \
$(srcdir)/Include/internal/pycore_descrobject.h \
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index dc9a25b..fc9dc74 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -1445,58 +1445,6 @@ run_in_subinterp(PyObject *self, PyObject *args)
return PyLong_FromLong(r);
}
-static void
-_xid_capsule_destructor(PyObject *capsule)
-{
- _PyCrossInterpreterData *data = \
- (_PyCrossInterpreterData *)PyCapsule_GetPointer(capsule, NULL);
- if (data != NULL) {
- assert(_PyCrossInterpreterData_Release(data) == 0);
- PyMem_Free(data);
- }
-}
-
-static PyObject *
-get_crossinterp_data(PyObject *self, PyObject *args)
-{
- PyObject *obj = NULL;
- if (!PyArg_ParseTuple(args, "O:get_crossinterp_data", &obj)) {
- return NULL;
- }
-
- _PyCrossInterpreterData *data = PyMem_NEW(_PyCrossInterpreterData, 1);
- if (data == NULL) {
- PyErr_NoMemory();
- return NULL;
- }
- if (_PyObject_GetCrossInterpreterData(obj, data) != 0) {
- PyMem_Free(data);
- return NULL;
- }
- PyObject *capsule = PyCapsule_New(data, NULL, _xid_capsule_destructor);
- if (capsule == NULL) {
- assert(_PyCrossInterpreterData_Release(data) == 0);
- PyMem_Free(data);
- }
- return capsule;
-}
-
-static PyObject *
-restore_crossinterp_data(PyObject *self, PyObject *args)
-{
- PyObject *capsule = NULL;
- if (!PyArg_ParseTuple(args, "O:restore_crossinterp_data", &capsule)) {
- return NULL;
- }
-
- _PyCrossInterpreterData *data = \
- (_PyCrossInterpreterData *)PyCapsule_GetPointer(capsule, NULL);
- if (data == NULL) {
- return NULL;
- }
- return _PyCrossInterpreterData_NewObject(data);
-}
-
static PyMethodDef ml;
static PyObject *
@@ -3282,8 +3230,6 @@ static PyMethodDef TestMethods[] = {
{"crash_no_current_thread", crash_no_current_thread, METH_NOARGS},
{"test_current_tstate_matches", test_current_tstate_matches, METH_NOARGS},
{"run_in_subinterp", run_in_subinterp, METH_VARARGS},
- {"get_crossinterp_data", get_crossinterp_data, METH_VARARGS},
- {"restore_crossinterp_data", restore_crossinterp_data, METH_VARARGS},
{"create_cfunction", create_cfunction, METH_NOARGS},
{"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_VARARGS,
PyDoc_STR("set_error_class(error_class) -> None")},
diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c
index 1869f48..a71e7e1 100644
--- a/Modules/_testinternalcapi.c
+++ b/Modules/_testinternalcapi.c
@@ -1466,6 +1466,58 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)
}
+static void
+_xid_capsule_destructor(PyObject *capsule)
+{
+ _PyCrossInterpreterData *data = \
+ (_PyCrossInterpreterData *)PyCapsule_GetPointer(capsule, NULL);
+ if (data != NULL) {
+ assert(_PyCrossInterpreterData_Release(data) == 0);
+ _PyCrossInterpreterData_Free(data);
+ }
+}
+
+static PyObject *
+get_crossinterp_data(PyObject *self, PyObject *args)
+{
+ PyObject *obj = NULL;
+ if (!PyArg_ParseTuple(args, "O:get_crossinterp_data", &obj)) {
+ return NULL;
+ }
+
+ _PyCrossInterpreterData *data = _PyCrossInterpreterData_New();
+ if (data == NULL) {
+ return NULL;
+ }
+ if (_PyObject_GetCrossInterpreterData(obj, data) != 0) {
+ _PyCrossInterpreterData_Free(data);
+ return NULL;
+ }
+ PyObject *capsule = PyCapsule_New(data, NULL, _xid_capsule_destructor);
+ if (capsule == NULL) {
+ assert(_PyCrossInterpreterData_Release(data) == 0);
+ _PyCrossInterpreterData_Free(data);
+ }
+ return capsule;
+}
+
+static PyObject *
+restore_crossinterp_data(PyObject *self, PyObject *args)
+{
+ PyObject *capsule = NULL;
+ if (!PyArg_ParseTuple(args, "O:restore_crossinterp_data", &capsule)) {
+ return NULL;
+ }
+
+ _PyCrossInterpreterData *data = \
+ (_PyCrossInterpreterData *)PyCapsule_GetPointer(capsule, NULL);
+ if (data == NULL) {
+ return NULL;
+ }
+ return _PyCrossInterpreterData_NewObject(data);
+}
+
+
/*[clinic input]
_testinternalcapi.write_unraisable_exc
exception as exc: object
@@ -1645,6 +1697,8 @@ static PyMethodDef module_functions[] = {
METH_VARARGS | METH_KEYWORDS},
{"compile_perf_trampoline_entry", compile_perf_trampoline_entry, METH_VARARGS},
{"perf_trampoline_set_persist_after_fork", perf_trampoline_set_persist_after_fork, METH_VARARGS},
+ {"get_crossinterp_data", get_crossinterp_data, METH_VARARGS},
+ {"restore_crossinterp_data", restore_crossinterp_data, METH_VARARGS},
_TESTINTERNALCAPI_WRITE_UNRAISABLE_EXC_METHODDEF
_TESTINTERNALCAPI_TEST_LONG_NUMBITS_METHODDEF
{NULL, NULL} /* sentinel */
diff --git a/Modules/_xxinterpchannelsmodule.c b/Modules/_xxinterpchannelsmodule.c
index 11fe8cd..1c9ae3b 100644
--- a/Modules/_xxinterpchannelsmodule.c
+++ b/Modules/_xxinterpchannelsmodule.c
@@ -7,6 +7,7 @@
#include "Python.h"
#include "interpreteridobject.h"
+#include "pycore_crossinterp.h" // struct _xid
#include "pycore_pybuffer.h" // _PyBuffer_ReleaseInInterpreterAndRawFree()
#include "pycore_interp.h" // _PyInterpreterState_LookUpID()
diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c
index 69078dc..640fd69 100644
--- a/Modules/_xxsubinterpretersmodule.c
+++ b/Modules/_xxsubinterpretersmodule.c
@@ -6,6 +6,7 @@
#endif
#include "Python.h"
+#include "pycore_crossinterp.h" // struct _xid
#include "pycore_initconfig.h" // _PyErr_SetFromPyStatus()
#include "pycore_modsupport.h" // _PyArg_BadArgument()
#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1()
diff --git a/Objects/abstract.c b/Objects/abstract.c
index 806ca65..070e762 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -5,6 +5,7 @@
#include "pycore_pybuffer.h"
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate()
+#include "pycore_crossinterp.h" // _Py_CallInInterpreter()
#include "pycore_object.h" // _Py_CheckSlotResult()
#include "pycore_long.h" // _Py_IsNegative
#include "pycore_pyerrors.h" // _PyErr_Occurred()
diff --git a/PCbuild/_freeze_module.vcxproj b/PCbuild/_freeze_module.vcxproj
index cfcb6cb..05b8bfd 100644
--- a/PCbuild/_freeze_module.vcxproj
+++ b/PCbuild/_freeze_module.vcxproj
@@ -209,6 +209,7 @@
<ClCompile Include="..\Python\codecs.c" />
<ClCompile Include="..\Python\compile.c" />
<ClCompile Include="..\Python\context.c" />
+ <ClCompile Include="..\Python\crossinterp.c" />
<ClCompile Include="..\Python\dtoa.c" />
<ClCompile Include="..\Python\dynamic_annotations.c" />
<ClCompile Include="..\Python\dynload_win.c" />
diff --git a/PCbuild/_freeze_module.vcxproj.filters b/PCbuild/_freeze_module.vcxproj.filters
index f0afa7a..d6cbd2d 100644
--- a/PCbuild/_freeze_module.vcxproj.filters
+++ b/PCbuild/_freeze_module.vcxproj.filters
@@ -103,6 +103,9 @@
<ClCompile Include="..\Python\context.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\Python\crossinterp.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\Objects\descrobject.c">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index 203d9c8..954a59a 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -216,6 +216,7 @@
<ClInclude Include="..\Include\internal\pycore_complexobject.h" />
<ClInclude Include="..\Include\internal\pycore_condvar.h" />
<ClInclude Include="..\Include\internal\pycore_context.h" />
+ <ClInclude Include="..\Include\internal\pycore_crossinterp.h" />
<ClInclude Include="..\Include\internal\pycore_descrobject.h" />
<ClInclude Include="..\Include\internal\pycore_dict.h" />
<ClInclude Include="..\Include\internal\pycore_dict_state.h" />
@@ -561,6 +562,7 @@
<ClCompile Include="..\Python\codecs.c" />
<ClCompile Include="..\Python\compile.c" />
<ClCompile Include="..\Python\context.c" />
+ <ClCompile Include="..\Python\crossinterp.c" />
<ClCompile Include="..\Python\dynamic_annotations.c" />
<ClCompile Include="..\Python\dynload_win.c" />
<ClCompile Include="..\Python\errors.c" />
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index 09f4a2f..2f8b206 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -579,6 +579,9 @@
<ClInclude Include="..\Include\internal\pycore_context.h">
<Filter>Include\internal</Filter>
</ClInclude>
+ <ClInclude Include="..\Include\internal\pycore_crossinterp.h">
+ <Filter>Include\internal</Filter>
+ </ClInclude>
<ClInclude Include="..\Include\internal\pycore_descrobject.h">
<Filter>Include\internal</Filter>
</ClInclude>
@@ -1280,6 +1283,9 @@
<ClCompile Include="..\Python\compile.c">
<Filter>Python</Filter>
</ClCompile>
+ <ClCompile Include="..\Python\crossinterp.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\Python\dynamic_annotations.c">
<Filter>Python</Filter>
</ClCompile>
diff --git a/Python/crossinterp.c b/Python/crossinterp.c
new file mode 100644
index 0000000..74f1d6e
--- /dev/null
+++ b/Python/crossinterp.c
@@ -0,0 +1,627 @@
+
+/* API for managing interactions between isolated interpreters */
+
+#include "Python.h"
+#include "pycore_ceval.h" // _Py_simple_func
+#include "pycore_crossinterp.h" // struct _xid
+#include "pycore_pyerrors.h" // _PyErr_Clear()
+#include "pycore_pystate.h" // _PyInterpreterState_GET()
+#include "pycore_weakref.h" // _PyWeakref_GET_REF()
+
+
+/***************************/
+/* cross-interpreter calls */
+/***************************/
+
+int
+_Py_CallInInterpreter(PyInterpreterState *interp,
+ _Py_simple_func func, void *arg)
+{
+ if (interp == _PyThreadState_GetCurrent()->interp) {
+ return func(arg);
+ }
+ // XXX Emit a warning if this fails?
+ _PyEval_AddPendingCall(interp, (_Py_pending_call_func)func, arg, 0);
+ return 0;
+}
+
+int
+_Py_CallInInterpreterAndRawFree(PyInterpreterState *interp,
+ _Py_simple_func func, void *arg)
+{
+ if (interp == _PyThreadState_GetCurrent()->interp) {
+ int res = func(arg);
+ PyMem_RawFree(arg);
+ return res;
+ }
+ // XXX Emit a warning if this fails?
+ _PyEval_AddPendingCall(interp, func, arg, _Py_PENDING_RAWFREE);
+ return 0;
+}
+
+
+/**************************/
+/* cross-interpreter data */
+/**************************/
+
+_PyCrossInterpreterData *
+_PyCrossInterpreterData_New(void)
+{
+ _PyCrossInterpreterData *xid = PyMem_RawMalloc(
+ sizeof(_PyCrossInterpreterData));
+ if (xid == NULL) {
+ PyErr_NoMemory();
+ }
+ return xid;
+}
+
+void
+_PyCrossInterpreterData_Free(_PyCrossInterpreterData *xid)
+{
+ PyInterpreterState *interp = PyInterpreterState_Get();
+ _PyCrossInterpreterData_Clear(interp, xid);
+ PyMem_RawFree(xid);
+}
+
+
+/* defining cross-interpreter data */
+
+static inline void
+_xidata_init(_PyCrossInterpreterData *data)
+{
+ // If the value is being reused
+ // then _xidata_clear() should have been called already.
+ assert(data->data == NULL);
+ assert(data->obj == NULL);
+ *data = (_PyCrossInterpreterData){0};
+ data->interpid = -1;
+}
+
+static inline void
+_xidata_clear(_PyCrossInterpreterData *data)
+{
+ // _PyCrossInterpreterData only has two members that need to be
+ // cleaned up, if set: "data" must be freed and "obj" must be decref'ed.
+ // In both cases the original (owning) interpreter must be used,
+ // which is the caller's responsibility to ensure.
+ if (data->data != NULL) {
+ if (data->free != NULL) {
+ data->free(data->data);
+ }
+ data->data = NULL;
+ }
+ Py_CLEAR(data->obj);
+}
+
+void
+_PyCrossInterpreterData_Init(_PyCrossInterpreterData *data,
+ PyInterpreterState *interp,
+ void *shared, PyObject *obj,
+ xid_newobjectfunc new_object)
+{
+ assert(data != NULL);
+ assert(new_object != NULL);
+ _xidata_init(data);
+ data->data = shared;
+ if (obj != NULL) {
+ assert(interp != NULL);
+ // released in _PyCrossInterpreterData_Clear()
+ data->obj = Py_NewRef(obj);
+ }
+ // Ideally every object would know its owning interpreter.
+ // Until then, we have to rely on the caller to identify it
+ // (but we don't need it in all cases).
+ data->interpid = (interp != NULL) ? interp->id : -1;
+ data->new_object = new_object;
+}
+
+int
+_PyCrossInterpreterData_InitWithSize(_PyCrossInterpreterData *data,
+ PyInterpreterState *interp,
+ const size_t size, PyObject *obj,
+ xid_newobjectfunc new_object)
+{
+ assert(size > 0);
+ // For now we always free the shared data in the same interpreter
+ // where it was allocated, so the interpreter is required.
+ assert(interp != NULL);
+ _PyCrossInterpreterData_Init(data, interp, NULL, obj, new_object);
+ data->data = PyMem_RawMalloc(size);
+ if (data->data == NULL) {
+ return -1;
+ }
+ data->free = PyMem_RawFree;
+ return 0;
+}
+
+void
+_PyCrossInterpreterData_Clear(PyInterpreterState *interp,
+ _PyCrossInterpreterData *data)
+{
+ assert(data != NULL);
+ // This must be called in the owning interpreter.
+ assert(interp == NULL
+ || data->interpid == -1
+ || data->interpid == interp->id);
+ _xidata_clear(data);
+}
+
+
+/* using cross-interpreter data */
+
+static int
+_check_xidata(PyThreadState *tstate, _PyCrossInterpreterData *data)
+{
+ // data->data can be anything, including NULL, so we don't check it.
+
+ // data->obj may be NULL, so we don't check it.
+
+ if (data->interpid < 0) {
+ _PyErr_SetString(tstate, PyExc_SystemError, "missing interp");
+ return -1;
+ }
+
+ if (data->new_object == NULL) {
+ _PyErr_SetString(tstate, PyExc_SystemError, "missing new_object func");
+ return -1;
+ }
+
+ // data->free may be NULL, so we don't check it.
+
+ return 0;
+}
+
+crossinterpdatafunc _PyCrossInterpreterData_Lookup(PyObject *);
+
+/* This is a separate func from _PyCrossInterpreterData_Lookup in order
+ to keep the registry code separate. */
+static crossinterpdatafunc
+_lookup_getdata(PyObject *obj)
+{
+ crossinterpdatafunc getdata = _PyCrossInterpreterData_Lookup(obj);
+ if (getdata == NULL && PyErr_Occurred() == 0)
+ PyErr_Format(PyExc_ValueError,
+ "%S does not support cross-interpreter data", obj);
+ return getdata;
+}
+
+int
+_PyObject_CheckCrossInterpreterData(PyObject *obj)
+{
+ crossinterpdatafunc getdata = _lookup_getdata(obj);
+ if (getdata == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+int
+_PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data)
+{
+ PyThreadState *tstate = _PyThreadState_GetCurrent();
+#ifdef Py_DEBUG
+ // The caller must hold the GIL
+ _Py_EnsureTstateNotNULL(tstate);
+#endif
+ PyInterpreterState *interp = tstate->interp;
+
+ // Reset data before re-populating.
+ *data = (_PyCrossInterpreterData){0};
+ data->interpid = -1;
+
+ // Call the "getdata" func for the object.
+ Py_INCREF(obj);
+ crossinterpdatafunc getdata = _lookup_getdata(obj);
+ if (getdata == NULL) {
+ Py_DECREF(obj);
+ return -1;
+ }
+ int res = getdata(tstate, obj, data);
+ Py_DECREF(obj);
+ if (res != 0) {
+ return -1;
+ }
+
+ // Fill in the blanks and validate the result.
+ data->interpid = interp->id;
+ if (_check_xidata(tstate, data) != 0) {
+ (void)_PyCrossInterpreterData_Release(data);
+ return -1;
+ }
+
+ return 0;
+}
+
+PyObject *
+_PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *data)
+{
+ return data->new_object(data);
+}
+
+static int
+_call_clear_xidata(void *data)
+{
+ _xidata_clear((_PyCrossInterpreterData *)data);
+ return 0;
+}
+
+static int
+_xidata_release(_PyCrossInterpreterData *data, int rawfree)
+{
+ if ((data->data == NULL || data->free == NULL) && data->obj == NULL) {
+ // Nothing to release!
+ if (rawfree) {
+ PyMem_RawFree(data);
+ }
+ else {
+ data->data = NULL;
+ }
+ return 0;
+ }
+
+ // Switch to the original interpreter.
+ PyInterpreterState *interp = _PyInterpreterState_LookUpID(data->interpid);
+ if (interp == NULL) {
+ // The interpreter was already destroyed.
+ // This function shouldn't have been called.
+ // XXX Someone leaked some memory...
+ assert(PyErr_Occurred());
+ if (rawfree) {
+ PyMem_RawFree(data);
+ }
+ return -1;
+ }
+
+ // "Release" the data and/or the object.
+ if (rawfree) {
+ return _Py_CallInInterpreterAndRawFree(interp, _call_clear_xidata, data);
+ }
+ else {
+ return _Py_CallInInterpreter(interp, _call_clear_xidata, data);
+ }
+}
+
+int
+_PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
+{
+ return _xidata_release(data, 0);
+}
+
+int
+_PyCrossInterpreterData_ReleaseAndRawFree(_PyCrossInterpreterData *data)
+{
+ return _xidata_release(data, 1);
+}
+
+
+/* registry of {type -> crossinterpdatafunc} */
+
+/* For now we use a global registry of shareable classes. An
+ alternative would be to add a tp_* slot for a class's
+ crossinterpdatafunc. It would be simpler and more efficient. */
+
+static int
+_xidregistry_add_type(struct _xidregistry *xidregistry,
+ PyTypeObject *cls, crossinterpdatafunc getdata)
+{
+ struct _xidregitem *newhead = PyMem_RawMalloc(sizeof(struct _xidregitem));
+ if (newhead == NULL) {
+ return -1;
+ }
+ *newhead = (struct _xidregitem){
+ // We do not keep a reference, to avoid keeping the class alive.
+ .cls = cls,
+ .refcount = 1,
+ .getdata = getdata,
+ };
+ if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) {
+ // XXX Assign a callback to clear the entry from the registry?
+ newhead->weakref = PyWeakref_NewRef((PyObject *)cls, NULL);
+ if (newhead->weakref == NULL) {
+ PyMem_RawFree(newhead);
+ return -1;
+ }
+ }
+ newhead->next = xidregistry->head;
+ if (newhead->next != NULL) {
+ newhead->next->prev = newhead;
+ }
+ xidregistry->head = newhead;
+ return 0;
+}
+
+static struct _xidregitem *
+_xidregistry_remove_entry(struct _xidregistry *xidregistry,
+ struct _xidregitem *entry)
+{
+ struct _xidregitem *next = entry->next;
+ if (entry->prev != NULL) {
+ assert(entry->prev->next == entry);
+ entry->prev->next = next;
+ }
+ else {
+ assert(xidregistry->head == entry);
+ xidregistry->head = next;
+ }
+ if (next != NULL) {
+ next->prev = entry->prev;
+ }
+ Py_XDECREF(entry->weakref);
+ PyMem_RawFree(entry);
+ return next;
+}
+
+// This is used in pystate.c (for now).
+void
+_Py_xidregistry_clear(struct _xidregistry *xidregistry)
+{
+ struct _xidregitem *cur = xidregistry->head;
+ xidregistry->head = NULL;
+ while (cur != NULL) {
+ struct _xidregitem *next = cur->next;
+ Py_XDECREF(cur->weakref);
+ PyMem_RawFree(cur);
+ cur = next;
+ }
+}
+
+static struct _xidregitem *
+_xidregistry_find_type(struct _xidregistry *xidregistry, PyTypeObject *cls)
+{
+ struct _xidregitem *cur = xidregistry->head;
+ while (cur != NULL) {
+ if (cur->weakref != NULL) {
+ // cur is/was a heap type.
+ PyObject *registered = _PyWeakref_GET_REF(cur->weakref);
+ if (registered == NULL) {
+ // The weakly ref'ed object was freed.
+ cur = _xidregistry_remove_entry(xidregistry, cur);
+ continue;
+ }
+ assert(PyType_Check(registered));
+ assert(cur->cls == (PyTypeObject *)registered);
+ assert(cur->cls->tp_flags & Py_TPFLAGS_HEAPTYPE);
+ Py_DECREF(registered);
+ }
+ if (cur->cls == cls) {
+ return cur;
+ }
+ cur = cur->next;
+ }
+ return NULL;
+}
+
+static inline struct _xidregistry *
+_get_xidregistry(PyInterpreterState *interp, PyTypeObject *cls)
+{
+ struct _xidregistry *xidregistry = &interp->runtime->xidregistry;
+ if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) {
+ assert(interp->xidregistry.mutex == xidregistry->mutex);
+ xidregistry = &interp->xidregistry;
+ }
+ return xidregistry;
+}
+
+static void _register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry);
+
+static inline void
+_ensure_builtins_xid(PyInterpreterState *interp, struct _xidregistry *xidregistry)
+{
+ if (xidregistry != &interp->xidregistry) {
+ assert(xidregistry == &interp->runtime->xidregistry);
+ if (xidregistry->head == NULL) {
+ _register_builtins_for_crossinterpreter_data(xidregistry);
+ }
+ }
+}
+
+int
+_PyCrossInterpreterData_RegisterClass(PyTypeObject *cls,
+ crossinterpdatafunc getdata)
+{
+ if (!PyType_Check(cls)) {
+ PyErr_Format(PyExc_ValueError, "only classes may be registered");
+ return -1;
+ }
+ if (getdata == NULL) {
+ PyErr_Format(PyExc_ValueError, "missing 'getdata' func");
+ return -1;
+ }
+
+ int res = 0;
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _xidregistry *xidregistry = _get_xidregistry(interp, cls);
+ PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
+
+ _ensure_builtins_xid(interp, xidregistry);
+
+ struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
+ if (matched != NULL) {
+ assert(matched->getdata == getdata);
+ matched->refcount += 1;
+ goto finally;
+ }
+
+ res = _xidregistry_add_type(xidregistry, cls, getdata);
+
+finally:
+ PyThread_release_lock(xidregistry->mutex);
+ return res;
+}
+
+int
+_PyCrossInterpreterData_UnregisterClass(PyTypeObject *cls)
+{
+ int res = 0;
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _xidregistry *xidregistry = _get_xidregistry(interp, cls);
+ PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
+
+ struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
+ if (matched != NULL) {
+ assert(matched->refcount > 0);
+ matched->refcount -= 1;
+ if (matched->refcount == 0) {
+ (void)_xidregistry_remove_entry(xidregistry, matched);
+ }
+ res = 1;
+ }
+
+ PyThread_release_lock(xidregistry->mutex);
+ return res;
+}
+
+
+/* Cross-interpreter objects are looked up by exact match on the class.
+ We can reassess this policy when we move from a global registry to a
+ tp_* slot. */
+
+crossinterpdatafunc
+_PyCrossInterpreterData_Lookup(PyObject *obj)
+{
+ PyTypeObject *cls = Py_TYPE(obj);
+
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _xidregistry *xidregistry = _get_xidregistry(interp, cls);
+ PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
+
+ _ensure_builtins_xid(interp, xidregistry);
+
+ struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
+ crossinterpdatafunc func = matched != NULL ? matched->getdata : NULL;
+
+ PyThread_release_lock(xidregistry->mutex);
+ return func;
+}
+
+/* cross-interpreter data for builtin types */
+
+struct _shared_bytes_data {
+ char *bytes;
+ Py_ssize_t len;
+};
+
+static PyObject *
+_new_bytes_object(_PyCrossInterpreterData *data)
+{
+ struct _shared_bytes_data *shared = (struct _shared_bytes_data *)(data->data);
+ return PyBytes_FromStringAndSize(shared->bytes, shared->len);
+}
+
+static int
+_bytes_shared(PyThreadState *tstate, PyObject *obj,
+ _PyCrossInterpreterData *data)
+{
+ if (_PyCrossInterpreterData_InitWithSize(
+ data, tstate->interp, sizeof(struct _shared_bytes_data), obj,
+ _new_bytes_object
+ ) < 0)
+ {
+ return -1;
+ }
+ struct _shared_bytes_data *shared = (struct _shared_bytes_data *)data->data;
+ if (PyBytes_AsStringAndSize(obj, &shared->bytes, &shared->len) < 0) {
+ _PyCrossInterpreterData_Clear(tstate->interp, data);
+ return -1;
+ }
+ return 0;
+}
+
+struct _shared_str_data {
+ int kind;
+ const void *buffer;
+ Py_ssize_t len;
+};
+
+static PyObject *
+_new_str_object(_PyCrossInterpreterData *data)
+{
+ struct _shared_str_data *shared = (struct _shared_str_data *)(data->data);
+ return PyUnicode_FromKindAndData(shared->kind, shared->buffer, shared->len);
+}
+
+static int
+_str_shared(PyThreadState *tstate, PyObject *obj,
+ _PyCrossInterpreterData *data)
+{
+ if (_PyCrossInterpreterData_InitWithSize(
+ data, tstate->interp, sizeof(struct _shared_str_data), obj,
+ _new_str_object
+ ) < 0)
+ {
+ return -1;
+ }
+ struct _shared_str_data *shared = (struct _shared_str_data *)data->data;
+ shared->kind = PyUnicode_KIND(obj);
+ shared->buffer = PyUnicode_DATA(obj);
+ shared->len = PyUnicode_GET_LENGTH(obj);
+ return 0;
+}
+
+static PyObject *
+_new_long_object(_PyCrossInterpreterData *data)
+{
+ return PyLong_FromSsize_t((Py_ssize_t)(data->data));
+}
+
+static int
+_long_shared(PyThreadState *tstate, PyObject *obj,
+ _PyCrossInterpreterData *data)
+{
+ /* Note that this means the size of shareable ints is bounded by
+ * sys.maxsize. Hence on 32-bit architectures that is half the
+ * size of maximum shareable ints on 64-bit.
+ */
+ Py_ssize_t value = PyLong_AsSsize_t(obj);
+ if (value == -1 && PyErr_Occurred()) {
+ if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+ PyErr_SetString(PyExc_OverflowError, "try sending as bytes");
+ }
+ return -1;
+ }
+ _PyCrossInterpreterData_Init(data, tstate->interp, (void *)value, NULL,
+ _new_long_object);
+ // data->obj and data->free remain NULL
+ return 0;
+}
+
+static PyObject *
+_new_none_object(_PyCrossInterpreterData *data)
+{
+ // XXX Singleton refcounts are problematic across interpreters...
+ return Py_NewRef(Py_None);
+}
+
+static int
+_none_shared(PyThreadState *tstate, PyObject *obj,
+ _PyCrossInterpreterData *data)
+{
+ _PyCrossInterpreterData_Init(data, tstate->interp, NULL, NULL,
+ _new_none_object);
+ // data->data, data->obj and data->free remain NULL
+ return 0;
+}
+
+static void
+_register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry)
+{
+ // None
+ if (_xidregistry_add_type(xidregistry, (PyTypeObject *)PyObject_Type(Py_None), _none_shared) != 0) {
+ Py_FatalError("could not register None for cross-interpreter sharing");
+ }
+
+ // int
+ if (_xidregistry_add_type(xidregistry, &PyLong_Type, _long_shared) != 0) {
+ Py_FatalError("could not register int for cross-interpreter sharing");
+ }
+
+ // bytes
+ if (_xidregistry_add_type(xidregistry, &PyBytes_Type, _bytes_shared) != 0) {
+ Py_FatalError("could not register bytes for cross-interpreter sharing");
+ }
+
+ // str
+ if (_xidregistry_add_type(xidregistry, &PyUnicode_Type, _str_shared) != 0) {
+ Py_FatalError("could not register str for cross-interpreter sharing");
+ }
+}
diff --git a/Python/pystate.c b/Python/pystate.c
index c44a28c..d97a03c 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -16,7 +16,6 @@
#include "pycore_pystate.h"
#include "pycore_runtime_init.h" // _PyRuntimeState_INIT
#include "pycore_sysmodule.h" // _PySys_Audit()
-#include "pycore_weakref.h" // _PyWeakref_GET_REF()
/* --------------------------------------------------------------------------
CAUTION
@@ -495,7 +494,8 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
return _PyStatus_OK();
}
-static void _xidregistry_clear(struct _xidregistry *);
+// This is defined in crossinterp.c (for now).
+extern void _Py_xidregistry_clear(struct _xidregistry *);
void
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
@@ -505,7 +505,7 @@ _PyRuntimeState_Fini(_PyRuntimeState *runtime)
assert(runtime->object_state.interpreter_leaks == 0);
#endif
- _xidregistry_clear(&runtime->xidregistry);
+ _Py_xidregistry_clear(&runtime->xidregistry);
if (gilstate_tss_initialized(runtime)) {
gilstate_tss_fini(runtime);
@@ -948,7 +948,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
Py_CLEAR(interp->sysdict);
Py_CLEAR(interp->builtins);
- _xidregistry_clear(&interp->xidregistry);
+ _Py_xidregistry_clear(&interp->xidregistry);
/* The lock is owned by the runtime, so we don't free it here. */
interp->xidregistry.mutex = NULL;
@@ -2415,593 +2415,6 @@ PyGILState_Release(PyGILState_STATE oldstate)
}
-/**************************/
-/* cross-interpreter data */
-/**************************/
-
-/* cross-interpreter data */
-
-static inline void
-_xidata_init(_PyCrossInterpreterData *data)
-{
- // If the value is being reused
- // then _xidata_clear() should have been called already.
- assert(data->data == NULL);
- assert(data->obj == NULL);
- *data = (_PyCrossInterpreterData){0};
- data->interpid = -1;
-}
-
-static inline void
-_xidata_clear(_PyCrossInterpreterData *data)
-{
- // _PyCrossInterpreterData only has two members that need to be
- // cleaned up, if set: "data" must be freed and "obj" must be decref'ed.
- // In both cases the original (owning) interpreter must be used,
- // which is the caller's responsibility to ensure.
- if (data->data != NULL) {
- if (data->free != NULL) {
- data->free(data->data);
- }
- data->data = NULL;
- }
- Py_CLEAR(data->obj);
-}
-
-void
-_PyCrossInterpreterData_Init(_PyCrossInterpreterData *data,
- PyInterpreterState *interp,
- void *shared, PyObject *obj,
- xid_newobjectfunc new_object)
-{
- assert(data != NULL);
- assert(new_object != NULL);
- _xidata_init(data);
- data->data = shared;
- if (obj != NULL) {
- assert(interp != NULL);
- // released in _PyCrossInterpreterData_Clear()
- data->obj = Py_NewRef(obj);
- }
- // Ideally every object would know its owning interpreter.
- // Until then, we have to rely on the caller to identify it
- // (but we don't need it in all cases).
- data->interpid = (interp != NULL) ? interp->id : -1;
- data->new_object = new_object;
-}
-
-int
-_PyCrossInterpreterData_InitWithSize(_PyCrossInterpreterData *data,
- PyInterpreterState *interp,
- const size_t size, PyObject *obj,
- xid_newobjectfunc new_object)
-{
- assert(size > 0);
- // For now we always free the shared data in the same interpreter
- // where it was allocated, so the interpreter is required.
- assert(interp != NULL);
- _PyCrossInterpreterData_Init(data, interp, NULL, obj, new_object);
- data->data = PyMem_RawMalloc(size);
- if (data->data == NULL) {
- return -1;
- }
- data->free = PyMem_RawFree;
- return 0;
-}
-
-void
-_PyCrossInterpreterData_Clear(PyInterpreterState *interp,
- _PyCrossInterpreterData *data)
-{
- assert(data != NULL);
- // This must be called in the owning interpreter.
- assert(interp == NULL || data->interpid == interp->id);
- _xidata_clear(data);
-}
-
-static int
-_check_xidata(PyThreadState *tstate, _PyCrossInterpreterData *data)
-{
- // data->data can be anything, including NULL, so we don't check it.
-
- // data->obj may be NULL, so we don't check it.
-
- if (data->interpid < 0) {
- _PyErr_SetString(tstate, PyExc_SystemError, "missing interp");
- return -1;
- }
-
- if (data->new_object == NULL) {
- _PyErr_SetString(tstate, PyExc_SystemError, "missing new_object func");
- return -1;
- }
-
- // data->free may be NULL, so we don't check it.
-
- return 0;
-}
-
-crossinterpdatafunc _PyCrossInterpreterData_Lookup(PyObject *);
-
-/* This is a separate func from _PyCrossInterpreterData_Lookup in order
- to keep the registry code separate. */
-static crossinterpdatafunc
-_lookup_getdata(PyObject *obj)
-{
- crossinterpdatafunc getdata = _PyCrossInterpreterData_Lookup(obj);
- if (getdata == NULL && PyErr_Occurred() == 0)
- PyErr_Format(PyExc_ValueError,
- "%S does not support cross-interpreter data", obj);
- return getdata;
-}
-
-int
-_PyObject_CheckCrossInterpreterData(PyObject *obj)
-{
- crossinterpdatafunc getdata = _lookup_getdata(obj);
- if (getdata == NULL) {
- return -1;
- }
- return 0;
-}
-
-int
-_PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data)
-{
- _PyRuntimeState *runtime = &_PyRuntime;
- PyThreadState *tstate = current_fast_get(runtime);
-#ifdef Py_DEBUG
- // The caller must hold the GIL
- _Py_EnsureTstateNotNULL(tstate);
-#endif
- PyInterpreterState *interp = tstate->interp;
-
- // Reset data before re-populating.
- *data = (_PyCrossInterpreterData){0};
- data->interpid = -1;
-
- // Call the "getdata" func for the object.
- Py_INCREF(obj);
- crossinterpdatafunc getdata = _lookup_getdata(obj);
- if (getdata == NULL) {
- Py_DECREF(obj);
- return -1;
- }
- int res = getdata(tstate, obj, data);
- Py_DECREF(obj);
- if (res != 0) {
- return -1;
- }
-
- // Fill in the blanks and validate the result.
- data->interpid = interp->id;
- if (_check_xidata(tstate, data) != 0) {
- (void)_PyCrossInterpreterData_Release(data);
- return -1;
- }
-
- return 0;
-}
-
-PyObject *
-_PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *data)
-{
- return data->new_object(data);
-}
-
-int
-_Py_CallInInterpreter(PyInterpreterState *interp,
- _Py_simple_func func, void *arg)
-{
- if (interp == current_fast_get(interp->runtime)->interp) {
- return func(arg);
- }
- // XXX Emit a warning if this fails?
- _PyEval_AddPendingCall(interp, (_Py_pending_call_func)func, arg, 0);
- return 0;
-}
-
-int
-_Py_CallInInterpreterAndRawFree(PyInterpreterState *interp,
- _Py_simple_func func, void *arg)
-{
- if (interp == current_fast_get(interp->runtime)->interp) {
- int res = func(arg);
- PyMem_RawFree(arg);
- return res;
- }
- // XXX Emit a warning if this fails?
- _PyEval_AddPendingCall(interp, func, arg, _Py_PENDING_RAWFREE);
- return 0;
-}
-
-static int
-_call_clear_xidata(void *data)
-{
- _xidata_clear((_PyCrossInterpreterData *)data);
- return 0;
-}
-
-static int
-_xidata_release(_PyCrossInterpreterData *data, int rawfree)
-{
- if ((data->data == NULL || data->free == NULL) && data->obj == NULL) {
- // Nothing to release!
- if (rawfree) {
- PyMem_RawFree(data);
- }
- else {
- data->data = NULL;
- }
- return 0;
- }
-
- // Switch to the original interpreter.
- PyInterpreterState *interp = _PyInterpreterState_LookUpID(data->interpid);
- if (interp == NULL) {
- // The interpreter was already destroyed.
- // This function shouldn't have been called.
- // XXX Someone leaked some memory...
- assert(PyErr_Occurred());
- if (rawfree) {
- PyMem_RawFree(data);
- }
- return -1;
- }
-
- // "Release" the data and/or the object.
- if (rawfree) {
- return _Py_CallInInterpreterAndRawFree(interp, _call_clear_xidata, data);
- }
- else {
- return _Py_CallInInterpreter(interp, _call_clear_xidata, data);
- }
-}
-
-int
-_PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
-{
- return _xidata_release(data, 0);
-}
-
-int
-_PyCrossInterpreterData_ReleaseAndRawFree(_PyCrossInterpreterData *data)
-{
- return _xidata_release(data, 1);
-}
-
-/* registry of {type -> crossinterpdatafunc} */
-
-/* For now we use a global registry of shareable classes. An
- alternative would be to add a tp_* slot for a class's
- crossinterpdatafunc. It would be simpler and more efficient. */
-
-static int
-_xidregistry_add_type(struct _xidregistry *xidregistry,
- PyTypeObject *cls, crossinterpdatafunc getdata)
-{
- struct _xidregitem *newhead = PyMem_RawMalloc(sizeof(struct _xidregitem));
- if (newhead == NULL) {
- return -1;
- }
- *newhead = (struct _xidregitem){
- // We do not keep a reference, to avoid keeping the class alive.
- .cls = cls,
- .refcount = 1,
- .getdata = getdata,
- };
- if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) {
- // XXX Assign a callback to clear the entry from the registry?
- newhead->weakref = PyWeakref_NewRef((PyObject *)cls, NULL);
- if (newhead->weakref == NULL) {
- PyMem_RawFree(newhead);
- return -1;
- }
- }
- newhead->next = xidregistry->head;
- if (newhead->next != NULL) {
- newhead->next->prev = newhead;
- }
- xidregistry->head = newhead;
- return 0;
-}
-
-static struct _xidregitem *
-_xidregistry_remove_entry(struct _xidregistry *xidregistry,
- struct _xidregitem *entry)
-{
- struct _xidregitem *next = entry->next;
- if (entry->prev != NULL) {
- assert(entry->prev->next == entry);
- entry->prev->next = next;
- }
- else {
- assert(xidregistry->head == entry);
- xidregistry->head = next;
- }
- if (next != NULL) {
- next->prev = entry->prev;
- }
- Py_XDECREF(entry->weakref);
- PyMem_RawFree(entry);
- return next;
-}
-
-static void
-_xidregistry_clear(struct _xidregistry *xidregistry)
-{
- struct _xidregitem *cur = xidregistry->head;
- xidregistry->head = NULL;
- while (cur != NULL) {
- struct _xidregitem *next = cur->next;
- Py_XDECREF(cur->weakref);
- PyMem_RawFree(cur);
- cur = next;
- }
-}
-
-static struct _xidregitem *
-_xidregistry_find_type(struct _xidregistry *xidregistry, PyTypeObject *cls)
-{
- struct _xidregitem *cur = xidregistry->head;
- while (cur != NULL) {
- if (cur->weakref != NULL) {
- // cur is/was a heap type.
- PyObject *registered = _PyWeakref_GET_REF(cur->weakref);
- if (registered == NULL) {
- // The weakly ref'ed object was freed.
- cur = _xidregistry_remove_entry(xidregistry, cur);
- continue;
- }
- assert(PyType_Check(registered));
- assert(cur->cls == (PyTypeObject *)registered);
- assert(cur->cls->tp_flags & Py_TPFLAGS_HEAPTYPE);
- Py_DECREF(registered);
- }
- if (cur->cls == cls) {
- return cur;
- }
- cur = cur->next;
- }
- return NULL;
-}
-
-static inline struct _xidregistry *
-_get_xidregistry(PyInterpreterState *interp, PyTypeObject *cls)
-{
- struct _xidregistry *xidregistry = &interp->runtime->xidregistry;
- if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) {
- assert(interp->xidregistry.mutex == xidregistry->mutex);
- xidregistry = &interp->xidregistry;
- }
- return xidregistry;
-}
-
-static void _register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry);
-
-static inline void
-_ensure_builtins_xid(PyInterpreterState *interp, struct _xidregistry *xidregistry)
-{
- if (xidregistry != &interp->xidregistry) {
- assert(xidregistry == &interp->runtime->xidregistry);
- if (xidregistry->head == NULL) {
- _register_builtins_for_crossinterpreter_data(xidregistry);
- }
- }
-}
-
-int
-_PyCrossInterpreterData_RegisterClass(PyTypeObject *cls,
- crossinterpdatafunc getdata)
-{
- if (!PyType_Check(cls)) {
- PyErr_Format(PyExc_ValueError, "only classes may be registered");
- return -1;
- }
- if (getdata == NULL) {
- PyErr_Format(PyExc_ValueError, "missing 'getdata' func");
- return -1;
- }
-
- int res = 0;
- PyInterpreterState *interp = _PyInterpreterState_GET();
- struct _xidregistry *xidregistry = _get_xidregistry(interp, cls);
- PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
-
- _ensure_builtins_xid(interp, xidregistry);
-
- struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
- if (matched != NULL) {
- assert(matched->getdata == getdata);
- matched->refcount += 1;
- goto finally;
- }
-
- res = _xidregistry_add_type(xidregistry, cls, getdata);
-
-finally:
- PyThread_release_lock(xidregistry->mutex);
- return res;
-}
-
-int
-_PyCrossInterpreterData_UnregisterClass(PyTypeObject *cls)
-{
- int res = 0;
- PyInterpreterState *interp = _PyInterpreterState_GET();
- struct _xidregistry *xidregistry = _get_xidregistry(interp, cls);
- PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
-
- struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
- if (matched != NULL) {
- assert(matched->refcount > 0);
- matched->refcount -= 1;
- if (matched->refcount == 0) {
- (void)_xidregistry_remove_entry(xidregistry, matched);
- }
- res = 1;
- }
-
- PyThread_release_lock(xidregistry->mutex);
- return res;
-}
-
-
-/* Cross-interpreter objects are looked up by exact match on the class.
- We can reassess this policy when we move from a global registry to a
- tp_* slot. */
-
-crossinterpdatafunc
-_PyCrossInterpreterData_Lookup(PyObject *obj)
-{
- PyTypeObject *cls = Py_TYPE(obj);
-
- PyInterpreterState *interp = _PyInterpreterState_GET();
- struct _xidregistry *xidregistry = _get_xidregistry(interp, cls);
- PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
-
- _ensure_builtins_xid(interp, xidregistry);
-
- struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
- crossinterpdatafunc func = matched != NULL ? matched->getdata : NULL;
-
- PyThread_release_lock(xidregistry->mutex);
- return func;
-}
-
-/* cross-interpreter data for builtin types */
-
-struct _shared_bytes_data {
- char *bytes;
- Py_ssize_t len;
-};
-
-static PyObject *
-_new_bytes_object(_PyCrossInterpreterData *data)
-{
- struct _shared_bytes_data *shared = (struct _shared_bytes_data *)(data->data);
- return PyBytes_FromStringAndSize(shared->bytes, shared->len);
-}
-
-static int
-_bytes_shared(PyThreadState *tstate, PyObject *obj,
- _PyCrossInterpreterData *data)
-{
- if (_PyCrossInterpreterData_InitWithSize(
- data, tstate->interp, sizeof(struct _shared_bytes_data), obj,
- _new_bytes_object
- ) < 0)
- {
- return -1;
- }
- struct _shared_bytes_data *shared = (struct _shared_bytes_data *)data->data;
- if (PyBytes_AsStringAndSize(obj, &shared->bytes, &shared->len) < 0) {
- _PyCrossInterpreterData_Clear(tstate->interp, data);
- return -1;
- }
- return 0;
-}
-
-struct _shared_str_data {
- int kind;
- const void *buffer;
- Py_ssize_t len;
-};
-
-static PyObject *
-_new_str_object(_PyCrossInterpreterData *data)
-{
- struct _shared_str_data *shared = (struct _shared_str_data *)(data->data);
- return PyUnicode_FromKindAndData(shared->kind, shared->buffer, shared->len);
-}
-
-static int
-_str_shared(PyThreadState *tstate, PyObject *obj,
- _PyCrossInterpreterData *data)
-{
- if (_PyCrossInterpreterData_InitWithSize(
- data, tstate->interp, sizeof(struct _shared_str_data), obj,
- _new_str_object
- ) < 0)
- {
- return -1;
- }
- struct _shared_str_data *shared = (struct _shared_str_data *)data->data;
- shared->kind = PyUnicode_KIND(obj);
- shared->buffer = PyUnicode_DATA(obj);
- shared->len = PyUnicode_GET_LENGTH(obj);
- return 0;
-}
-
-static PyObject *
-_new_long_object(_PyCrossInterpreterData *data)
-{
- return PyLong_FromSsize_t((Py_ssize_t)(data->data));
-}
-
-static int
-_long_shared(PyThreadState *tstate, PyObject *obj,
- _PyCrossInterpreterData *data)
-{
- /* Note that this means the size of shareable ints is bounded by
- * sys.maxsize. Hence on 32-bit architectures that is half the
- * size of maximum shareable ints on 64-bit.
- */
- Py_ssize_t value = PyLong_AsSsize_t(obj);
- if (value == -1 && PyErr_Occurred()) {
- if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
- PyErr_SetString(PyExc_OverflowError, "try sending as bytes");
- }
- return -1;
- }
- _PyCrossInterpreterData_Init(data, tstate->interp, (void *)value, NULL,
- _new_long_object);
- // data->obj and data->free remain NULL
- return 0;
-}
-
-static PyObject *
-_new_none_object(_PyCrossInterpreterData *data)
-{
- // XXX Singleton refcounts are problematic across interpreters...
- return Py_NewRef(Py_None);
-}
-
-static int
-_none_shared(PyThreadState *tstate, PyObject *obj,
- _PyCrossInterpreterData *data)
-{
- _PyCrossInterpreterData_Init(data, tstate->interp, NULL, NULL,
- _new_none_object);
- // data->data, data->obj and data->free remain NULL
- return 0;
-}
-
-static void
-_register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry)
-{
- // None
- if (_xidregistry_add_type(xidregistry, (PyTypeObject *)PyObject_Type(Py_None), _none_shared) != 0) {
- Py_FatalError("could not register None for cross-interpreter sharing");
- }
-
- // int
- if (_xidregistry_add_type(xidregistry, &PyLong_Type, _long_shared) != 0) {
- Py_FatalError("could not register int for cross-interpreter sharing");
- }
-
- // bytes
- if (_xidregistry_add_type(xidregistry, &PyBytes_Type, _bytes_shared) != 0) {
- Py_FatalError("could not register bytes for cross-interpreter sharing");
- }
-
- // str
- if (_xidregistry_add_type(xidregistry, &PyUnicode_Type, _str_shared) != 0) {
- Py_FatalError("could not register str for cross-interpreter sharing");
- }
-}
-
-
/*************/
/* Other API */
/*************/