| #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; |
| } _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 */ |