https://github.com/python/cpython/commit/e54e8288521c1bd5fa496dfa281d034af20d8f42
commit: e54e8288521c1bd5fa496dfa281d034af20d8f42
branch: main
author: Eric Snow <ericsnowcurren...@gmail.com>
committer: ericsnowcurrently <ericsnowcurren...@gmail.com>
date: 2025-04-24T18:25:29-06:00
summary:

gh-132776: Cleanup for XIBufferViewType (gh-132821)

* add notes
* rename XIBufferViewObject to xibufferview
* move memoryview XIData code to memoryobject.c

files:
M Include/internal/pycore_interp_structs.h
M Include/internal/pycore_memoryobject.h
M Modules/_interpreters_common.h
M Modules/_interpretersmodule.c
M Objects/memoryobject.c
M Python/crossinterp.c
M Python/crossinterp_data_lookup.h
M Python/pylifecycle.c

diff --git a/Include/internal/pycore_interp_structs.h 
b/Include/internal/pycore_interp_structs.h
index af6ee3ab48939f..3c2b2d30028280 100644
--- a/Include/internal/pycore_interp_structs.h
+++ b/Include/internal/pycore_interp_structs.h
@@ -9,6 +9,7 @@ extern "C" {
 
 #include "pycore_ast_state.h"     // struct ast_state
 #include "pycore_llist.h"         // struct llist_node
+#include "pycore_memoryobject.h"  // struct _memoryobject_state
 #include "pycore_opcode_utils.h"  // NUM_COMMON_CONSTANTS
 #include "pycore_pymath.h"        // _PY_SHORT_FLOAT_REPR
 #include "pycore_structs.h"       // PyHamtObject
@@ -912,9 +913,10 @@ struct _is {
     struct _dtoa_state dtoa;
     struct _py_func_state func_state;
     struct _py_code_state code_state;
-
     struct _Py_dict_state dict_state;
     struct _Py_exc_state exc_state;
+    struct _memoryobject_state memobj_state;
+
     struct _Py_mem_interp_free_queue mem_free_queue;
 
     struct ast_state ast;
diff --git a/Include/internal/pycore_memoryobject.h 
b/Include/internal/pycore_memoryobject.h
index 62e204fcbf6533..43e37330e1b07f 100644
--- a/Include/internal/pycore_memoryobject.h
+++ b/Include/internal/pycore_memoryobject.h
@@ -8,6 +8,17 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
+struct _memoryobject_state {
+    PyTypeObject *XIBufferViewType;
+};
+
+extern PyStatus _PyMemoryView_InitTypes(PyInterpreterState *);
+extern void _PyMemoryView_FiniTypes(PyInterpreterState *);
+
+// exported for _interpreters module
+PyAPI_FUNC(PyTypeObject *) _PyMemoryView_GetXIBuffewViewType(void);
+
+
 extern PyTypeObject _PyManagedBuffer_Type;
 
 PyObject *
diff --git a/Modules/_interpreters_common.h b/Modules/_interpreters_common.h
index a6c639feea5d14..bc919485885294 100644
--- a/Modules/_interpreters_common.h
+++ b/Modules/_interpreters_common.h
@@ -5,6 +5,7 @@
     _RESOLVE_MODINIT_FUNC_NAME(NAME)
 
 
+#ifdef REGISTERS_HEAP_TYPES
 static int
 ensure_xid_class(PyTypeObject *cls, xidatafunc getdata)
 {
@@ -16,7 +17,6 @@ ensure_xid_class(PyTypeObject *cls, xidatafunc getdata)
     return _PyXIData_RegisterClass(&ctx, cls, getdata);
 }
 
-#ifdef REGISTERS_HEAP_TYPES
 static int
 clear_xid_class(PyTypeObject *cls)
 {
diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c
index d9c2d2bfd081f7..f636ce882c3023 100644
--- a/Modules/_interpretersmodule.c
+++ b/Modules/_interpretersmodule.c
@@ -9,6 +9,7 @@
 #include "pycore_code.h"          // _PyCode_HAS_EXECUTORS()
 #include "pycore_crossinterp.h"   // _PyXIData_t
 #include "pycore_interp.h"        // _PyInterpreterState_IDIncref()
+#include "pycore_memoryobject.h"  // _PyMemoryView_GetXIBuffewViewType()
 #include "pycore_modsupport.h"    // _PyArg_BadArgument()
 #include "pycore_namespace.h"     // _PyNamespace_New()
 #include "pycore_pybuffer.h"      // _PyBuffer_ReleaseInInterpreterAndRawFree()
@@ -36,23 +37,6 @@ _get_current_interp(void)
 #define look_up_interp _PyInterpreterState_LookUpIDObject
 
 
-static PyObject *
-_get_current_module(void)
-{
-    PyObject *name = PyUnicode_FromString(MODULE_NAME_STR);
-    if (name == NULL) {
-        return NULL;
-    }
-    PyObject *mod = PyImport_GetModule(name);
-    Py_DECREF(name);
-    if (mod == NULL) {
-        return NULL;
-    }
-    assert(mod != Py_None);
-    return mod;
-}
-
-
 static int
 is_running_main(PyInterpreterState *interp)
 {
@@ -71,204 +55,10 @@ is_running_main(PyInterpreterState *interp)
 }
 
 
-/* Cross-interpreter Buffer Views *******************************************/
-
-// XXX Release when the original interpreter is destroyed.
-
-typedef struct {
-    PyObject base;
-    Py_buffer *view;
-    int64_t interpid;
-} XIBufferViewObject;
-
-#define XIBufferViewObject_CAST(op) ((XIBufferViewObject *)(op))
-
-static PyObject *
-xibufferview_from_buffer(PyTypeObject *cls, Py_buffer *view, int64_t interpid)
-{
-    assert(interpid >= 0);
-
-    Py_buffer *copied = PyMem_RawMalloc(sizeof(Py_buffer));
-    if (copied == NULL) {
-        return NULL;
-    }
-    /* This steals the view->obj reference  */
-    *copied = *view;
-
-    XIBufferViewObject *self = PyObject_Malloc(sizeof(XIBufferViewObject));
-    if (self == NULL) {
-        PyMem_RawFree(copied);
-        return NULL;
-    }
-    PyObject_Init(&self->base, cls);
-    *self = (XIBufferViewObject){
-        .base = self->base,
-        .view = copied,
-        .interpid = interpid,
-    };
-    return (PyObject *)self;
-}
-
-static void
-xibufferview_dealloc(PyObject *op)
-{
-    XIBufferViewObject *self = XIBufferViewObject_CAST(op);
-
-    if (self->view != NULL) {
-        PyInterpreterState *interp =
-                        _PyInterpreterState_LookUpID(self->interpid);
-        if (interp == NULL) {
-            /* The interpreter is no longer alive. */
-            PyErr_Clear();
-            PyMem_RawFree(self->view);
-        }
-        else {
-            if (_PyBuffer_ReleaseInInterpreterAndRawFree(interp,
-                                                         self->view) < 0)
-            {
-                // XXX Emit a warning?
-                PyErr_Clear();
-            }
-        }
-    }
-
-    PyTypeObject *tp = Py_TYPE(self);
-    tp->tp_free(self);
-    /* "Instances of heap-allocated types hold a reference to their type."
-     * See: 
https://docs.python.org/3.11/howto/isolating-extensions.html#garbage-collection-protocol
-     * See: 
https://docs.python.org/3.11/c-api/typeobj.html#c.PyTypeObject.tp_traverse
-    */
-    // XXX Why don't we implement Py_TPFLAGS_HAVE_GC, e.g. Py_tp_traverse,
-    // like we do for _abc._abc_data?
-    Py_DECREF(tp);
-}
-
-static int
-xibufferview_getbuf(PyObject *op, Py_buffer *view, int flags)
-{
-    /* Only PyMemoryView_FromObject() should ever call this,
-       via _memoryview_from_xid() below. */
-    XIBufferViewObject *self = XIBufferViewObject_CAST(op);
-    *view = *self->view;
-    view->obj = op;
-    // XXX Should we leave it alone?
-    view->internal = NULL;
-    return 0;
-}
-
-static PyType_Slot XIBufferViewType_slots[] = {
-    {Py_tp_dealloc, xibufferview_dealloc},
-    {Py_bf_getbuffer, xibufferview_getbuf},
-    // We don't bother with Py_bf_releasebuffer since we don't need it.
-    {0, NULL},
-};
-
-static PyType_Spec XIBufferViewType_spec = {
-    .name = MODULE_NAME_STR ".CrossInterpreterBufferView",
-    .basicsize = sizeof(XIBufferViewObject),
-    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
-              Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE),
-    .slots = XIBufferViewType_slots,
-};
-
-
-static PyTypeObject * _get_current_xibufferview_type(void);
-
-
-struct xibuffer {
-    Py_buffer view;
-    int used;
-};
-
-static PyObject *
-_memoryview_from_xid(_PyXIData_t *data)
-{
-    assert(_PyXIData_DATA(data) != NULL);
-    assert(_PyXIData_OBJ(data) == NULL);
-    assert(_PyXIData_INTERPID(data) >= 0);
-    struct xibuffer *view = (struct xibuffer *)_PyXIData_DATA(data);
-    assert(!view->used);
-
-    PyTypeObject *cls = _get_current_xibufferview_type();
-    if (cls == NULL) {
-        return NULL;
-    }
-
-    PyObject *obj = xibufferview_from_buffer(
-                        cls, &view->view, _PyXIData_INTERPID(data));
-    if (obj == NULL) {
-        return NULL;
-    }
-    PyObject *res = PyMemoryView_FromObject(obj);
-    if (res == NULL) {
-        Py_DECREF(obj);
-        return NULL;
-    }
-    view->used = 1;
-    return res;
-}
-
-static void
-_pybuffer_shared_free(void* data)
-{
-    struct xibuffer *view = (struct xibuffer *)data;
-    if (!view->used) {
-        PyBuffer_Release(&view->view);
-    }
-    PyMem_RawFree(data);
-}
-
-static int
-_pybuffer_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
-{
-    struct xibuffer *view = PyMem_RawMalloc(sizeof(struct xibuffer));
-    if (view == NULL) {
-        return -1;
-    }
-    view->used = 0;
-    if (PyObject_GetBuffer(obj, &view->view, PyBUF_FULL_RO) < 0) {
-        PyMem_RawFree(view);
-        return -1;
-    }
-    _PyXIData_Init(data, tstate->interp, view, NULL, _memoryview_from_xid);
-    data->free = _pybuffer_shared_free;
-    return 0;
-}
-
-static int
-register_memoryview_xid(PyObject *mod, PyTypeObject **p_state)
-{
-    // XIBufferView
-    assert(*p_state == NULL);
-    PyTypeObject *cls = (PyTypeObject *)PyType_FromModuleAndSpec(
-                mod, &XIBufferViewType_spec, NULL);
-    if (cls == NULL) {
-        return -1;
-    }
-    if (PyModule_AddType(mod, cls) < 0) {
-        Py_DECREF(cls);
-        return -1;
-    }
-    *p_state = cls;
-
-    // Register XID for the builtin memoryview type.
-    if (ensure_xid_class(&PyMemoryView_Type, _pybuffer_shared) < 0) {
-        return -1;
-    }
-    // We don't ever bother un-registering memoryview.
-
-    return 0;
-}
-
-
-
 /* module state *************************************************************/
 
 typedef struct {
     int _notused;
-
-    /* heap types */
-    PyTypeObject *XIBufferViewType;
 } module_state;
 
 static inline module_state *
@@ -280,51 +70,19 @@ get_module_state(PyObject *mod)
     return state;
 }
 
-static module_state *
-_get_current_module_state(void)
-{
-    PyObject *mod = _get_current_module();
-    if (mod == NULL) {
-        // XXX import it?
-        PyErr_SetString(PyExc_RuntimeError,
-                        MODULE_NAME_STR " module not imported yet");
-        return NULL;
-    }
-    module_state *state = get_module_state(mod);
-    Py_DECREF(mod);
-    return state;
-}
-
 static int
 traverse_module_state(module_state *state, visitproc visit, void *arg)
 {
-    /* heap types */
-    Py_VISIT(state->XIBufferViewType);
-
     return 0;
 }
 
 static int
 clear_module_state(module_state *state)
 {
-    /* heap types */
-    Py_CLEAR(state->XIBufferViewType);
-
     return 0;
 }
 
 
-static PyTypeObject *
-_get_current_xibufferview_type(void)
-{
-    module_state *state = _get_current_module_state();
-    if (state == NULL) {
-        return NULL;
-    }
-    return state->XIBufferViewType;
-}
-
-
 /* Python code **************************************************************/
 
 static const char *
@@ -1545,6 +1303,7 @@ module_exec(PyObject *mod)
 {
     PyInterpreterState *interp = PyInterpreterState_Get();
     module_state *state = get_module_state(mod);
+    (void)state;
 
     _PyXIData_lookup_context_t ctx;
     if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
@@ -1576,9 +1335,14 @@ module_exec(PyObject *mod)
         goto error;
     }
 
-    if (register_memoryview_xid(mod, &state->XIBufferViewType) < 0) {
+    PyTypeObject *XIBufferViewType = _PyMemoryView_GetXIBuffewViewType();
+    if (XIBufferViewType == NULL) {
         goto error;
     }
+    if (PyModule_AddType(mod, XIBufferViewType) < 0) {
+        Py_DECREF(XIBufferViewType);
+        return -1;
+    }
 
     return 0;
 
diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c
index cf673fb379edcd..9098c27c564d78 100644
--- a/Objects/memoryobject.c
+++ b/Objects/memoryobject.c
@@ -12,8 +12,11 @@
 
 #include "Python.h"
 #include "pycore_abstract.h"      // _PyIndex_Check()
+#include "pycore_initconfig.h"    // _PyStatus_OK()
+#include "pycore_interp.h"        // _PyInterpreterState_LookUpID()
 #include "pycore_memoryobject.h"  // _PyManagedBuffer_Type
 #include "pycore_object.h"        // _PyObject_GC_UNTRACK()
+#include "pycore_pybuffer.h"      // _PyBuffer_ReleaseInInterpreterAndRawFree()
 #include "pycore_strhex.h"        // _Py_strhex_with_sep()
 #include <stddef.h>               // offsetof()
 
@@ -3556,6 +3559,252 @@ PyTypeObject _PyMemoryIter_Type = {
     .tp_iternext = memoryiter_next,
 };
 
+
+/**************************************************************************/
+/*                   Memoryview Cross-interpreter Data                    */
+/**************************************************************************/
+
+/* When a memoryview object is "shared" between interpreters,
+ * its underlying "buffer" memory is actually shared, rather than just
+ * copied.  This facilitates efficient use of that data where otherwise
+ * interpreters are strictly isolated.  However, this also means that
+ * the underlying data is subject to the complexities of thread-safety,
+ * which the user must manage carefully.
+ *
+ * When the memoryview is "shared", it is essentially copied in the same
+ * way as PyMemory_FromObject() does, but in another interpreter.
+ * The Py_buffer value is copied like normal, including the "buf" pointer,
+ * with one key exception.
+ *
+ * When a Py_buffer is released and it holds a reference to an object,
+ * that object gets a chance to call its bf_releasebuffer() (if any)
+ * before the object is decref'ed.  The same is true with the memoryview
+ * tp_dealloc, which essentially calls PyBuffer_Release().
+ *
+ * The problem for a Py_buffer shared between two interpreters is that
+ * the naive approach breaks interpreter isolation.  Operations on an
+ * object must only happen while that object's interpreter is active.
+ * If the copied mv->view.obj pointed to the original memoryview then
+ * the PyBuffer_Release() would happen under the wrong interpreter.
+ *
+ * To work around this, we set mv->view.obj on the copied memoryview
+ * to a wrapper object with the only job of releasing the original
+ * buffer in a cross-interpreter-safe way.
+ */
+
+// XXX Note that there is still an issue to sort out, where the original
+// interpreter is destroyed but code in another interpreter is still
+// using dependent buffers.  Using such buffers segfaults.  This will
+// require a careful fix.  In the meantime, users will have to be
+// diligent about avoiding the problematic situation.
+
+typedef struct {
+    PyObject base;
+    Py_buffer *view;
+    int64_t interpid;
+} xibufferview;
+
+static PyObject *
+xibufferview_from_buffer(PyTypeObject *cls, Py_buffer *view, int64_t interpid)
+{
+    assert(interpid >= 0);
+
+    Py_buffer *copied = PyMem_RawMalloc(sizeof(Py_buffer));
+    if (copied == NULL) {
+        return NULL;
+    }
+    /* This steals the view->obj reference  */
+    *copied = *view;
+
+    xibufferview *self = PyObject_Malloc(sizeof(xibufferview));
+    if (self == NULL) {
+        PyMem_RawFree(copied);
+        return NULL;
+    }
+    *self = (xibufferview){
+        .view = copied,
+        .interpid = interpid,
+    };
+    PyObject_Init(&self->base, cls);
+    return (PyObject *)self;
+}
+
+static void
+xibufferview_dealloc(PyObject *op)
+{
+    xibufferview *self = (xibufferview *)op;
+
+    if (self->view != NULL) {
+        PyInterpreterState *interp =
+                        _PyInterpreterState_LookUpID(self->interpid);
+        if (interp == NULL) {
+            /* The interpreter is no longer alive. */
+            PyErr_Clear();
+            PyMem_RawFree(self->view);
+        }
+        else {
+            if (_PyBuffer_ReleaseInInterpreterAndRawFree(interp,
+                                                         self->view) < 0)
+            {
+                // XXX Emit a warning?
+                PyErr_Clear();
+            }
+        }
+    }
+
+    PyTypeObject *tp = Py_TYPE(self);
+    tp->tp_free(self);
+    /* "Instances of heap-allocated types hold a reference to their type."
+     * See: 
https://docs.python.org/3.11/howto/isolating-extensions.html#garbage-collection-protocol
+     * See: 
https://docs.python.org/3.11/c-api/typeobj.html#c.PyTypeObject.tp_traverse
+    */
+    // XXX Why don't we implement Py_TPFLAGS_HAVE_GC, e.g. Py_tp_traverse,
+    // like we do for _abc._abc_data?
+    Py_DECREF(tp);
+}
+
+static int
+xibufferview_getbuf(PyObject *op, Py_buffer *view, int flags)
+{
+    /* Only PyMemoryView_FromObject() should ever call this,
+       via _memoryview_from_xid() below. */
+    xibufferview *self = (xibufferview *)op;
+    *view = *self->view;
+    /* This is the workaround mentioned earlier. */
+    view->obj = op;
+    // XXX Should we leave it alone?
+    view->internal = NULL;
+    return 0;
+}
+
+static PyType_Slot XIBufferViewType_slots[] = {
+    {Py_tp_dealloc, xibufferview_dealloc},
+    {Py_bf_getbuffer, xibufferview_getbuf},
+    // We don't bother with Py_bf_releasebuffer since we don't need it.
+    {0, NULL},
+};
+
+static PyType_Spec XIBufferViewType_spec = {
+    .name = "_interpreters.CrossInterpreterBufferView",
+    .basicsize = sizeof(xibufferview),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+              Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = XIBufferViewType_slots,
+};
+
+PyTypeObject *
+_PyMemoryView_GetXIBuffewViewType(void)
+{
+    PyInterpreterState *interp = _PyInterpreterState_GET();
+    PyTypeObject *cls = interp->memobj_state.XIBufferViewType;
+    if (cls == NULL) {
+        cls = (PyTypeObject *)PyType_FromSpec(&XIBufferViewType_spec);
+        if (cls == NULL) {
+            return NULL;
+        }
+        /* It gets cleaned up during interpreter finalization
+         * in clear_xidata_state(). */
+        interp->memobj_state.XIBufferViewType = cls;
+    }
+    Py_INCREF(cls);
+    return cls;
+}
+
+
+struct xibuffer {
+    Py_buffer view;
+    int used;
+};
+
+static PyObject *
+_memoryview_from_xid(_PyXIData_t *data)
+{
+    assert(_PyXIData_DATA(data) != NULL);
+    assert(_PyXIData_OBJ(data) == NULL);
+    assert(_PyXIData_INTERPID(data) >= 0);
+    struct xibuffer *view = (struct xibuffer *)_PyXIData_DATA(data);
+    assert(!view->used);
+
+    PyTypeObject *cls = _PyMemoryView_GetXIBuffewViewType();
+    if (cls == NULL) {
+        return NULL;
+    }
+
+    PyObject *obj = xibufferview_from_buffer(
+                        cls, &view->view, _PyXIData_INTERPID(data));
+    if (obj == NULL) {
+        return NULL;
+    }
+    PyObject *res = PyMemoryView_FromObject(obj);
+    if (res == NULL) {
+        Py_DECREF(obj);
+        return NULL;
+    }
+    view->used = 1;
+    return res;
+}
+
+static void
+_pybuffer_shared_free(void* data)
+{
+    struct xibuffer *view = (struct xibuffer *)data;
+    if (!view->used) {
+        PyBuffer_Release(&view->view);
+    }
+    PyMem_RawFree(data);
+}
+
+static int
+_pybuffer_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
+{
+    struct xibuffer *view = PyMem_RawMalloc(sizeof(struct xibuffer));
+    if (view == NULL) {
+        return -1;
+    }
+    view->used = 0;
+    /* This will increment the memoryview's export count, which won't get
+     * decremented until the view sent to other interpreters is released. */
+    if (PyObject_GetBuffer(obj, &view->view, PyBUF_FULL_RO) < 0) {
+        PyMem_RawFree(view);
+        return -1;
+    }
+    /* The view holds a reference to the object, so we don't worry
+     * about also tracking it on the cross-interpreter data. */
+    _PyXIData_Init(data, tstate->interp, view, NULL, _memoryview_from_xid);
+    data->free = _pybuffer_shared_free;
+    return 0;
+}
+
+
+static int
+init_xidata_types(PyInterpreterState *interp)
+{
+    /* Register an XI data handler for memoryview. */
+    // This is necessary only as long as we don't have a tp_ slot for it.
+    _PyXIData_lookup_context_t ctx;
+    if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
+        return -1;
+    }
+    if (_PyXIData_RegisterClass(&ctx, &PyMemoryView_Type, _pybuffer_shared) < 
0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static void
+clear_xidata_types(PyInterpreterState *interp)
+{
+    if (interp->memobj_state.XIBufferViewType != NULL) {
+        Py_CLEAR(interp->memobj_state.XIBufferViewType);
+    }
+}
+
+
+/**************************************************************************/
+/*                            Memoryview Type                             */
+/**************************************************************************/
+
 PyTypeObject PyMemoryView_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     "memoryview",                             /* tp_name */
@@ -3597,3 +3846,27 @@ PyTypeObject PyMemoryView_Type = {
     0,                                        /* tp_alloc */
     memoryview,                               /* tp_new */
 };
+
+
+/**************************************************************************/
+/*                           Runtime Lifecycle                            */
+/**************************************************************************/
+
+PyStatus
+_PyMemoryView_InitTypes(PyInterpreterState *interp)
+{
+    /* interp->memobj_state.XIBufferViewType is initialized lazily
+     * in _PyMemoryView_GetXIBuffewViewType(). */
+
+    if (init_xidata_types(interp) < 0) {
+        return _PyStatus_ERR("failed to initialize cross-interpreter data 
types");
+    }
+
+    return _PyStatus_OK();
+}
+
+void
+_PyMemoryView_FiniTypes(PyInterpreterState *interp)
+{
+    clear_xidata_types(interp);
+}
diff --git a/Python/crossinterp.c b/Python/crossinterp.c
index 094bbbe54f2a75..7a19cc3da1f3f8 100644
--- a/Python/crossinterp.c
+++ b/Python/crossinterp.c
@@ -1902,7 +1902,25 @@ _PyXI_Fini(PyInterpreterState *interp)
 PyStatus
 _PyXI_InitTypes(PyInterpreterState *interp)
 {
-    if (init_static_exctypes(&_PyXI_GET_STATE(interp)->exceptions, interp) < 
0) {
+    if (_Py_IsMainInterpreter(interp)) {
+        _PyXI_global_state_t *global_state = _PyXI_GET_GLOBAL_STATE(interp);
+        if (global_state == NULL) {
+            PyErr_PrintEx(0);
+            return _PyStatus_ERR(
+                    "failed to get global cross-interpreter state");
+        }
+        xid_lookup_init(&global_state->data_lookup);
+    }
+
+    _PyXI_state_t *state = _PyXI_GET_STATE(interp);
+    if (state == NULL) {
+        PyErr_PrintEx(0);
+        return _PyStatus_ERR(
+                "failed to get interpreter's cross-interpreter state");
+    }
+    xid_lookup_init(&state->data_lookup);
+
+    if (init_static_exctypes(&state->exceptions, interp) < 0) {
         PyErr_PrintEx(0);
         return _PyStatus_ERR(
                 "failed to initialize the cross-interpreter exception types");
@@ -1915,9 +1933,21 @@ _PyXI_InitTypes(PyInterpreterState *interp)
 void
 _PyXI_FiniTypes(PyInterpreterState *interp)
 {
-    // We would finalize heap types here too but that leads to ref leaks.
-    // Instead, we finalize them in _PyXI_Fini().
-    fini_static_exctypes(&_PyXI_GET_STATE(interp)->exceptions, interp);
+    _PyXI_state_t *state = _PyXI_GET_STATE(interp);
+    if (state != NULL) {
+        // We would finalize heap types here too but that leads to ref leaks.
+        // Instead, we finalize them in _PyXI_Fini().
+        fini_static_exctypes(&state->exceptions, interp);
+
+        xid_lookup_fini(&state->data_lookup);
+    }
+
+    if (_Py_IsMainInterpreter(interp)) {
+        _PyXI_global_state_t *global_state = _PyXI_GET_GLOBAL_STATE(interp);
+        if (global_state != NULL) {
+            xid_lookup_fini(&global_state->data_lookup);
+        }
+    }
 }
 
 
diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h
index 48e5d9762cd697..6e75e2475280cf 100644
--- a/Python/crossinterp_data_lookup.h
+++ b/Python/crossinterp_data_lookup.h
@@ -174,6 +174,7 @@ _lookup_getdata_from_registry(dlcontext_t *ctx, PyObject 
*obj)
     PyTypeObject *cls = Py_TYPE(obj);
 
     dlregistry_t *xidregistry = _get_xidregistry_for_type(ctx, cls);
+    assert(xidregistry->initialized);
     _xidregistry_lock(xidregistry);
 
     dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls);
@@ -190,6 +191,7 @@ static int
 _xidregistry_add_type(dlregistry_t *xidregistry,
                       PyTypeObject *cls, xidatafunc getdata)
 {
+    assert(xidregistry->initialized);
     dlregitem_t *newhead = PyMem_RawMalloc(sizeof(dlregitem_t));
     if (newhead == NULL) {
         return -1;
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 1b9832bff17ba5..4c25198b702c57 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -14,6 +14,7 @@
 #include "pycore_global_objects_fini_generated.h"  // 
_PyStaticObjects_CheckRefcnt()
 #include "pycore_initconfig.h"    // _PyStatus_OK()
 #include "pycore_long.h"          // _PyLong_InitTypes()
+#include "pycore_memoryobject.h"  // _PyMemoryView_InitTypes()
 #include "pycore_object.h"        // _PyDebug_PrintTotalRefs()
 #include "pycore_obmalloc.h"      // _PyMem_init_obmalloc()
 #include "pycore_optimizer.h"     // _Py_Executors_InvalidateAll
@@ -754,6 +755,11 @@ pycore_init_types(PyInterpreterState *interp)
         return status;
     }
 
+    status = _PyMemoryView_InitTypes(interp);
+    if (_PyStatus_EXCEPTION(status)) {
+        return status;
+    }
+
     return _PyStatus_OK();
 }
 
@@ -1845,6 +1851,7 @@ finalize_interp_types(PyInterpreterState *interp)
     _PyTypes_FiniExtTypes(interp);
     _PyUnicode_FiniTypes(interp);
     _PySys_FiniTypes(interp);
+    _PyMemoryView_FiniTypes(interp);
     _PyXI_FiniTypes(interp);
     _PyExc_Fini(interp);
     _PyFloat_FiniType(interp);

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to