From 4c09639eb2fb87e3a6b31cf05a24355ea1527ffd Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 5 Nov 2024 08:25:06 -0700 Subject: [PATCH 1/8] Fix parking lot. --- Include/internal/pycore_pystate.h | 6 ++++++ Python/parking_lot.c | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index fade55945b7dbf..edcd75a55b686b 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -141,6 +141,12 @@ _PyThreadState_GET(void) #endif } +static inline int +_PyThreadState_IsAttached(PyThreadState *tstate) +{ + return (_Py_atomic_load_int_relaxed(&tstate->state) == _Py_THREAD_ATTACHED); +} + // Attaches the current thread to the interpreter. // // This may block while acquiring the GIL (if the GIL is enabled) or while diff --git a/Python/parking_lot.c b/Python/parking_lot.c index bffc959e5d0978..8edf43235942ab 100644 --- a/Python/parking_lot.c +++ b/Python/parking_lot.c @@ -221,8 +221,7 @@ _PySemaphore_Wait(_PySemaphore *sema, PyTime_t timeout, int detach) PyThreadState *tstate = NULL; if (detach) { tstate = _PyThreadState_GET(); - if (tstate && _Py_atomic_load_int_relaxed(&tstate->state) == - _Py_THREAD_ATTACHED) { + if (tstate && _PyThreadState_IsAttached(tstate)) { // Only detach if we are attached PyEval_ReleaseThread(tstate); } From 6972d0b6af9dbd4ee5d56fdf51d88ce14995fb07 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 5 Nov 2024 13:47:56 -0700 Subject: [PATCH 2/8] xid_newobjectfunc -> xid_newobjfunc --- Include/internal/pycore_crossinterp.h | 8 ++++---- Modules/_interpchannelsmodule.c | 2 +- Python/crossinterp.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index a7e71efc5daa49..349afefcacc50f 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -40,7 +40,7 @@ extern int _Py_CallInInterpreterAndRawFree( /**************************/ typedef struct _xid _PyXIData_t; -typedef PyObject *(*xid_newobjectfunc)(_PyXIData_t *); +typedef PyObject *(*xid_newobjfunc)(_PyXIData_t *); typedef void (*xid_freefunc)(void *); // _PyXIData_t is similar to Py_buffer as an effectively @@ -72,7 +72,7 @@ struct _xid { // interpreter given the data. The resulting object (a new // reference) will be equivalent to the original object. This field // is required. - xid_newobjectfunc new_object; + xid_newobjfunc new_object; // free is called when the data is released. If it is NULL then // nothing will be done to free the data. For some types this is // okay (e.g. bytes) and for those types this field should be set @@ -117,11 +117,11 @@ PyAPI_FUNC(int) _PyXIData_ReleaseAndRawFree(_PyXIData_t *); PyAPI_FUNC(void) _PyXIData_Init( _PyXIData_t *data, PyInterpreterState *interp, void *shared, PyObject *obj, - xid_newobjectfunc new_object); + xid_newobjfunc new_object); PyAPI_FUNC(int) _PyXIData_InitWithSize( _PyXIData_t *, PyInterpreterState *interp, const size_t, PyObject *, - xid_newobjectfunc); + xid_newobjfunc); PyAPI_FUNC(void) _PyXIData_Clear( PyInterpreterState *, _PyXIData_t *); // Normally the Init* functions are sufficient. The only time diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c index b8d7dfb87cce0e..cd3c5026938568 100644 --- a/Modules/_interpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -63,7 +63,7 @@ _globals (static struct globals): data (void *) obj (PyObject *) interpid (int64_t) - new_object (xid_newobjectfunc) + new_object (xid_newobjfunc) free (xid_freefunc) last (struct _channelitem *): ... diff --git a/Python/crossinterp.c b/Python/crossinterp.c index b7aa8da8ac550e..150c37c74bc2a6 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -126,7 +126,7 @@ void _PyXIData_Init(_PyXIData_t *data, PyInterpreterState *interp, void *shared, PyObject *obj, - xid_newobjectfunc new_object) + xid_newobjfunc new_object) { assert(data != NULL); assert(new_object != NULL); @@ -150,7 +150,7 @@ int _PyXIData_InitWithSize(_PyXIData_t *data, PyInterpreterState *interp, const size_t size, PyObject *obj, - xid_newobjectfunc new_object) + xid_newobjfunc new_object) { assert(size > 0); // For now we always free the shared data in the same interpreter From e4c64adb7975ec7c3d4e8b710f49f59a80f57ecf Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 5 Nov 2024 08:30:40 -0700 Subject: [PATCH 3/8] _xid -> _xidata --- Include/internal/pycore_crossinterp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index 349afefcacc50f..7acf7b839bb612 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -39,14 +39,14 @@ extern int _Py_CallInInterpreterAndRawFree( /* cross-interpreter data */ /**************************/ -typedef struct _xid _PyXIData_t; +typedef struct _xidata _PyXIData_t; typedef PyObject *(*xid_newobjfunc)(_PyXIData_t *); typedef void (*xid_freefunc)(void *); // _PyXIData_t is similar to Py_buffer as an effectively // opaque struct that holds data outside the object machinery. This // is necessary to pass safely between interpreters in the same process. -struct _xid { +struct _xidata { // data is the cross-interpreter-safe derivation of a Python object // (see _PyObject_GetXIData). It will be NULL if the // new_object func (below) encodes the data. From 8ff613c2cb10861ca68d06eb5ecbc77bf33c7dba Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 5 Nov 2024 09:34:45 -0700 Subject: [PATCH 4/8] struct _xidregitem -> _PyXIData_regitem_t --- Include/internal/pycore_crossinterp_data_registry.h | 12 ++++++------ Python/crossinterp_data_lookup.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Include/internal/pycore_crossinterp_data_registry.h b/Include/internal/pycore_crossinterp_data_registry.h index 2990c6af62e952..92241d3f1c7ce7 100644 --- a/Include/internal/pycore_crossinterp_data_registry.h +++ b/Include/internal/pycore_crossinterp_data_registry.h @@ -7,24 +7,24 @@ // alternative would be to add a tp_* slot for a class's // xidatafunc. It would be simpler and more efficient. -struct _xidregitem; +struct _xid_regitem; -struct _xidregitem { - struct _xidregitem *prev; - struct _xidregitem *next; +typedef struct _xid_regitem { + struct _xid_regitem *prev; + struct _xid_regitem *next; /* This can be a dangling pointer, but only if weakref is set. */ PyTypeObject *cls; /* This is NULL for builtin types. */ PyObject *weakref; size_t refcount; xidatafunc getdata; -}; +} _PyXIData_regitem_t; struct _xidregistry { int global; /* builtin types or heap types */ int initialized; PyMutex mutex; - struct _xidregitem *head; + _PyXIData_regitem_t *head; }; PyAPI_FUNC(int) _PyXIData_RegisterClass(PyTypeObject *, xidatafunc); diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h index 88c662a3df00d6..aa3ceb8e518f2d 100644 --- a/Python/crossinterp_data_lookup.h +++ b/Python/crossinterp_data_lookup.h @@ -2,7 +2,7 @@ typedef struct _xidregistry dlregistry_t; -typedef struct _xidregitem dlregitem_t; +typedef _PyXIData_regitem_t dlregitem_t; // forward From bca218a941ed24467cbacdf58291571bfe0dd183 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 5 Nov 2024 09:40:53 -0700 Subject: [PATCH 5/8] struct _xidregistry -> _PyXIData_registry_t --- Include/internal/pycore_crossinterp_data_registry.h | 6 +++--- Include/internal/pycore_interp.h | 2 +- Include/internal/pycore_runtime.h | 2 +- Python/crossinterp_data_lookup.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Include/internal/pycore_crossinterp_data_registry.h b/Include/internal/pycore_crossinterp_data_registry.h index 92241d3f1c7ce7..04f25bc05fd1b8 100644 --- a/Include/internal/pycore_crossinterp_data_registry.h +++ b/Include/internal/pycore_crossinterp_data_registry.h @@ -20,17 +20,17 @@ typedef struct _xid_regitem { xidatafunc getdata; } _PyXIData_regitem_t; -struct _xidregistry { +typedef struct { int global; /* builtin types or heap types */ int initialized; PyMutex mutex; _PyXIData_regitem_t *head; -}; +} _PyXIData_registry_t; PyAPI_FUNC(int) _PyXIData_RegisterClass(PyTypeObject *, xidatafunc); PyAPI_FUNC(int) _PyXIData_UnregisterClass(PyTypeObject *); struct _xid_lookup_state { // XXX Remove this field once we have a tp_* slot. - struct _xidregistry registry; + _PyXIData_registry_t registry; }; diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 9e3b4299693bbc..beb6539ba8c6d8 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -16,7 +16,7 @@ extern "C" { #include "pycore_code.h" // struct callable_cache #include "pycore_codecs.h" // struct codecs_state #include "pycore_context.h" // struct _Py_context_state -#include "pycore_crossinterp.h" // struct _xidregistry +#include "pycore_crossinterp.h" // struct _xi_state #include "pycore_dict_state.h" // struct _Py_dict_state #include "pycore_dtoa.h" // struct _dtoa_state #include "pycore_exceptions.h" // struct _Py_exc_state diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 7f592aa6cf9f05..21c3a820b84e9d 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -11,7 +11,7 @@ extern "C" { #include "pycore_atexit.h" // struct _atexit_runtime_state #include "pycore_audit.h" // _Py_AuditHookEntry #include "pycore_ceval_state.h" // struct _ceval_runtime_state -#include "pycore_crossinterp.h" // struct _xidregistry +#include "pycore_crossinterp.h" // struct _xi_runtime_state #include "pycore_debug_offsets.h" // _Py_DebugOffsets #include "pycore_faulthandler.h" // struct _faulthandler_runtime_state #include "pycore_floatobject.h" // struct _Py_float_runtime_state diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h index aa3ceb8e518f2d..9048f90cff160a 100644 --- a/Python/crossinterp_data_lookup.h +++ b/Python/crossinterp_data_lookup.h @@ -1,7 +1,7 @@ #include "pycore_weakref.h" // _PyWeakref_GET_REF() -typedef struct _xidregistry dlregistry_t; +typedef _PyXIData_registry_t dlregistry_t; typedef _PyXIData_regitem_t dlregitem_t; From 023966738b5690741af4dbd1332ed194c8f8a7a6 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 5 Nov 2024 09:46:31 -0700 Subject: [PATCH 6/8] struct _xi_runtime_state -> _PyXI_global_state_t --- Include/internal/pycore_crossinterp.h | 4 ++-- Include/internal/pycore_runtime.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index 7acf7b839bb612..6f941c43f0edf6 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -155,10 +155,10 @@ PyAPI_FUNC(void) _PyXIData_Clear( PyInterpreterState *, _PyXIData_t *); /* runtime state & lifecycle */ /*****************************/ -struct _xi_runtime_state { +typedef struct { // builtin types _PyXIData_lookup_t data_lookup; -}; +} _PyXI_global_state_t; struct _xi_state { // heap types diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 21c3a820b84e9d..2f2cec22cf1589 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -11,7 +11,7 @@ extern "C" { #include "pycore_atexit.h" // struct _atexit_runtime_state #include "pycore_audit.h" // _Py_AuditHookEntry #include "pycore_ceval_state.h" // struct _ceval_runtime_state -#include "pycore_crossinterp.h" // struct _xi_runtime_state +#include "pycore_crossinterp.h" // _PyXI_global_state_t #include "pycore_debug_offsets.h" // _Py_DebugOffsets #include "pycore_faulthandler.h" // struct _faulthandler_runtime_state #include "pycore_floatobject.h" // struct _Py_float_runtime_state @@ -106,7 +106,7 @@ typedef struct pyruntimestate { tools. */ /* cross-interpreter data and utils */ - struct _xi_runtime_state xi; + _PyXI_global_state_t xi; struct _pymem_allocators allocators; struct _obmalloc_global_state obmalloc; From 8f29d80a94515f598ab4d6f9e90917ebf0995d23 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 5 Nov 2024 09:48:01 -0700 Subject: [PATCH 7/8] struct _xi_state -> _PyXI_state_t --- Include/internal/pycore_crossinterp.h | 4 ++-- Include/internal/pycore_interp.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index 6f941c43f0edf6..66719796aeee22 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -160,7 +160,7 @@ typedef struct { _PyXIData_lookup_t data_lookup; } _PyXI_global_state_t; -struct _xi_state { +typedef struct { // heap types _PyXIData_lookup_t data_lookup; @@ -171,7 +171,7 @@ struct _xi_state { // heap types PyObject *PyExc_NotShareableError; } exceptions; -}; +} _PyXI_state_t; extern PyStatus _PyXI_Init(PyInterpreterState *interp); extern void _PyXI_Fini(PyInterpreterState *interp); diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index beb6539ba8c6d8..824b865eda60df 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -16,7 +16,7 @@ extern "C" { #include "pycore_code.h" // struct callable_cache #include "pycore_codecs.h" // struct codecs_state #include "pycore_context.h" // struct _Py_context_state -#include "pycore_crossinterp.h" // struct _xi_state +#include "pycore_crossinterp.h" // _PyXI_state_t #include "pycore_dict_state.h" // struct _Py_dict_state #include "pycore_dtoa.h" // struct _dtoa_state #include "pycore_exceptions.h" // struct _Py_exc_state @@ -205,7 +205,7 @@ struct _is { freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS]; /* cross-interpreter data and utils */ - struct _xi_state xi; + _PyXI_state_t xi; #ifdef HAVE_FORK PyObject *before_forkers; From de75cb492bc7dc40fc6d8e9ad52e2f197409c22e Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 5 Nov 2024 09:55:33 -0700 Subject: [PATCH 8/8] Use struct _xi_state to look up PyExc_NotShareableError. --- Python/crossinterp.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 150c37c74bc2a6..dfdb5f9d87a7c7 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -202,11 +202,9 @@ _check_xidata(PyThreadState *tstate, _PyXIData_t *data) } static inline void -_set_xid_lookup_failure(PyInterpreterState *interp, - PyObject *obj, const char *msg) +_set_xid_lookup_failure(_PyXI_state_t *state, PyObject *obj, const char *msg) { - exceptions_t *state = &_PyInterpreterState_GetXIState(interp)->exceptions; - PyObject *exctype = state->PyExc_NotShareableError; + PyObject *exctype = state->exceptions.PyExc_NotShareableError; assert(exctype != NULL); if (msg != NULL) { assert(obj == NULL); @@ -226,10 +224,11 @@ int _PyObject_CheckXIData(PyObject *obj) { PyInterpreterState *interp = PyInterpreterState_Get(); + _PyXI_state_t *state = _PyXI_GET_STATE(interp); xidatafunc getdata = lookup_getdata(interp, obj); if (getdata == NULL) { if (!PyErr_Occurred()) { - _set_xid_lookup_failure(interp, obj, NULL); + _set_xid_lookup_failure(state, obj, NULL); } return -1; } @@ -241,6 +240,7 @@ _PyObject_GetXIData(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}; @@ -252,7 +252,7 @@ _PyObject_GetXIData(PyObject *obj, _PyXIData_t *data) if (getdata == NULL) { Py_DECREF(obj); if (!PyErr_Occurred()) { - _set_xid_lookup_failure(interp, obj, NULL); + _set_xid_lookup_failure(state, obj, NULL); } return -1; } @@ -969,6 +969,7 @@ _PyXI_ClearExcInfo(_PyXI_excinfo *info) static int _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp) { + _PyXI_state_t *state; assert(!PyErr_Occurred()); switch (code) { case _PyXI_ERR_NO_ERROR: _Py_FALLTHROUGH; @@ -999,7 +1000,8 @@ _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp) "failed to apply namespace to __main__"); break; case _PyXI_ERR_NOT_SHAREABLE: - _set_xid_lookup_failure(interp, NULL, NULL); + state = _PyXI_GET_STATE(interp); + _set_xid_lookup_failure(state, NULL, NULL); break; default: #ifdef Py_DEBUG @@ -1061,7 +1063,8 @@ _PyXI_ApplyError(_PyXI_error *error) } else if (error->code == _PyXI_ERR_NOT_SHAREABLE) { // Propagate the exception directly. - _set_xid_lookup_failure(error->interp, NULL, error->uncaught.msg); + _PyXI_state_t *state = _PyXI_GET_STATE(error->interp); + _set_xid_lookup_failure(state, NULL, error->uncaught.msg); } else { // Raise an exception corresponding to the code. @@ -1606,9 +1609,9 @@ _propagate_not_shareable_error(_PyXI_session *session) return; } PyInterpreterState *interp = PyInterpreterState_Get(); - exceptions_t *state = &_PyInterpreterState_GetXIState(interp)->exceptions; - assert(state->PyExc_NotShareableError != NULL); - if (PyErr_ExceptionMatches(state->PyExc_NotShareableError)) { + _PyXI_state_t *state = _PyXI_GET_STATE(interp); + assert(state->exceptions.PyExc_NotShareableError != NULL); + if (PyErr_ExceptionMatches(state->exceptions.PyExc_NotShareableError)) { // We want to propagate the exception directly. session->_error_override = _PyXI_ERR_NOT_SHAREABLE; session->error_override = &session->_error_override; @@ -1779,11 +1782,13 @@ _PyXI_Exit(_PyXI_session *session) 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); } - xid_lookup_init(&_PyXI_GET_STATE(interp)->data_lookup); + xid_lookup_init(&state->data_lookup); // Initialize exceptions.(heap types). // See _PyXI_InitTypes() for the static types. @@ -1801,12 +1806,14 @@ _PyXI_Init(PyInterpreterState *interp) void _PyXI_Fini(PyInterpreterState *interp) { + _PyXI_state_t *state = _PyXI_GET_STATE(interp); + // 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(&_PyXI_GET_STATE(interp)->data_lookup); + xid_lookup_fini(&state->data_lookup); if (_Py_IsMainInterpreter(interp)) { xid_lookup_fini(&_PyXI_GET_GLOBAL_STATE(interp)->data_lookup); }