From 45ace429a2e85a081a455c10fea83ce059fc4cbc Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 5 Nov 2024 13:38:28 -0700 Subject: [PATCH 1/6] Move _PyXI_GET_STATE() to the API. --- Include/internal/pycore_crossinterp.h | 5 +++-- Modules/_interpretersmodule.c | 2 +- Python/crossinterp.c | 4 ---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index 66719796aeee22..faee13694d4381 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -173,13 +173,14 @@ typedef struct { } exceptions; } _PyXI_state_t; +#define _PyXI_GET_GLOBAL_STATE(interp) (&(interp)->runtime->xi) +#define _PyXI_GET_STATE(interp) (&(interp)->xi) + extern PyStatus _PyXI_Init(PyInterpreterState *interp); extern void _PyXI_Fini(PyInterpreterState *interp); extern PyStatus _PyXI_InitTypes(PyInterpreterState *interp); extern void _PyXI_FiniTypes(PyInterpreterState *interp); -#define _PyInterpreterState_GetXIState(interp) (&(interp)->xi) - /***************************/ /* short-term data sharing */ diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index eb4ac9847dcd2b..41999d2a003fe3 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -1507,7 +1507,7 @@ module_exec(PyObject *mod) goto error; } PyObject *PyExc_NotShareableError = \ - _PyInterpreterState_GetXIState(interp)->exceptions.PyExc_NotShareableError; + _PyXI_GET_STATE(interp)->exceptions.PyExc_NotShareableError; if (PyModule_AddType(mod, (PyTypeObject *)PyExc_NotShareableError) < 0) { goto error; } diff --git a/Python/crossinterp.c b/Python/crossinterp.c index dfdb5f9d87a7c7..30cdbed83ef326 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -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 */ /**************/ From 89230fbdea6835dd3bf190b8a54cb78f8d67b3c0 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Nov 2024 09:55:42 -0700 Subject: [PATCH 2/6] Check the result of _PyXI_GET_STATE(). --- Python/crossinterp.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 30cdbed83ef326..a9807675268c44 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -1779,10 +1779,21 @@ PyStatus _PyXI_Init(PyInterpreterState *interp) { _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"); + } // 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"); + } + xid_lookup_init(&global_state->data_lookup); } xid_lookup_init(&state->data_lookup); @@ -1803,6 +1814,13 @@ void _PyXI_Fini(PyInterpreterState *interp) { _PyXI_state_t *state = _PyXI_GET_STATE(interp); + assert(state != NULL); +#ifndef NDEBUG + if (state == NULL) { + PyErr_PrintEx(0); + return; + } +#endif // Finalize exceptions (heap types). // See _PyXI_FiniTypes() for the static types. @@ -1811,7 +1829,9 @@ _PyXI_Fini(PyInterpreterState *interp) // 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); + assert(global_state != NULL); + xid_lookup_fini(&global_state->data_lookup); } } From ecb2d459081c91668655893b41936df0f59e944b Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 5 Nov 2024 11:33:44 -0700 Subject: [PATCH 3/6] Add _PyXIData_lookup_context_t. --- Include/internal/pycore_crossinterp.h | 23 +++++- .../pycore_crossinterp_data_registry.h | 9 ++- Modules/_interpchannelsmodule.c | 7 +- Modules/_interpqueuesmodule.c | 11 ++- Modules/_interpreters_common.h | 15 +++- Modules/_interpretersmodule.c | 17 ++++- Modules/_testinternalcapi.c | 8 +- Python/crossinterp.c | 55 ++++++++------ Python/crossinterp_data_lookup.h | 75 +++++++++++-------- 9 files changed, 151 insertions(+), 69 deletions(-) diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index faee13694d4381..f15066b6540d8b 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -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 */ diff --git a/Include/internal/pycore_crossinterp_data_registry.h b/Include/internal/pycore_crossinterp_data_registry.h index 04f25bc05fd1b8..bbad4de770857f 100644 --- a/Include/internal/pycore_crossinterp_data_registry.h +++ b/Include/internal/pycore_crossinterp_data_registry.h @@ -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. diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c index cd3c5026938568..75d69ade1d3c9b 100644 --- a/Modules/_interpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -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; diff --git a/Modules/_interpqueuesmodule.c b/Modules/_interpqueuesmodule.c index 8d0e223db7ff19..808938a9e8cd16 100644 --- a/Modules/_interpqueuesmodule.c +++ b/Modules/_interpqueuesmodule.c @@ -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. diff --git a/Modules/_interpreters_common.h b/Modules/_interpreters_common.h index b0e31a33734dab..a6c639feea5d14 100644 --- a/Modules/_interpreters_common.h +++ b/Modules/_interpreters_common.h @@ -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 diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index 41999d2a003fe3..a36823c4bb982b 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -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 = \ - _PyXI_GET_STATE(interp)->exceptions.PyExc_NotShareableError; - if (PyModule_AddType(mod, (PyTypeObject *)PyExc_NotShareableError) < 0) { + if (PyModule_AddType(mod, (PyTypeObject *)ctx.PyExc_NotShareableError) < 0) { goto error; } diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 327a077671047c..2c1ebcbbfdf419 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -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; } diff --git a/Python/crossinterp.c b/Python/crossinterp.c index a9807675268c44..79b8dafff562ab 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -64,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" @@ -198,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); @@ -217,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; } @@ -232,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}; @@ -244,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; } @@ -965,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; @@ -996,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 @@ -1059,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. @@ -1147,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 @@ -1605,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; diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h index 9048f90cff160a..e86b3f85a63fb3 100644 --- a/Python/crossinterp_data_lookup.h +++ b/Python/crossinterp_data_lookup.h @@ -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) -{ - 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) +_get_xidregistry_for_type(dlcontext_t *ctx, 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 @@ _PyXIData_RegisterClass(PyTypeObject *cls, xidatafunc getdata) } 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) { From ad7cd0d9744cc2254e0575c210533d4e30a8ae3b Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 5 Nov 2024 11:50:29 -0700 Subject: [PATCH 4/6] Add state init/fini funcs. --- Include/internal/pycore_crossinterp.h | 7 +++++ Python/crossinterp.c | 43 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index f15066b6540d8b..20add0a5b9a450 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -193,10 +193,17 @@ typedef struct { #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 + +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 *); +void _Py_xi_state_fini(_PyXI_state_t *); /***************************/ diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 79b8dafff562ab..5e80f548800e8c 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -1868,6 +1868,49 @@ _PyXI_FiniTypes(PyInterpreterState *interp) fini_static_exctypes(&_PyXI_GET_STATE(interp)->exceptions, interp); } +int +_Py_xi_global_state_init(_PyXI_global_state_t *state) +{ + xid_lookup_init(&state->data_lookup); + return 0; +} + +void +_Py_xi_global_state_fini(_PyXI_global_state_t *state) +{ + xid_lookup_fini(&state->data_lookup); +} + +int +_Py_xi_state_init(_PyXI_state_t *state) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + + xid_lookup_init(&state->data_lookup); + + // Initialize exceptions. + if (init_exceptions(interp) < 0) { + return -1; + } + if (_init_not_shareable_error_type(state) < 0) { + return -1; + } + return 0; +} + +void +_Py_xi_state_fini(_PyXI_state_t *state) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + + // Finalize exceptions. + _fini_not_shareable_error_type(state); + fini_exceptions(interp); + + // Finalize the XID lookup state (e.g. registry). + xid_lookup_fini(&state->data_lookup); +} + /*************/ /* other API */ From cf4801668cfeae7311e9ee4c28d2152fee7f01b2 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Nov 2024 11:21:47 -0700 Subject: [PATCH 5/6] Use the init funcs in _PyXI_Init(). --- Include/internal/pycore_crossinterp.h | 4 +- Python/crossinterp.c | 141 ++++++++++++++------------ 2 files changed, 76 insertions(+), 69 deletions(-) diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index 20add0a5b9a450..69a60d73e05c26 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -202,8 +202,8 @@ extern void _PyXI_FiniTypes(PyInterpreterState *interp); 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 *); -void _Py_xi_state_fini(_PyXI_state_t *); +int _Py_xi_state_init(_PyXI_state_t *, PyInterpreterState *); +void _Py_xi_state_fini(_PyXI_state_t *, PyInterpreterState *); /***************************/ diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 5e80f548800e8c..fe7d75f6b72f68 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -1788,17 +1788,61 @@ _PyXI_Exit(_PyXI_session *session) /* runtime lifecycle */ /*********************/ -PyStatus -_PyXI_Init(PyInterpreterState *interp) +int +_Py_xi_global_state_init(_PyXI_global_state_t *state) { - _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"); + 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); } - // Initialize the XID lookup state (e.g. registry). + xid_lookup_fini(&state->data_lookup); +} + + +PyStatus +_PyXI_Init(PyInterpreterState *interp) +{ if (_Py_IsMainInterpreter(interp)) { _PyXI_global_state_t *global_state = _PyXI_GET_GLOBAL_STATE(interp); if (global_state == NULL) { @@ -1806,15 +1850,25 @@ _PyXI_Init(PyInterpreterState *interp) return _PyStatus_ERR( "failed to get global cross-interpreter state"); } - xid_lookup_init(&global_state->data_lookup); + 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 initialize exceptions"); + 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 interpreter's cross-interpreter state"); } return _PyStatus_OK(); @@ -1827,24 +1881,19 @@ void _PyXI_Fini(PyInterpreterState *interp) { _PyXI_state_t *state = _PyXI_GET_STATE(interp); - assert(state != NULL); #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)) { _PyXI_global_state_t *global_state = _PyXI_GET_GLOBAL_STATE(interp); - assert(global_state != NULL); - xid_lookup_fini(&global_state->data_lookup); + _Py_xi_global_state_fini(global_state); } } @@ -1853,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(). @@ -1868,49 +1918,6 @@ _PyXI_FiniTypes(PyInterpreterState *interp) fini_static_exctypes(&_PyXI_GET_STATE(interp)->exceptions, interp); } -int -_Py_xi_global_state_init(_PyXI_global_state_t *state) -{ - xid_lookup_init(&state->data_lookup); - return 0; -} - -void -_Py_xi_global_state_fini(_PyXI_global_state_t *state) -{ - xid_lookup_fini(&state->data_lookup); -} - -int -_Py_xi_state_init(_PyXI_state_t *state) -{ - PyInterpreterState *interp = _PyInterpreterState_GET(); - - xid_lookup_init(&state->data_lookup); - - // Initialize exceptions. - if (init_exceptions(interp) < 0) { - return -1; - } - if (_init_not_shareable_error_type(state) < 0) { - return -1; - } - return 0; -} - -void -_Py_xi_state_fini(_PyXI_state_t *state) -{ - PyInterpreterState *interp = _PyInterpreterState_GET(); - - // Finalize exceptions. - _fini_not_shareable_error_type(state); - fini_exceptions(interp); - - // Finalize the XID lookup state (e.g. registry). - xid_lookup_fini(&state->data_lookup); -} - /*************/ /* other API */ From 9b13684344c35c8a054be8804b34c9594a5ea899 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 12 Nov 2024 09:44:43 -0700 Subject: [PATCH 6/6] lint --- Python/crossinterp_data_lookup.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h index e86b3f85a63fb3..48e5d9762cd697 100644 --- a/Python/crossinterp_data_lookup.h +++ b/Python/crossinterp_data_lookup.h @@ -59,7 +59,7 @@ _PyXIData_GetLookupContext(PyInterpreterState *interp, }; return 0; } - + xidatafunc _PyXIData_Lookup(_PyXIData_lookup_context_t *ctx, PyObject *obj) {