]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-76785: Improved Subinterpreters Compatibility with 3.12 (2/2) (gh-126707)
authorEric Snow <ericsnowcurrently@gmail.com>
Tue, 12 Nov 2024 17:41:51 +0000 (10:41 -0700)
committerGitHub <noreply@github.com>
Tue, 12 Nov 2024 17:41:51 +0000 (10:41 -0700)
These changes makes it easier to backport the _interpreters, _interpqueues, and _interpchannels modules to Python 3.12.

This involves the following:

* add the _PyXI_GET_STATE() and _PyXI_GET_GLOBAL_STATE() macros
* add _PyXIData_lookup_context_t and _PyXIData_GetLookupContext()
* add _Py_xi_state_init() and _Py_xi_state_fini()

Include/internal/pycore_crossinterp.h
Include/internal/pycore_crossinterp_data_registry.h
Modules/_interpchannelsmodule.c
Modules/_interpqueuesmodule.c
Modules/_interpreters_common.h
Modules/_interpretersmodule.c
Modules/_testinternalcapi.c
Python/crossinterp.c
Python/crossinterp_data_lookup.h

index 66719796aeee227261307f6f0a2e23b923953b53..69a60d73e05c269dc92707ecb4d3f3ce476a3a69 100644 (file)
@@ -100,9 +100,26 @@ typedef int (*xidatafunc)(PyThreadState *tstate, PyObject *, _PyXIData_t *);
 
 typedef struct _xid_lookup_state _PyXIData_lookup_t;
 
-PyAPI_FUNC(xidatafunc) _PyXIData_Lookup(PyObject *);
-PyAPI_FUNC(int) _PyObject_CheckXIData(PyObject *);
-PyAPI_FUNC(int) _PyObject_GetXIData(PyObject *, _PyXIData_t *);
+typedef struct {
+    _PyXIData_lookup_t *global;
+    _PyXIData_lookup_t *local;
+    PyObject *PyExc_NotShareableError;
+} _PyXIData_lookup_context_t;
+
+PyAPI_FUNC(int) _PyXIData_GetLookupContext(
+        PyInterpreterState *,
+        _PyXIData_lookup_context_t *);
+
+PyAPI_FUNC(xidatafunc) _PyXIData_Lookup(
+        _PyXIData_lookup_context_t *,
+        PyObject *);
+PyAPI_FUNC(int) _PyObject_CheckXIData(
+        _PyXIData_lookup_context_t *,
+        PyObject *);
+PyAPI_FUNC(int) _PyObject_GetXIData(
+        _PyXIData_lookup_context_t *,
+        PyObject *,
+        _PyXIData_t *);
 
 
 /* using cross-interpreter data */
@@ -173,12 +190,20 @@ typedef struct {
     } exceptions;
 } _PyXI_state_t;
 
+#define _PyXI_GET_GLOBAL_STATE(interp) (&(interp)->runtime->xi)
+#define _PyXI_GET_STATE(interp) (&(interp)->xi)
+
+#ifndef Py_BUILD_CORE_MODULE
 extern PyStatus _PyXI_Init(PyInterpreterState *interp);
 extern void _PyXI_Fini(PyInterpreterState *interp);
 extern PyStatus _PyXI_InitTypes(PyInterpreterState *interp);
 extern void _PyXI_FiniTypes(PyInterpreterState *interp);
+#endif  // Py_BUILD_CORE_MODULE
 
-#define _PyInterpreterState_GetXIState(interp) (&(interp)->xi)
+int _Py_xi_global_state_init(_PyXI_global_state_t *);
+void _Py_xi_global_state_fini(_PyXI_global_state_t *);
+int _Py_xi_state_init(_PyXI_state_t *, PyInterpreterState *);
+void _Py_xi_state_fini(_PyXI_state_t *, PyInterpreterState *);
 
 
 /***************************/
index 04f25bc05fd1b80333368b3428e68cb3bd201efd..bbad4de770857f16b6cb166cdddd15b28c2b65d3 100644 (file)
@@ -27,8 +27,13 @@ typedef struct {
     _PyXIData_regitem_t *head;
 } _PyXIData_registry_t;
 
-PyAPI_FUNC(int) _PyXIData_RegisterClass(PyTypeObject *, xidatafunc);
-PyAPI_FUNC(int) _PyXIData_UnregisterClass(PyTypeObject *);
+PyAPI_FUNC(int) _PyXIData_RegisterClass(
+    _PyXIData_lookup_context_t *,
+    PyTypeObject *,
+    xidatafunc);
+PyAPI_FUNC(int) _PyXIData_UnregisterClass(
+    _PyXIData_lookup_context_t *,
+    PyTypeObject *);
 
 struct _xid_lookup_state {
     // XXX Remove this field once we have a tp_* slot.
index cd3c50269385680bf491efddcfec8691689c10d4..75d69ade1d3c9b3f0fe2adfb311d05868b682177 100644 (file)
@@ -1758,6 +1758,11 @@ channel_send(_channels *channels, int64_t cid, PyObject *obj,
     }
     int64_t interpid = PyInterpreterState_GetID(interp);
 
+    _PyXIData_lookup_context_t ctx;
+    if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
+        return -1;
+    }
+
     // Look up the channel.
     PyThread_type_lock mutex = NULL;
     _channel_state *chan = NULL;
@@ -1779,7 +1784,7 @@ channel_send(_channels *channels, int64_t cid, PyObject *obj,
         PyThread_release_lock(mutex);
         return -1;
     }
-    if (_PyObject_GetXIData(obj, data) != 0) {
+    if (_PyObject_GetXIData(&ctx, obj, data) != 0) {
         PyThread_release_lock(mutex);
         GLOBAL_FREE(data);
         return -1;
index 8d0e223db7ff1947860ee3ce9cef43d55961383b..808938a9e8cd16a12e755d6ae5aee9dff2f7b7d4 100644 (file)
@@ -1127,6 +1127,12 @@ queue_destroy(_queues *queues, int64_t qid)
 static int
 queue_put(_queues *queues, int64_t qid, PyObject *obj, int fmt, int unboundop)
 {
+    PyInterpreterState *interp = PyInterpreterState_Get();
+    _PyXIData_lookup_context_t ctx;
+    if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
+        return -1;
+    }
+
     // Look up the queue.
     _queue *queue = NULL;
     int err = _queues_lookup(queues, qid, &queue);
@@ -1141,13 +1147,12 @@ queue_put(_queues *queues, int64_t qid, PyObject *obj, int fmt, int unboundop)
         _queue_unmark_waiter(queue, queues->mutex);
         return -1;
     }
-    if (_PyObject_GetXIData(obj, data) != 0) {
+    if (_PyObject_GetXIData(&ctx, obj, data) != 0) {
         _queue_unmark_waiter(queue, queues->mutex);
         GLOBAL_FREE(data);
         return -1;
     }
-    assert(_PyXIData_INTERPID(data) == \
-           PyInterpreterState_GetID(PyInterpreterState_Get()));
+    assert(_PyXIData_INTERPID(data) == PyInterpreterState_GetID(interp));
 
     // Add the data to the queue.
     int64_t interpid = -1;  // _queueitem_init() will set it.
index b0e31a33734dabc3878fd0c65125452e630083d7..a6c639feea5d14af34966c39631fdebba181773e 100644 (file)
@@ -8,15 +8,24 @@
 static int
 ensure_xid_class(PyTypeObject *cls, xidatafunc getdata)
 {
-    //assert(cls->tp_flags & Py_TPFLAGS_HEAPTYPE);
-    return _PyXIData_RegisterClass(cls, getdata);
+    PyInterpreterState *interp = PyInterpreterState_Get();
+    _PyXIData_lookup_context_t ctx;
+    if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
+        return -1;
+    }
+    return _PyXIData_RegisterClass(&ctx, cls, getdata);
 }
 
 #ifdef REGISTERS_HEAP_TYPES
 static int
 clear_xid_class(PyTypeObject *cls)
 {
-    return _PyXIData_UnregisterClass(cls);
+    PyInterpreterState *interp = PyInterpreterState_Get();
+    _PyXIData_lookup_context_t ctx;
+    if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
+        return -1;
+    }
+    return _PyXIData_UnregisterClass(&ctx, cls);
 }
 #endif
 
index eb4ac9847dcd2bae52bfcbf7897a12f62a1ac4cc..a36823c4bb982b4a04253d7a64b69dab47e194d1 100644 (file)
@@ -1186,7 +1186,13 @@ object_is_shareable(PyObject *self, PyObject *args, PyObject *kwds)
         return NULL;
     }
 
-    if (_PyObject_CheckXIData(obj) == 0) {
+    PyInterpreterState *interp = PyInterpreterState_Get();
+    _PyXIData_lookup_context_t ctx;
+    if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
+        return NULL;
+    }
+
+    if (_PyObject_CheckXIData(&ctx, obj) == 0) {
         Py_RETURN_TRUE;
     }
     PyErr_Clear();
@@ -1485,6 +1491,11 @@ module_exec(PyObject *mod)
     PyInterpreterState *interp = PyInterpreterState_Get();
     module_state *state = get_module_state(mod);
 
+    _PyXIData_lookup_context_t ctx;
+    if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
+        return -1;
+    }
+
 #define ADD_WHENCE(NAME) \
     if (PyModule_AddIntConstant(mod, "WHENCE_" #NAME,                   \
                                 _PyInterpreterState_WHENCE_##NAME) < 0) \
@@ -1506,9 +1517,7 @@ module_exec(PyObject *mod)
     if (PyModule_AddType(mod, (PyTypeObject *)PyExc_InterpreterNotFoundError) < 0) {
         goto error;
     }
-    PyObject *PyExc_NotShareableError = \
-                _PyInterpreterState_GetXIState(interp)->exceptions.PyExc_NotShareableError;
-    if (PyModule_AddType(mod, (PyTypeObject *)PyExc_NotShareableError) < 0) {
+    if (PyModule_AddType(mod, (PyTypeObject *)ctx.PyExc_NotShareableError) < 0) {
         goto error;
     }
 
index 327a077671047c68542e76f20d381b119552934d..2c1ebcbbfdf419782982987ada9038cc549a956f 100644 (file)
@@ -1797,6 +1797,12 @@ _xid_capsule_destructor(PyObject *capsule)
 static PyObject *
 get_crossinterp_data(PyObject *self, PyObject *args)
 {
+    PyInterpreterState *interp = PyInterpreterState_Get();
+    _PyXIData_lookup_context_t ctx;
+    if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
+        return NULL;
+    }
+
     PyObject *obj = NULL;
     if (!PyArg_ParseTuple(args, "O:get_crossinterp_data", &obj)) {
         return NULL;
@@ -1806,7 +1812,7 @@ get_crossinterp_data(PyObject *self, PyObject *args)
     if (data == NULL) {
         return NULL;
     }
-    if (_PyObject_GetXIData(obj, data) != 0) {
+    if (_PyObject_GetXIData(&ctx, obj, data) != 0) {
         _PyXIData_Free(data);
         return NULL;
     }
index dfdb5f9d87a7c7f3efe0752e7872bb9ff967fa21..fe7d75f6b72f68af6f754e1fb399402880074dd7 100644 (file)
@@ -9,10 +9,6 @@
 #include "pycore_pyerrors.h"      // _PyErr_Clear()
 
 
-#define _PyXI_GET_GLOBAL_STATE(interp) (&(interp)->runtime->xi)
-#define _PyXI_GET_STATE(interp) (&(interp)->xi)
-
-
 /**************/
 /* exceptions */
 /**************/
@@ -68,7 +64,7 @@ _Py_CallInInterpreterAndRawFree(PyInterpreterState *interp,
 
 static void xid_lookup_init(_PyXIData_lookup_t *);
 static void xid_lookup_fini(_PyXIData_lookup_t *);
-static xidatafunc lookup_getdata(PyInterpreterState *, PyObject *);
+static xidatafunc lookup_getdata(_PyXIData_lookup_context_t *, PyObject *);
 #include "crossinterp_data_lookup.h"
 
 
@@ -202,9 +198,9 @@ _check_xidata(PyThreadState *tstate, _PyXIData_t *data)
 }
 
 static inline void
-_set_xid_lookup_failure(_PyXI_state_t *state, PyObject *obj, const char *msg)
+_set_xid_lookup_failure(dlcontext_t *ctx, PyObject *obj, const char *msg)
 {
-    PyObject *exctype = state->exceptions.PyExc_NotShareableError;
+    PyObject *exctype = ctx->PyExc_NotShareableError;
     assert(exctype != NULL);
     if (msg != NULL) {
         assert(obj == NULL);
@@ -221,14 +217,12 @@ _set_xid_lookup_failure(_PyXI_state_t *state, PyObject *obj, const char *msg)
 }
 
 int
-_PyObject_CheckXIData(PyObject *obj)
+_PyObject_CheckXIData(_PyXIData_lookup_context_t *ctx, PyObject *obj)
 {
-    PyInterpreterState *interp = PyInterpreterState_Get();
-    _PyXI_state_t *state = _PyXI_GET_STATE(interp);
-    xidatafunc getdata = lookup_getdata(interp, obj);
+    xidatafunc getdata = lookup_getdata(ctx, obj);
     if (getdata == NULL) {
         if (!PyErr_Occurred()) {
-            _set_xid_lookup_failure(state, obj, NULL);
+            _set_xid_lookup_failure(ctx, obj, NULL);
         }
         return -1;
     }
@@ -236,11 +230,11 @@ _PyObject_CheckXIData(PyObject *obj)
 }
 
 int
-_PyObject_GetXIData(PyObject *obj, _PyXIData_t *data)
+_PyObject_GetXIData(_PyXIData_lookup_context_t *ctx,
+                    PyObject *obj, _PyXIData_t *data)
 {
     PyThreadState *tstate = PyThreadState_Get();
     PyInterpreterState *interp = tstate->interp;
-    _PyXI_state_t *state = _PyXI_GET_STATE(interp);
 
     // Reset data before re-populating.
     *data = (_PyXIData_t){0};
@@ -248,11 +242,11 @@ _PyObject_GetXIData(PyObject *obj, _PyXIData_t *data)
 
     // Call the "getdata" func for the object.
     Py_INCREF(obj);
-    xidatafunc getdata = lookup_getdata(interp, obj);
+    xidatafunc getdata = lookup_getdata(ctx, obj);
     if (getdata == NULL) {
         Py_DECREF(obj);
         if (!PyErr_Occurred()) {
-            _set_xid_lookup_failure(state, obj, NULL);
+            _set_xid_lookup_failure(ctx, obj, NULL);
         }
         return -1;
     }
@@ -969,7 +963,8 @@ _PyXI_ClearExcInfo(_PyXI_excinfo *info)
 static int
 _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp)
 {
-    _PyXI_state_t *state;
+    dlcontext_t ctx;
+
     assert(!PyErr_Occurred());
     switch (code) {
     case _PyXI_ERR_NO_ERROR: _Py_FALLTHROUGH;
@@ -1000,8 +995,10 @@ _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp)
                         "failed to apply namespace to __main__");
         break;
     case _PyXI_ERR_NOT_SHAREABLE:
-        state = _PyXI_GET_STATE(interp);
-        _set_xid_lookup_failure(state, NULL, NULL);
+        if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
+            return -1;
+        }
+        _set_xid_lookup_failure(&ctx, NULL, NULL);
         break;
     default:
 #ifdef Py_DEBUG
@@ -1063,8 +1060,11 @@ _PyXI_ApplyError(_PyXI_error *error)
     }
     else if (error->code == _PyXI_ERR_NOT_SHAREABLE) {
         // Propagate the exception directly.
-        _PyXI_state_t *state = _PyXI_GET_STATE(error->interp);
-        _set_xid_lookup_failure(state, NULL, error->uncaught.msg);
+        dlcontext_t ctx;
+        if (_PyXIData_GetLookupContext(error->interp, &ctx) < 0) {
+            return NULL;
+        }
+        _set_xid_lookup_failure(&ctx, NULL, error->uncaught.msg);
     }
     else {
         // Raise an exception corresponding to the code.
@@ -1151,7 +1151,12 @@ _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value)
         PyErr_NoMemory();
         return -1;
     }
-    if (_PyObject_GetXIData(value, item->data) != 0) {
+    PyInterpreterState *interp = PyInterpreterState_Get();
+    dlcontext_t ctx;
+    if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
+        return -1;
+    }
+    if (_PyObject_GetXIData(&ctx, value, item->data) != 0) {
         PyMem_RawFree(item->data);
         item->data = NULL;
         // The caller may want to propagate PyExc_NotShareableError
@@ -1609,9 +1614,13 @@ _propagate_not_shareable_error(_PyXI_session *session)
         return;
     }
     PyInterpreterState *interp = PyInterpreterState_Get();
-    _PyXI_state_t *state = _PyXI_GET_STATE(interp);
-    assert(state->exceptions.PyExc_NotShareableError != NULL);
-    if (PyErr_ExceptionMatches(state->exceptions.PyExc_NotShareableError)) {
+    dlcontext_t ctx;
+    if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
+        PyErr_FormatUnraisable(
+                "Exception ignored while propagating not shareable error");
+        return;
+    }
+    if (PyErr_ExceptionMatches(ctx.PyExc_NotShareableError)) {
         // We want to propagate the exception directly.
         session->_error_override = _PyXI_ERR_NOT_SHAREABLE;
         session->error_override = &session->_error_override;
@@ -1779,22 +1788,87 @@ _PyXI_Exit(_PyXI_session *session)
 /* runtime lifecycle */
 /*********************/
 
+int
+_Py_xi_global_state_init(_PyXI_global_state_t *state)
+{
+    assert(state != NULL);
+    xid_lookup_init(&state->data_lookup);
+    return 0;
+}
+
+void
+_Py_xi_global_state_fini(_PyXI_global_state_t *state)
+{
+    assert(state != NULL);
+    xid_lookup_fini(&state->data_lookup);
+}
+
+int
+_Py_xi_state_init(_PyXI_state_t *state, PyInterpreterState *interp)
+{
+    assert(state != NULL);
+    assert(interp == NULL || state == _PyXI_GET_STATE(interp));
+
+    xid_lookup_init(&state->data_lookup);
+
+    // Initialize exceptions.
+    if (interp != NULL) {
+        if (init_static_exctypes(&state->exceptions, interp) < 0) {
+            fini_heap_exctypes(&state->exceptions);
+            return -1;
+        }
+    }
+    if (init_heap_exctypes(&state->exceptions) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+void
+_Py_xi_state_fini(_PyXI_state_t *state, PyInterpreterState *interp)
+{
+    assert(state != NULL);
+    assert(interp == NULL || state == _PyXI_GET_STATE(interp));
+
+    fini_heap_exctypes(&state->exceptions);
+    if (interp != NULL) {
+        fini_static_exctypes(&state->exceptions, interp);
+    }
+
+    xid_lookup_fini(&state->data_lookup);
+}
+
+
 PyStatus
 _PyXI_Init(PyInterpreterState *interp)
 {
-    _PyXI_state_t *state = _PyXI_GET_STATE(interp);
-
-    // Initialize the XID lookup state (e.g. registry).
     if (_Py_IsMainInterpreter(interp)) {
-        xid_lookup_init(&_PyXI_GET_GLOBAL_STATE(interp)->data_lookup);
+        _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");
+        }
+        if (_Py_xi_global_state_init(global_state) < 0) {
+            PyErr_PrintEx(0);
+            return _PyStatus_ERR(
+                    "failed to initialize  global cross-interpreter state");
+        }
     }
-    xid_lookup_init(&state->data_lookup);
 
-    // Initialize exceptions.(heap types).
-    // See _PyXI_InitTypes() for the static types.
-    if (init_heap_exctypes(&_PyXI_GET_STATE(interp)->exceptions) < 0) {
+    _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");
+    }
+    // The static types were already initialized in _PyXI_InitTypes(),
+    // so we pass in NULL here to avoid initializing them again.
+    if (_Py_xi_state_init(state, NULL) < 0) {
         PyErr_PrintEx(0);
-        return _PyStatus_ERR("failed to initialize exceptions");
+        return _PyStatus_ERR(
+                "failed to initialize interpreter's cross-interpreter state");
     }
 
     return _PyStatus_OK();
@@ -1807,15 +1881,19 @@ void
 _PyXI_Fini(PyInterpreterState *interp)
 {
     _PyXI_state_t *state = _PyXI_GET_STATE(interp);
+#ifndef NDEBUG
+    if (state == NULL) {
+        PyErr_PrintEx(0);
+        return;
+    }
+#endif
+    // The static types will be finalized soon in _PyXI_FiniTypes(),
+    // so we pass in NULL here to avoid finalizing them right now.
+    _Py_xi_state_fini(state, NULL);
 
-    // Finalize exceptions (heap types).
-    // See _PyXI_FiniTypes() for the static types.
-    fini_heap_exctypes(&_PyXI_GET_STATE(interp)->exceptions);
-
-    // Finalize the XID lookup state (e.g. registry).
-    xid_lookup_fini(&state->data_lookup);
     if (_Py_IsMainInterpreter(interp)) {
-        xid_lookup_fini(&_PyXI_GET_GLOBAL_STATE(interp)->data_lookup);
+        _PyXI_global_state_t *global_state = _PyXI_GET_GLOBAL_STATE(interp);
+        _Py_xi_global_state_fini(global_state);
     }
 }
 
@@ -1824,7 +1902,8 @@ _PyXI_InitTypes(PyInterpreterState *interp)
 {
     if (init_static_exctypes(&_PyXI_GET_STATE(interp)->exceptions, interp) < 0) {
         PyErr_PrintEx(0);
-        return _PyStatus_ERR("failed to initialize an exception type");
+        return _PyStatus_ERR(
+                "failed to initialize the cross-interpreter exception types");
     }
     // We would initialize heap types here too but that leads to ref leaks.
     // Instead, we intialize them in _PyXI_Init().
index 9048f90cff160a9eb5c189925158b8732cf7c1e8..48e5d9762cd6970c8a1588364f86698b192499bb 100644 (file)
@@ -1,6 +1,7 @@
 #include "pycore_weakref.h"       // _PyWeakref_GET_REF()
 
 
+typedef _PyXIData_lookup_context_t dlcontext_t;
 typedef _PyXIData_registry_t dlregistry_t;
 typedef _PyXIData_regitem_t dlregitem_t;
 
@@ -8,7 +9,7 @@ typedef _PyXIData_regitem_t dlregitem_t;
 // forward
 static void _xidregistry_init(dlregistry_t *);
 static void _xidregistry_fini(dlregistry_t *);
-static xidatafunc _lookup_getdata_from_registry(PyInterpreterState *, PyObject *);
+static xidatafunc _lookup_getdata_from_registry(dlcontext_t *, PyObject *);
 
 
 /* used in crossinterp.c */
@@ -26,22 +27,43 @@ xid_lookup_fini(_PyXIData_lookup_t *state)
 }
 
 static xidatafunc
-lookup_getdata(PyInterpreterState *interp, PyObject *obj)
+lookup_getdata(dlcontext_t *ctx, PyObject *obj)
 {
    /* 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. */
-    return _lookup_getdata_from_registry(interp, obj);
+    return _lookup_getdata_from_registry(ctx, obj);
 }
 
 
 /* exported API */
 
+int
+_PyXIData_GetLookupContext(PyInterpreterState *interp,
+                           _PyXIData_lookup_context_t *res)
+{
+    _PyXI_global_state_t *global = _PyXI_GET_GLOBAL_STATE(interp);
+    if (global == NULL) {
+        assert(PyErr_Occurred());
+        return -1;
+    }
+    _PyXI_state_t *local = _PyXI_GET_STATE(interp);
+    if (local == NULL) {
+        assert(PyErr_Occurred());
+        return -1;
+    }
+    *res = (dlcontext_t){
+        .global = &global->data_lookup,
+        .local = &local->data_lookup,
+        .PyExc_NotShareableError = local->exceptions.PyExc_NotShareableError,
+    };
+    return 0;
+}
+
 xidatafunc
-_PyXIData_Lookup(PyObject *obj)
+_PyXIData_Lookup(_PyXIData_lookup_context_t *ctx, PyObject *obj)
 {
-    PyInterpreterState *interp = PyInterpreterState_Get();
-    return lookup_getdata(interp, obj);
+    return lookup_getdata(ctx, obj);
 }
 
 
@@ -110,25 +132,12 @@ _xidregistry_unlock(dlregistry_t *registry)
 /* accessing the registry */
 
 static inline dlregistry_t *
-_get_global_xidregistry(_PyRuntimeState *runtime)
+_get_xidregistry_for_type(dlcontext_t *ctx, PyTypeObject *cls)
 {
-    return &runtime->xi.data_lookup.registry;
-}
-
-static inline dlregistry_t *
-_get_xidregistry(PyInterpreterState *interp)
-{
-    return &interp->xi.data_lookup.registry;
-}
-
-static inline dlregistry_t *
-_get_xidregistry_for_type(PyInterpreterState *interp, PyTypeObject *cls)
-{
-    dlregistry_t *registry = _get_global_xidregistry(interp->runtime);
     if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) {
-        registry = _get_xidregistry(interp);
+        return &ctx->local->registry;
     }
-    return registry;
+    return &ctx->global->registry;
 }
 
 static dlregitem_t* _xidregistry_remove_entry(dlregistry_t *, dlregitem_t *);
@@ -160,11 +169,11 @@ _xidregistry_find_type(dlregistry_t *xidregistry, PyTypeObject *cls)
 }
 
 static xidatafunc
-_lookup_getdata_from_registry(PyInterpreterState *interp, PyObject *obj)
+_lookup_getdata_from_registry(dlcontext_t *ctx, PyObject *obj)
 {
     PyTypeObject *cls = Py_TYPE(obj);
 
-    dlregistry_t *xidregistry = _get_xidregistry_for_type(interp, cls);
+    dlregistry_t *xidregistry = _get_xidregistry_for_type(ctx, cls);
     _xidregistry_lock(xidregistry);
 
     dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls);
@@ -241,7 +250,8 @@ _xidregistry_clear(dlregistry_t *xidregistry)
 }
 
 int
-_PyXIData_RegisterClass(PyTypeObject *cls, xidatafunc getdata)
+_PyXIData_RegisterClass(_PyXIData_lookup_context_t *ctx,
+                        PyTypeObject *cls, xidatafunc getdata)
 {
     if (!PyType_Check(cls)) {
         PyErr_Format(PyExc_ValueError, "only classes may be registered");
@@ -253,8 +263,7 @@ _PyXIData_RegisterClass(PyTypeObject *cls, xidatafunc getdata)
     }
 
     int res = 0;
-    PyInterpreterState *interp = _PyInterpreterState_GET();
-    dlregistry_t *xidregistry = _get_xidregistry_for_type(interp, cls);
+    dlregistry_t *xidregistry = _get_xidregistry_for_type(ctx, cls);
     _xidregistry_lock(xidregistry);
 
     dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls);
@@ -272,11 +281,10 @@ finally:
 }
 
 int
-_PyXIData_UnregisterClass(PyTypeObject *cls)
+_PyXIData_UnregisterClass(_PyXIData_lookup_context_t *ctx, PyTypeObject *cls)
 {
     int res = 0;
-    PyInterpreterState *interp = _PyInterpreterState_GET();
-    dlregistry_t *xidregistry = _get_xidregistry_for_type(interp, cls);
+    dlregistry_t *xidregistry = _get_xidregistry_for_type(ctx, cls);
     _xidregistry_lock(xidregistry);
 
     dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls);
@@ -500,6 +508,11 @@ _tuple_shared_free(void* data)
 static int
 _tuple_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
 {
+    dlcontext_t ctx;
+    if (_PyXIData_GetLookupContext(tstate->interp, &ctx) < 0) {
+        return -1;
+    }
+
     Py_ssize_t len = PyTuple_GET_SIZE(obj);
     if (len < 0) {
         return -1;
@@ -526,7 +539,7 @@ _tuple_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
 
         int res = -1;
         if (!_Py_EnterRecursiveCallTstate(tstate, " while sharing a tuple")) {
-            res = _PyObject_GetXIData(item, data);
+            res = _PyObject_GetXIData(&ctx, item, data);
             _Py_LeaveRecursiveCallTstate(tstate);
         }
         if (res < 0) {