| #ifndef Py_INTERNAL_CROSSINTERP_H | |
| #define Py_INTERNAL_CROSSINTERP_H | |
| #ifdef __cplusplus | |
| extern "C" { | |
| #endif | |
| #ifndef Py_BUILD_CORE | |
| # error "this header requires Py_BUILD_CORE define" | |
| #endif | |
| #include "pycore_pyerrors.h" | |
| /**************/ | |
| /* exceptions */ | |
| /**************/ | |
| PyAPI_DATA(PyObject *) PyExc_InterpreterError; | |
| PyAPI_DATA(PyObject *) PyExc_InterpreterNotFoundError; | |
| /***************************/ | |
| /* cross-interpreter calls */ | |
| /***************************/ | |
| typedef int (*_Py_simple_func)(void *); | |
| extern int _Py_CallInInterpreter( | |
| PyInterpreterState *interp, | |
| _Py_simple_func func, | |
| void *arg); | |
| extern int _Py_CallInInterpreterAndRawFree( | |
| PyInterpreterState *interp, | |
| _Py_simple_func func, | |
| void *arg); | |
| /**************************/ | |
| /* cross-interpreter data */ | |
| /**************************/ | |
| 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 _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. | |
| void *data; | |
| // obj is the Python object from which the data was derived. This | |
| // is non-NULL only if the data remains bound to the object in some | |
| // way, such that the object must be "released" (via a decref) when | |
| // the data is released. In that case the code that sets the field, | |
| // likely a registered "xidatafunc", is responsible for | |
| // ensuring it owns the reference (i.e. incref). | |
| PyObject *obj; | |
| // interpid is the ID of the owning interpreter of the original | |
| // object. It corresponds to the active interpreter when | |
| // _PyObject_GetXIData() was called. This should only | |
| // be set by the cross-interpreter machinery. | |
| // | |
| // We use the ID rather than the PyInterpreterState to avoid issues | |
| // with deleted interpreters. Note that IDs are never re-used, so | |
| // each one will always correspond to a specific interpreter | |
| // (whether still alive or not). | |
| int64_t interpid; | |
| // new_object is a function that returns a new object in the current | |
| // interpreter given the data. The resulting object (a new | |
| // reference) will be equivalent to the original object. This field | |
| // is required. | |
| 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 | |
| // to NULL. However, for most the data was allocated just for | |
| // cross-interpreter use, so it must be freed when | |
| // _PyXIData_Release is called or the memory will | |
| // leak. In that case, at the very least this field should be set | |
| // to PyMem_RawFree (the default if not explicitly set to NULL). | |
| // The call will happen with the original interpreter activated. | |
| xid_freefunc free; | |
| }; | |
| PyAPI_FUNC(_PyXIData_t *) _PyXIData_New(void); | |
| PyAPI_FUNC(void) _PyXIData_Free(_PyXIData_t *data); | |
| #define _PyXIData_DATA(DATA) ((DATA)->data) | |
| #define _PyXIData_OBJ(DATA) ((DATA)->obj) | |
| #define _PyXIData_INTERPID(DATA) ((DATA)->interpid) | |
| // Users should not need getters for "new_object" or "free". | |
| /* defining cross-interpreter data */ | |
| PyAPI_FUNC(void) _PyXIData_Init( | |
| _PyXIData_t *data, | |
| PyInterpreterState *interp, void *shared, PyObject *obj, | |
| xid_newobjfunc new_object); | |
| PyAPI_FUNC(int) _PyXIData_InitWithSize( | |
| _PyXIData_t *, | |
| PyInterpreterState *interp, const size_t, PyObject *, | |
| xid_newobjfunc); | |
| PyAPI_FUNC(void) _PyXIData_Clear(PyInterpreterState *, _PyXIData_t *); | |
| // Normally the Init* functions are sufficient. The only time | |
| // additional initialization might be needed is to set the "free" func, | |
| // though that should be infrequent. | |
| #define _PyXIData_SET_FREE(DATA, FUNC) \ | |
| do { \ | |
| (DATA)->free = (FUNC); \ | |
| } while (0) | |
| #define _PyXIData_CHECK_FREE(DATA, FUNC) \ | |
| ((DATA)->free == (FUNC)) | |
| // Additionally, some shareable types are essentially light wrappers | |
| // around other shareable types. The xidatafunc of the wrapper | |
| // can often be implemented by calling the wrapped object's | |
| // xidatafunc and then changing the "new_object" function. | |
| // We have _PyXIData_SET_NEW_OBJECT() here for that, | |
| // but might be better to have a function like | |
| // _PyXIData_AdaptToWrapper() instead. | |
| #define _PyXIData_SET_NEW_OBJECT(DATA, FUNC) \ | |
| do { \ | |
| (DATA)->new_object = (FUNC); \ | |
| } while (0) | |
| #define _PyXIData_CHECK_NEW_OBJECT(DATA, FUNC) \ | |
| ((DATA)->new_object == (FUNC)) | |
| /* getting cross-interpreter data */ | |
| typedef int xidata_fallback_t; | |
| #define _PyXIDATA_XIDATA_ONLY (0) | |
| #define _PyXIDATA_FULL_FALLBACK (1) | |
| // Technically, we don't need two different function types; | |
| // we could go with just the fallback one. However, only container | |
| // types like tuple need it, so always having the extra arg would be | |
| // a bit unfortunate. It's also nice to be able to clearly distinguish | |
| // between types that might call _PyObject_GetXIData() and those that won't. | |
| // | |
| typedef int (*xidatafunc)(PyThreadState *, PyObject *, _PyXIData_t *); | |
| typedef int (*xidatafbfunc)( | |
| PyThreadState *, PyObject *, xidata_fallback_t, _PyXIData_t *); | |
| typedef struct { | |
| xidatafunc basic; | |
| xidatafbfunc fallback; | |
| } _PyXIData_getdata_t; | |
| PyAPI_FUNC(PyObject *) _PyXIData_GetNotShareableErrorType(PyThreadState *); | |
| PyAPI_FUNC(void) _PyXIData_SetNotShareableError(PyThreadState *, const char *); | |
| PyAPI_FUNC(void) _PyXIData_FormatNotShareableError( | |
| PyThreadState *, | |
| const char *, | |
| ...); | |
| PyAPI_FUNC(_PyXIData_getdata_t) _PyXIData_Lookup( | |
| PyThreadState *, | |
| PyObject *); | |
| PyAPI_FUNC(int) _PyObject_CheckXIData( | |
| PyThreadState *, | |
| PyObject *); | |
| PyAPI_FUNC(int) _PyObject_GetXIDataNoFallback( | |
| PyThreadState *, | |
| PyObject *, | |
| _PyXIData_t *); | |
| PyAPI_FUNC(int) _PyObject_GetXIData( | |
| PyThreadState *, | |
| PyObject *, | |
| xidata_fallback_t, | |
| _PyXIData_t *); | |
| // _PyObject_GetXIData() for bytes | |
| typedef struct { | |
| const char *bytes; | |
| Py_ssize_t len; | |
| } _PyBytes_data_t; | |
| PyAPI_FUNC(int) _PyBytes_GetData(PyObject *, _PyBytes_data_t *); | |
| PyAPI_FUNC(PyObject *) _PyBytes_FromData(_PyBytes_data_t *); | |
| PyAPI_FUNC(PyObject *) _PyBytes_FromXIData(_PyXIData_t *); | |
| PyAPI_FUNC(int) _PyBytes_GetXIData( | |
| PyThreadState *, | |
| PyObject *, | |
| _PyXIData_t *); | |
| PyAPI_FUNC(_PyBytes_data_t *) _PyBytes_GetXIDataWrapped( | |
| PyThreadState *, | |
| PyObject *, | |
| size_t, | |
| xid_newobjfunc, | |
| _PyXIData_t *); | |
| // _PyObject_GetXIData() for pickle | |
| PyAPI_DATA(PyObject *) _PyPickle_LoadFromXIData(_PyXIData_t *); | |
| PyAPI_FUNC(int) _PyPickle_GetXIData( | |
| PyThreadState *, | |
| PyObject *, | |
| _PyXIData_t *); | |
| // _PyObject_GetXIData() for marshal | |
| PyAPI_FUNC(PyObject *) _PyMarshal_ReadObjectFromXIData(_PyXIData_t *); | |
| PyAPI_FUNC(int) _PyMarshal_GetXIData( | |
| PyThreadState *, | |
| PyObject *, | |
| _PyXIData_t *); | |
| // _PyObject_GetXIData() for code objects | |
| PyAPI_FUNC(PyObject *) _PyCode_FromXIData(_PyXIData_t *); | |
| PyAPI_FUNC(int) _PyCode_GetXIData( | |
| PyThreadState *, | |
| PyObject *, | |
| _PyXIData_t *); | |
| PyAPI_FUNC(int) _PyCode_GetScriptXIData( | |
| PyThreadState *, | |
| PyObject *, | |
| _PyXIData_t *); | |
| PyAPI_FUNC(int) _PyCode_GetPureScriptXIData( | |
| PyThreadState *, | |
| PyObject *, | |
| _PyXIData_t *); | |
| // _PyObject_GetXIData() for functions | |
| PyAPI_FUNC(PyObject *) _PyFunction_FromXIData(_PyXIData_t *); | |
| PyAPI_FUNC(int) _PyFunction_GetXIData( | |
| PyThreadState *, | |
| PyObject *, | |
| _PyXIData_t *); | |
| /* using cross-interpreter data */ | |
| PyAPI_FUNC(PyObject *) _PyXIData_NewObject(_PyXIData_t *); | |
| PyAPI_FUNC(int) _PyXIData_Release(_PyXIData_t *); | |
| PyAPI_FUNC(int) _PyXIData_ReleaseAndRawFree(_PyXIData_t *); | |
| /* cross-interpreter data registry */ | |
| #define Py_CORE_CROSSINTERP_DATA_REGISTRY_H | |
| #include "pycore_crossinterp_data_registry.h" | |
| #undef Py_CORE_CROSSINTERP_DATA_REGISTRY_H | |
| /*****************************/ | |
| /* runtime state & lifecycle */ | |
| /*****************************/ | |
| typedef struct _xid_lookup_state _PyXIData_lookup_t; | |
| typedef struct { | |
| // builtin types | |
| _PyXIData_lookup_t data_lookup; | |
| } _PyXI_global_state_t; | |
| typedef struct { | |
| // heap types | |
| _PyXIData_lookup_t data_lookup; | |
| struct xi_exceptions { | |
| // static types | |
| PyObject *PyExc_InterpreterError; | |
| PyObject *PyExc_InterpreterNotFoundError; | |
| // heap types | |
| PyObject *PyExc_NotShareableError; | |
| } exceptions; | |
| // Cached references to pickle.dumps/loads (per-interpreter). | |
| struct { | |
| PyObject *dumps; | |
| PyObject *loads; | |
| } pickle; | |
| } _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 | |
| 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 *); | |
| /***************************/ | |
| /* short-term data sharing */ | |
| /***************************/ | |
| // Ultimately we'd like to preserve enough information about the | |
| // exception and traceback that we could re-constitute (or at least | |
| // simulate, a la traceback.TracebackException), and even chain, a copy | |
| // of the exception in the calling interpreter. | |
| typedef struct _excinfo { | |
| struct _excinfo_type { | |
| PyTypeObject *builtin; | |
| const char *name; | |
| const char *qualname; | |
| const char *module; | |
| } type; | |
| const char *msg; | |
| const char *errdisplay; | |
| } _PyXI_excinfo; | |
| PyAPI_FUNC(_PyXI_excinfo *) _PyXI_NewExcInfo(PyObject *exc); | |
| PyAPI_FUNC(void) _PyXI_FreeExcInfo(_PyXI_excinfo *info); | |
| PyAPI_FUNC(PyObject *) _PyXI_FormatExcInfo(_PyXI_excinfo *info); | |
| PyAPI_FUNC(PyObject *) _PyXI_ExcInfoAsObject(_PyXI_excinfo *info); | |
| typedef enum error_code { | |
| _PyXI_ERR_NO_ERROR = 0, | |
| _PyXI_ERR_UNCAUGHT_EXCEPTION = -1, | |
| _PyXI_ERR_OTHER = -2, | |
| _PyXI_ERR_NO_MEMORY = -3, | |
| _PyXI_ERR_ALREADY_RUNNING = -4, | |
| _PyXI_ERR_MAIN_NS_FAILURE = -5, | |
| _PyXI_ERR_APPLY_NS_FAILURE = -6, | |
| _PyXI_ERR_PRESERVE_FAILURE = -7, | |
| _PyXI_ERR_EXC_PROPAGATION_FAILURE = -8, | |
| _PyXI_ERR_NOT_SHAREABLE = -9, | |
| } _PyXI_errcode; | |
| typedef struct xi_failure _PyXI_failure; | |
| PyAPI_FUNC(_PyXI_failure *) _PyXI_NewFailure(void); | |
| PyAPI_FUNC(void) _PyXI_FreeFailure(_PyXI_failure *); | |
| PyAPI_FUNC(_PyXI_errcode) _PyXI_GetFailureCode(_PyXI_failure *); | |
| PyAPI_FUNC(int) _PyXI_InitFailure(_PyXI_failure *, _PyXI_errcode, PyObject *); | |
| PyAPI_FUNC(void) _PyXI_InitFailureUTF8( | |
| _PyXI_failure *, | |
| _PyXI_errcode, | |
| const char *); | |
| PyAPI_FUNC(int) _PyXI_UnwrapNotShareableError( | |
| PyThreadState *, | |
| _PyXI_failure *); | |
| // A cross-interpreter session involves entering an interpreter | |
| // with _PyXI_Enter(), doing some work with it, and finally exiting | |
| // that interpreter with _PyXI_Exit(). | |
| // | |
| // At the boundaries of the session, both entering and exiting, | |
| // data may be exchanged between the previous interpreter and the | |
| // target one in a thread-safe way that does not violate the | |
| // isolation between interpreters. This includes setting objects | |
| // in the target's __main__ module on the way in, and capturing | |
| // uncaught exceptions on the way out. | |
| typedef struct xi_session _PyXI_session; | |
| PyAPI_FUNC(_PyXI_session *) _PyXI_NewSession(void); | |
| PyAPI_FUNC(void) _PyXI_FreeSession(_PyXI_session *); | |
| typedef struct { | |
| PyObject *preserved; | |
| PyObject *excinfo; | |
| _PyXI_errcode errcode; | |
| } _PyXI_session_result; | |
| PyAPI_FUNC(void) _PyXI_ClearResult(_PyXI_session_result *); | |
| PyAPI_FUNC(int) _PyXI_Enter( | |
| _PyXI_session *session, | |
| PyInterpreterState *interp, | |
| PyObject *nsupdates, | |
| _PyXI_session_result *); | |
| PyAPI_FUNC(int) _PyXI_Exit( | |
| _PyXI_session *, | |
| _PyXI_failure *, | |
| _PyXI_session_result *); | |
| PyAPI_FUNC(PyObject *) _PyXI_GetMainNamespace( | |
| _PyXI_session *, | |
| _PyXI_failure *); | |
| PyAPI_FUNC(int) _PyXI_Preserve( | |
| _PyXI_session *, | |
| const char *, | |
| PyObject *, | |
| _PyXI_failure *); | |
| PyAPI_FUNC(PyObject *) _PyXI_GetPreserved( | |
| _PyXI_session_result *, | |
| const char *); | |
| /*************/ | |
| /* other API */ | |
| /*************/ | |
| // Export for _testinternalcapi shared extension | |
| PyAPI_FUNC(PyInterpreterState *) _PyXI_NewInterpreter( | |
| PyInterpreterConfig *config, | |
| long *maybe_whence, | |
| PyThreadState **p_tstate, | |
| PyThreadState **p_save_tstate); | |
| PyAPI_FUNC(void) _PyXI_EndInterpreter( | |
| PyInterpreterState *interp, | |
| PyThreadState *tstate, | |
| PyThreadState **p_save_tstate); | |
| #ifdef __cplusplus | |
| } | |
| #endif | |
| #endif /* !Py_INTERNAL_CROSSINTERP_H */ |