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()
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 */
} 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 *);
/***************************/
_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.
}
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;
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;
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);
_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.
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
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();
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) \
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;
}
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;
if (data == NULL) {
return NULL;
}
- if (_PyObject_GetXIData(obj, data) != 0) {
+ if (_PyObject_GetXIData(&ctx, obj, data) != 0) {
_PyXIData_Free(data);
return NULL;
}
#include "pycore_pyerrors.h" // _PyErr_Clear()
-#define _PyXI_GET_GLOBAL_STATE(interp) (&(interp)->runtime->xi)
-#define _PyXI_GET_STATE(interp) (&(interp)->xi)
-
-
/**************/
/* exceptions */
/**************/
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"
}
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);
}
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;
}
}
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};
// 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;
}
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;
"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
}
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.
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
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;
/* 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();
_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);
}
}
{
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().
#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;
// 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 */
}
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);
}
/* 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 *);
}
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);
}
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");
}
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);
}
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);
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;
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) {