| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* API for managing interactions between isolated interpreters */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "Python.h"
 | 
					
						
							|  |  |  | #include "pycore_ceval.h"         // _Py_simple_func
 | 
					
						
							|  |  |  | #include "pycore_crossinterp.h"   // struct _xid
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | #include "pycore_initconfig.h"    // _PyStatus_OK()
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | #include "pycore_namespace.h"     //_PyNamespace_New()
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | #include "pycore_pyerrors.h"      // _PyErr_Clear()
 | 
					
						
							|  |  |  | #include "pycore_pystate.h"       // _PyInterpreterState_GET()
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | #include "pycore_typeobject.h"    // _PyType_GetModuleName()
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | #include "pycore_weakref.h"       // _PyWeakref_GET_REF()
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | /**************/ | 
					
						
							|  |  |  | /* exceptions */ | 
					
						
							|  |  |  | /**************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* InterpreterError extends Exception */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyTypeObject _PyExc_InterpreterError = { | 
					
						
							|  |  |  |     PyVarObject_HEAD_INIT(NULL, 0) | 
					
						
							|  |  |  |     .tp_name = "InterpreterError", | 
					
						
							|  |  |  |     .tp_doc = PyDoc_STR("An interpreter was not found."), | 
					
						
							|  |  |  |     //.tp_base = (PyTypeObject *)PyExc_BaseException,
 | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | PyObject *PyExc_InterpreterError = (PyObject *)&_PyExc_InterpreterError; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* InterpreterNotFoundError extends InterpreterError */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyTypeObject _PyExc_InterpreterNotFoundError = { | 
					
						
							|  |  |  |     PyVarObject_HEAD_INIT(NULL, 0) | 
					
						
							|  |  |  |     .tp_name = "InterpreterNotFoundError", | 
					
						
							|  |  |  |     .tp_doc = PyDoc_STR("An interpreter was not found."), | 
					
						
							|  |  |  |     .tp_base = &_PyExc_InterpreterError, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | PyObject *PyExc_InterpreterNotFoundError = (PyObject *)&_PyExc_InterpreterNotFoundError; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* lifecycle */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | init_exceptions(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyExc_InterpreterError.tp_base = (PyTypeObject *)PyExc_BaseException; | 
					
						
							|  |  |  |     if (_PyStaticType_InitBuiltin(interp, &_PyExc_InterpreterError) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (_PyStaticType_InitBuiltin(interp, &_PyExc_InterpreterNotFoundError) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | fini_exceptions(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyStaticType_Dealloc(interp, &_PyExc_InterpreterNotFoundError); | 
					
						
							|  |  |  |     _PyStaticType_Dealloc(interp, &_PyExc_InterpreterError); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | /***************************/ | 
					
						
							|  |  |  | /* cross-interpreter calls */ | 
					
						
							|  |  |  | /***************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _Py_CallInInterpreter(PyInterpreterState *interp, | 
					
						
							|  |  |  |                       _Py_simple_func func, void *arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (interp == _PyThreadState_GetCurrent()->interp) { | 
					
						
							|  |  |  |         return func(arg); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // XXX Emit a warning if this fails?
 | 
					
						
							|  |  |  |     _PyEval_AddPendingCall(interp, (_Py_pending_call_func)func, arg, 0); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _Py_CallInInterpreterAndRawFree(PyInterpreterState *interp, | 
					
						
							|  |  |  |                                 _Py_simple_func func, void *arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (interp == _PyThreadState_GetCurrent()->interp) { | 
					
						
							|  |  |  |         int res = func(arg); | 
					
						
							|  |  |  |         PyMem_RawFree(arg); | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // XXX Emit a warning if this fails?
 | 
					
						
							|  |  |  |     _PyEval_AddPendingCall(interp, func, arg, _Py_PENDING_RAWFREE); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**************************/ | 
					
						
							|  |  |  | /* cross-interpreter data */ | 
					
						
							|  |  |  | /**************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _PyCrossInterpreterData * | 
					
						
							|  |  |  | _PyCrossInterpreterData_New(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyCrossInterpreterData *xid = PyMem_RawMalloc( | 
					
						
							|  |  |  |                                             sizeof(_PyCrossInterpreterData)); | 
					
						
							|  |  |  |     if (xid == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return xid; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyCrossInterpreterData_Free(_PyCrossInterpreterData *xid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyInterpreterState *interp = PyInterpreterState_Get(); | 
					
						
							|  |  |  |     _PyCrossInterpreterData_Clear(interp, xid); | 
					
						
							|  |  |  |     PyMem_RawFree(xid); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | /* exceptions */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyStatus | 
					
						
							|  |  |  | _init_not_shareable_error_type(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     const char *name = "_interpreters.NotShareableError"; | 
					
						
							|  |  |  |     PyObject *base = PyExc_ValueError; | 
					
						
							|  |  |  |     PyObject *ns = NULL; | 
					
						
							|  |  |  |     PyObject *exctype = PyErr_NewException(name, base, ns); | 
					
						
							|  |  |  |     if (exctype == NULL) { | 
					
						
							|  |  |  |         PyErr_Clear(); | 
					
						
							|  |  |  |         return _PyStatus_ERR("could not initialize NotShareableError"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     interp->xi.PyExc_NotShareableError = exctype; | 
					
						
							|  |  |  |     return _PyStatus_OK(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _fini_not_shareable_error_type(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Py_CLEAR(interp->xi.PyExc_NotShareableError); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | _get_not_shareable_error_type(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(interp->xi.PyExc_NotShareableError != NULL); | 
					
						
							|  |  |  |     return interp->xi.PyExc_NotShareableError; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | /* defining cross-interpreter data */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void | 
					
						
							|  |  |  | _xidata_init(_PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // If the value is being reused
 | 
					
						
							|  |  |  |     // then _xidata_clear() should have been called already.
 | 
					
						
							|  |  |  |     assert(data->data == NULL); | 
					
						
							|  |  |  |     assert(data->obj == NULL); | 
					
						
							|  |  |  |     *data = (_PyCrossInterpreterData){0}; | 
					
						
							|  |  |  |     data->interpid = -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void | 
					
						
							|  |  |  | _xidata_clear(_PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // _PyCrossInterpreterData only has two members that need to be
 | 
					
						
							|  |  |  |     // cleaned up, if set: "data" must be freed and "obj" must be decref'ed.
 | 
					
						
							|  |  |  |     // In both cases the original (owning) interpreter must be used,
 | 
					
						
							|  |  |  |     // which is the caller's responsibility to ensure.
 | 
					
						
							|  |  |  |     if (data->data != NULL) { | 
					
						
							|  |  |  |         if (data->free != NULL) { | 
					
						
							|  |  |  |             data->free(data->data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         data->data = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_CLEAR(data->obj); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyCrossInterpreterData_Init(_PyCrossInterpreterData *data, | 
					
						
							|  |  |  |                              PyInterpreterState *interp, | 
					
						
							|  |  |  |                              void *shared, PyObject *obj, | 
					
						
							|  |  |  |                              xid_newobjectfunc new_object) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(data != NULL); | 
					
						
							|  |  |  |     assert(new_object != NULL); | 
					
						
							|  |  |  |     _xidata_init(data); | 
					
						
							|  |  |  |     data->data = shared; | 
					
						
							|  |  |  |     if (obj != NULL) { | 
					
						
							|  |  |  |         assert(interp != NULL); | 
					
						
							|  |  |  |         // released in _PyCrossInterpreterData_Clear()
 | 
					
						
							|  |  |  |         data->obj = Py_NewRef(obj); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // Ideally every object would know its owning interpreter.
 | 
					
						
							|  |  |  |     // Until then, we have to rely on the caller to identify it
 | 
					
						
							|  |  |  |     // (but we don't need it in all cases).
 | 
					
						
							|  |  |  |     data->interpid = (interp != NULL) ? interp->id : -1; | 
					
						
							|  |  |  |     data->new_object = new_object; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyCrossInterpreterData_InitWithSize(_PyCrossInterpreterData *data, | 
					
						
							|  |  |  |                                      PyInterpreterState *interp, | 
					
						
							|  |  |  |                                      const size_t size, PyObject *obj, | 
					
						
							|  |  |  |                                      xid_newobjectfunc new_object) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(size > 0); | 
					
						
							|  |  |  |     // For now we always free the shared data in the same interpreter
 | 
					
						
							|  |  |  |     // where it was allocated, so the interpreter is required.
 | 
					
						
							|  |  |  |     assert(interp != NULL); | 
					
						
							|  |  |  |     _PyCrossInterpreterData_Init(data, interp, NULL, obj, new_object); | 
					
						
							|  |  |  |     data->data = PyMem_RawMalloc(size); | 
					
						
							|  |  |  |     if (data->data == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     data->free = PyMem_RawFree; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyCrossInterpreterData_Clear(PyInterpreterState *interp, | 
					
						
							|  |  |  |                               _PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(data != NULL); | 
					
						
							|  |  |  |     // This must be called in the owning interpreter.
 | 
					
						
							|  |  |  |     assert(interp == NULL | 
					
						
							|  |  |  |            || data->interpid == -1 | 
					
						
							|  |  |  |            || data->interpid == interp->id); | 
					
						
							|  |  |  |     _xidata_clear(data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* using cross-interpreter data */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _check_xidata(PyThreadState *tstate, _PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // data->data can be anything, including NULL, so we don't check it.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // data->obj may be NULL, so we don't check it.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (data->interpid < 0) { | 
					
						
							|  |  |  |         _PyErr_SetString(tstate, PyExc_SystemError, "missing interp"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (data->new_object == NULL) { | 
					
						
							|  |  |  |         _PyErr_SetString(tstate, PyExc_SystemError, "missing new_object func"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // data->free may be NULL, so we don't check it.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | static crossinterpdatafunc _lookup_getdata_from_registry( | 
					
						
							|  |  |  |                                             PyInterpreterState *, PyObject *); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | static crossinterpdatafunc | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | _lookup_getdata(PyInterpreterState *interp, 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); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | crossinterpdatafunc | 
					
						
							|  |  |  | _PyCrossInterpreterData_Lookup(PyObject *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyInterpreterState *interp = _PyInterpreterState_GET(); | 
					
						
							|  |  |  |     return _lookup_getdata(interp, obj); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void | 
					
						
							|  |  |  | _set_xid_lookup_failure(PyInterpreterState *interp, | 
					
						
							|  |  |  |                         PyObject *obj, const char *msg) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     PyObject *exctype = _get_not_shareable_error_type(interp); | 
					
						
							|  |  |  |     assert(exctype != NULL); | 
					
						
							|  |  |  |     if (msg != NULL) { | 
					
						
							|  |  |  |         assert(obj == NULL); | 
					
						
							|  |  |  |         PyErr_SetString(exctype, msg); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (obj == NULL) { | 
					
						
							|  |  |  |         PyErr_SetString(exctype, | 
					
						
							|  |  |  |                         "object does not support cross-interpreter data"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         PyErr_Format(exctype, | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |                      "%S does not support cross-interpreter data", obj); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyObject_CheckCrossInterpreterData(PyObject *obj) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     PyInterpreterState *interp = _PyInterpreterState_GET(); | 
					
						
							|  |  |  |     crossinterpdatafunc getdata = _lookup_getdata(interp, obj); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     if (getdata == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         if (!PyErr_Occurred()) { | 
					
						
							|  |  |  |             _set_xid_lookup_failure(interp, obj, NULL); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyThreadState *tstate = _PyThreadState_GetCurrent(); | 
					
						
							|  |  |  | #ifdef Py_DEBUG
 | 
					
						
							|  |  |  |     // The caller must hold the GIL
 | 
					
						
							|  |  |  |     _Py_EnsureTstateNotNULL(tstate); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     PyInterpreterState *interp = tstate->interp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Reset data before re-populating.
 | 
					
						
							|  |  |  |     *data = (_PyCrossInterpreterData){0}; | 
					
						
							|  |  |  |     data->interpid = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Call the "getdata" func for the object.
 | 
					
						
							|  |  |  |     Py_INCREF(obj); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     crossinterpdatafunc getdata = _lookup_getdata(interp, obj); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     if (getdata == NULL) { | 
					
						
							|  |  |  |         Py_DECREF(obj); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         if (!PyErr_Occurred()) { | 
					
						
							|  |  |  |             _set_xid_lookup_failure(interp, obj, NULL); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     int res = getdata(tstate, obj, data); | 
					
						
							|  |  |  |     Py_DECREF(obj); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Fill in the blanks and validate the result.
 | 
					
						
							|  |  |  |     data->interpid = interp->id; | 
					
						
							|  |  |  |     if (_check_xidata(tstate, data) != 0) { | 
					
						
							|  |  |  |         (void)_PyCrossInterpreterData_Release(data); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyObject * | 
					
						
							|  |  |  | _PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return data->new_object(data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _call_clear_xidata(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _xidata_clear((_PyCrossInterpreterData *)data); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _xidata_release(_PyCrossInterpreterData *data, int rawfree) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if ((data->data == NULL || data->free == NULL) && data->obj == NULL) { | 
					
						
							|  |  |  |         // Nothing to release!
 | 
					
						
							|  |  |  |         if (rawfree) { | 
					
						
							|  |  |  |             PyMem_RawFree(data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             data->data = NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Switch to the original interpreter.
 | 
					
						
							|  |  |  |     PyInterpreterState *interp = _PyInterpreterState_LookUpID(data->interpid); | 
					
						
							|  |  |  |     if (interp == NULL) { | 
					
						
							|  |  |  |         // The interpreter was already destroyed.
 | 
					
						
							|  |  |  |         // This function shouldn't have been called.
 | 
					
						
							|  |  |  |         // XXX Someone leaked some memory...
 | 
					
						
							|  |  |  |         assert(PyErr_Occurred()); | 
					
						
							|  |  |  |         if (rawfree) { | 
					
						
							|  |  |  |             PyMem_RawFree(data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // "Release" the data and/or the object.
 | 
					
						
							|  |  |  |     if (rawfree) { | 
					
						
							|  |  |  |         return _Py_CallInInterpreterAndRawFree(interp, _call_clear_xidata, data); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         return _Py_CallInInterpreter(interp, _call_clear_xidata, data); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyCrossInterpreterData_Release(_PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return _xidata_release(data, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyCrossInterpreterData_ReleaseAndRawFree(_PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return _xidata_release(data, 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* registry of {type -> crossinterpdatafunc} */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* For now we use a global registry of shareable classes.  An
 | 
					
						
							|  |  |  |    alternative would be to add a tp_* slot for a class's | 
					
						
							|  |  |  |    crossinterpdatafunc. It would be simpler and more efficient. */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | static inline struct _xidregistry * | 
					
						
							|  |  |  | _get_global_xidregistry(_PyRuntimeState *runtime) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return &runtime->xi.registry; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline struct _xidregistry * | 
					
						
							|  |  |  | _get_xidregistry(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return &interp->xi.registry; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline struct _xidregistry * | 
					
						
							|  |  |  | _get_xidregistry_for_type(PyInterpreterState *interp, PyTypeObject *cls) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct _xidregistry *registry = _get_global_xidregistry(interp->runtime); | 
					
						
							|  |  |  |     if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) { | 
					
						
							|  |  |  |         registry = _get_xidregistry(interp); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return registry; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | _xidregistry_add_type(struct _xidregistry *xidregistry, | 
					
						
							|  |  |  |                       PyTypeObject *cls, crossinterpdatafunc getdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct _xidregitem *newhead = PyMem_RawMalloc(sizeof(struct _xidregitem)); | 
					
						
							|  |  |  |     if (newhead == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *newhead = (struct _xidregitem){ | 
					
						
							|  |  |  |         // We do not keep a reference, to avoid keeping the class alive.
 | 
					
						
							|  |  |  |         .cls = cls, | 
					
						
							|  |  |  |         .refcount = 1, | 
					
						
							|  |  |  |         .getdata = getdata, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) { | 
					
						
							|  |  |  |         // XXX Assign a callback to clear the entry from the registry?
 | 
					
						
							|  |  |  |         newhead->weakref = PyWeakref_NewRef((PyObject *)cls, NULL); | 
					
						
							|  |  |  |         if (newhead->weakref == NULL) { | 
					
						
							|  |  |  |             PyMem_RawFree(newhead); | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     newhead->next = xidregistry->head; | 
					
						
							|  |  |  |     if (newhead->next != NULL) { | 
					
						
							|  |  |  |         newhead->next->prev = newhead; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     xidregistry->head = newhead; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct _xidregitem * | 
					
						
							|  |  |  | _xidregistry_remove_entry(struct _xidregistry *xidregistry, | 
					
						
							|  |  |  |                           struct _xidregitem *entry) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct _xidregitem *next = entry->next; | 
					
						
							|  |  |  |     if (entry->prev != NULL) { | 
					
						
							|  |  |  |         assert(entry->prev->next == entry); | 
					
						
							|  |  |  |         entry->prev->next = next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         assert(xidregistry->head == entry); | 
					
						
							|  |  |  |         xidregistry->head = next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (next != NULL) { | 
					
						
							|  |  |  |         next->prev = entry->prev; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_XDECREF(entry->weakref); | 
					
						
							|  |  |  |     PyMem_RawFree(entry); | 
					
						
							|  |  |  |     return next; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | static void | 
					
						
							|  |  |  | _xidregistry_clear(struct _xidregistry *xidregistry) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     struct _xidregitem *cur = xidregistry->head; | 
					
						
							|  |  |  |     xidregistry->head = NULL; | 
					
						
							|  |  |  |     while (cur != NULL) { | 
					
						
							|  |  |  |         struct _xidregitem *next = cur->next; | 
					
						
							|  |  |  |         Py_XDECREF(cur->weakref); | 
					
						
							|  |  |  |         PyMem_RawFree(cur); | 
					
						
							|  |  |  |         cur = next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | static void | 
					
						
							|  |  |  | _xidregistry_lock(struct _xidregistry *registry) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-12-07 14:33:40 -05:00
										 |  |  |     if (registry->global) { | 
					
						
							|  |  |  |         PyMutex_Lock(®istry->mutex); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-07 14:33:40 -05:00
										 |  |  |     // else: Within an interpreter we rely on the GIL instead of a separate lock.
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _xidregistry_unlock(struct _xidregistry *registry) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-12-07 14:33:40 -05:00
										 |  |  |     if (registry->global) { | 
					
						
							|  |  |  |         PyMutex_Unlock(®istry->mutex); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | static struct _xidregitem * | 
					
						
							|  |  |  | _xidregistry_find_type(struct _xidregistry *xidregistry, PyTypeObject *cls) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct _xidregitem *cur = xidregistry->head; | 
					
						
							|  |  |  |     while (cur != NULL) { | 
					
						
							|  |  |  |         if (cur->weakref != NULL) { | 
					
						
							|  |  |  |             // cur is/was a heap type.
 | 
					
						
							|  |  |  |             PyObject *registered = _PyWeakref_GET_REF(cur->weakref); | 
					
						
							|  |  |  |             if (registered == NULL) { | 
					
						
							|  |  |  |                 // The weakly ref'ed object was freed.
 | 
					
						
							|  |  |  |                 cur = _xidregistry_remove_entry(xidregistry, cur); | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             assert(PyType_Check(registered)); | 
					
						
							|  |  |  |             assert(cur->cls == (PyTypeObject *)registered); | 
					
						
							|  |  |  |             assert(cur->cls->tp_flags & Py_TPFLAGS_HEAPTYPE); | 
					
						
							|  |  |  |             Py_DECREF(registered); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (cur->cls == cls) { | 
					
						
							|  |  |  |             return cur; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         cur = cur->next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyCrossInterpreterData_RegisterClass(PyTypeObject *cls, | 
					
						
							|  |  |  |                                       crossinterpdatafunc getdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!PyType_Check(cls)) { | 
					
						
							|  |  |  |         PyErr_Format(PyExc_ValueError, "only classes may be registered"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (getdata == NULL) { | 
					
						
							|  |  |  |         PyErr_Format(PyExc_ValueError, "missing 'getdata' func"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int res = 0; | 
					
						
							|  |  |  |     PyInterpreterState *interp = _PyInterpreterState_GET(); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls); | 
					
						
							|  |  |  |     _xidregistry_lock(xidregistry); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls); | 
					
						
							|  |  |  |     if (matched != NULL) { | 
					
						
							|  |  |  |         assert(matched->getdata == getdata); | 
					
						
							|  |  |  |         matched->refcount += 1; | 
					
						
							|  |  |  |         goto finally; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = _xidregistry_add_type(xidregistry, cls, getdata); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | finally: | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     _xidregistry_unlock(xidregistry); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyCrossInterpreterData_UnregisterClass(PyTypeObject *cls) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int res = 0; | 
					
						
							|  |  |  |     PyInterpreterState *interp = _PyInterpreterState_GET(); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls); | 
					
						
							|  |  |  |     _xidregistry_lock(xidregistry); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls); | 
					
						
							|  |  |  |     if (matched != NULL) { | 
					
						
							|  |  |  |         assert(matched->refcount > 0); | 
					
						
							|  |  |  |         matched->refcount -= 1; | 
					
						
							|  |  |  |         if (matched->refcount == 0) { | 
					
						
							|  |  |  |             (void)_xidregistry_remove_entry(xidregistry, matched); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         res = 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     _xidregistry_unlock(xidregistry); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | static crossinterpdatafunc | 
					
						
							|  |  |  | _lookup_getdata_from_registry(PyInterpreterState *interp, PyObject *obj) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     PyTypeObject *cls = Py_TYPE(obj); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls); | 
					
						
							|  |  |  |     _xidregistry_lock(xidregistry); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls); | 
					
						
							|  |  |  |     crossinterpdatafunc func = matched != NULL ? matched->getdata : NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     _xidregistry_unlock(xidregistry); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     return func; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* cross-interpreter data for builtin types */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | // bytes
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | struct _shared_bytes_data { | 
					
						
							|  |  |  |     char *bytes; | 
					
						
							|  |  |  |     Py_ssize_t len; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | _new_bytes_object(_PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct _shared_bytes_data *shared = (struct _shared_bytes_data *)(data->data); | 
					
						
							|  |  |  |     return PyBytes_FromStringAndSize(shared->bytes, shared->len); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _bytes_shared(PyThreadState *tstate, PyObject *obj, | 
					
						
							|  |  |  |               _PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (_PyCrossInterpreterData_InitWithSize( | 
					
						
							|  |  |  |             data, tstate->interp, sizeof(struct _shared_bytes_data), obj, | 
					
						
							|  |  |  |             _new_bytes_object | 
					
						
							|  |  |  |             ) < 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     struct _shared_bytes_data *shared = (struct _shared_bytes_data *)data->data; | 
					
						
							|  |  |  |     if (PyBytes_AsStringAndSize(obj, &shared->bytes, &shared->len) < 0) { | 
					
						
							|  |  |  |         _PyCrossInterpreterData_Clear(tstate->interp, data); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | // str
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | struct _shared_str_data { | 
					
						
							|  |  |  |     int kind; | 
					
						
							|  |  |  |     const void *buffer; | 
					
						
							|  |  |  |     Py_ssize_t len; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | _new_str_object(_PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct _shared_str_data *shared = (struct _shared_str_data *)(data->data); | 
					
						
							|  |  |  |     return PyUnicode_FromKindAndData(shared->kind, shared->buffer, shared->len); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _str_shared(PyThreadState *tstate, PyObject *obj, | 
					
						
							|  |  |  |             _PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (_PyCrossInterpreterData_InitWithSize( | 
					
						
							|  |  |  |             data, tstate->interp, sizeof(struct _shared_str_data), obj, | 
					
						
							|  |  |  |             _new_str_object | 
					
						
							|  |  |  |             ) < 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     struct _shared_str_data *shared = (struct _shared_str_data *)data->data; | 
					
						
							|  |  |  |     shared->kind = PyUnicode_KIND(obj); | 
					
						
							|  |  |  |     shared->buffer = PyUnicode_DATA(obj); | 
					
						
							|  |  |  |     shared->len = PyUnicode_GET_LENGTH(obj); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | // int
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | static PyObject * | 
					
						
							|  |  |  | _new_long_object(_PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return PyLong_FromSsize_t((Py_ssize_t)(data->data)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _long_shared(PyThreadState *tstate, PyObject *obj, | 
					
						
							|  |  |  |              _PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* Note that this means the size of shareable ints is bounded by
 | 
					
						
							|  |  |  |      * sys.maxsize.  Hence on 32-bit architectures that is half the | 
					
						
							|  |  |  |      * size of maximum shareable ints on 64-bit. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     Py_ssize_t value = PyLong_AsSsize_t(obj); | 
					
						
							|  |  |  |     if (value == -1 && PyErr_Occurred()) { | 
					
						
							|  |  |  |         if (PyErr_ExceptionMatches(PyExc_OverflowError)) { | 
					
						
							|  |  |  |             PyErr_SetString(PyExc_OverflowError, "try sending as bytes"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _PyCrossInterpreterData_Init(data, tstate->interp, (void *)value, NULL, | 
					
						
							|  |  |  |             _new_long_object); | 
					
						
							|  |  |  |     // data->obj and data->free remain NULL
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | // float
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-31 23:17:20 +09:00
										 |  |  | static PyObject * | 
					
						
							|  |  |  | _new_float_object(_PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     double * value_ptr = data->data; | 
					
						
							|  |  |  |     return PyFloat_FromDouble(*value_ptr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _float_shared(PyThreadState *tstate, PyObject *obj, | 
					
						
							|  |  |  |              _PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (_PyCrossInterpreterData_InitWithSize( | 
					
						
							|  |  |  |             data, tstate->interp, sizeof(double), NULL, | 
					
						
							|  |  |  |             _new_float_object | 
					
						
							|  |  |  |             ) < 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     double *shared = (double *)data->data; | 
					
						
							|  |  |  |     *shared = PyFloat_AsDouble(obj); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | // None
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | static PyObject * | 
					
						
							|  |  |  | _new_none_object(_PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // XXX Singleton refcounts are problematic across interpreters...
 | 
					
						
							|  |  |  |     return Py_NewRef(Py_None); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _none_shared(PyThreadState *tstate, PyObject *obj, | 
					
						
							|  |  |  |              _PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyCrossInterpreterData_Init(data, tstate->interp, NULL, NULL, | 
					
						
							|  |  |  |             _new_none_object); | 
					
						
							|  |  |  |     // data->data, data->obj and data->free remain NULL
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | // bool
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-02 09:09:01 +09:00
										 |  |  | static PyObject * | 
					
						
							|  |  |  | _new_bool_object(_PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (data->data){ | 
					
						
							|  |  |  |         Py_RETURN_TRUE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_RETURN_FALSE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _bool_shared(PyThreadState *tstate, PyObject *obj, | 
					
						
							|  |  |  |              _PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyCrossInterpreterData_Init(data, tstate->interp, | 
					
						
							|  |  |  |             (void *) (Py_IsTrue(obj) ? (uintptr_t) 1 : (uintptr_t) 0), NULL, | 
					
						
							|  |  |  |             _new_bool_object); | 
					
						
							|  |  |  |     // data->obj and data->free remain NULL
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | // tuple
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-08 02:58:29 +09:00
										 |  |  | struct _shared_tuple_data { | 
					
						
							|  |  |  |     Py_ssize_t len; | 
					
						
							|  |  |  |     _PyCrossInterpreterData **data; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | _new_tuple_object(_PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(data->data); | 
					
						
							|  |  |  |     PyObject *tuple = PyTuple_New(shared->len); | 
					
						
							|  |  |  |     if (tuple == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (Py_ssize_t i = 0; i < shared->len; i++) { | 
					
						
							|  |  |  |         PyObject *item = _PyCrossInterpreterData_NewObject(shared->data[i]); | 
					
						
							|  |  |  |         if (item == NULL){ | 
					
						
							|  |  |  |             Py_DECREF(tuple); | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         PyTuple_SET_ITEM(tuple, i, item); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return tuple; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _tuple_shared_free(void* data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(data); | 
					
						
							|  |  |  | #ifndef NDEBUG
 | 
					
						
							|  |  |  |     int64_t interpid = PyInterpreterState_GetID(_PyInterpreterState_GET()); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     for (Py_ssize_t i = 0; i < shared->len; i++) { | 
					
						
							|  |  |  |         if (shared->data[i] != NULL) { | 
					
						
							|  |  |  |             assert(shared->data[i]->interpid == interpid); | 
					
						
							|  |  |  |             _PyCrossInterpreterData_Release(shared->data[i]); | 
					
						
							|  |  |  |             PyMem_RawFree(shared->data[i]); | 
					
						
							|  |  |  |             shared->data[i] = NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyMem_Free(shared->data); | 
					
						
							|  |  |  |     PyMem_RawFree(shared); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _tuple_shared(PyThreadState *tstate, PyObject *obj, | 
					
						
							|  |  |  |              _PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Py_ssize_t len = PyTuple_GET_SIZE(obj); | 
					
						
							|  |  |  |     if (len < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     struct _shared_tuple_data *shared = PyMem_RawMalloc(sizeof(struct _shared_tuple_data)); | 
					
						
							|  |  |  |     if (shared == NULL){ | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     shared->len = len; | 
					
						
							|  |  |  |     shared->data = (_PyCrossInterpreterData **) PyMem_Calloc(shared->len, sizeof(_PyCrossInterpreterData *)); | 
					
						
							|  |  |  |     if (shared->data == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (Py_ssize_t i = 0; i < shared->len; i++) { | 
					
						
							|  |  |  |         _PyCrossInterpreterData *data = _PyCrossInterpreterData_New(); | 
					
						
							|  |  |  |         if (data == NULL) { | 
					
						
							|  |  |  |             goto error;  // PyErr_NoMemory already set
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         PyObject *item = PyTuple_GET_ITEM(obj, i); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         int res = -1; | 
					
						
							|  |  |  |         if (!_Py_EnterRecursiveCallTstate(tstate, " while sharing a tuple")) { | 
					
						
							|  |  |  |             res = _PyObject_GetCrossInterpreterData(item, data); | 
					
						
							|  |  |  |             _Py_LeaveRecursiveCallTstate(tstate); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (res < 0) { | 
					
						
							|  |  |  |             PyMem_RawFree(data); | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         shared->data[i] = data; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _PyCrossInterpreterData_Init( | 
					
						
							|  |  |  |             data, tstate->interp, shared, obj, _new_tuple_object); | 
					
						
							|  |  |  |     data->free = _tuple_shared_free; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     _tuple_shared_free(shared); | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | // registration
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | static void | 
					
						
							|  |  |  | _register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // None
 | 
					
						
							|  |  |  |     if (_xidregistry_add_type(xidregistry, (PyTypeObject *)PyObject_Type(Py_None), _none_shared) != 0) { | 
					
						
							|  |  |  |         Py_FatalError("could not register None for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // int
 | 
					
						
							|  |  |  |     if (_xidregistry_add_type(xidregistry, &PyLong_Type, _long_shared) != 0) { | 
					
						
							|  |  |  |         Py_FatalError("could not register int for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // bytes
 | 
					
						
							|  |  |  |     if (_xidregistry_add_type(xidregistry, &PyBytes_Type, _bytes_shared) != 0) { | 
					
						
							|  |  |  |         Py_FatalError("could not register bytes for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // str
 | 
					
						
							|  |  |  |     if (_xidregistry_add_type(xidregistry, &PyUnicode_Type, _str_shared) != 0) { | 
					
						
							|  |  |  |         Py_FatalError("could not register str for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-10-31 23:17:20 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-02 09:09:01 +09:00
										 |  |  |     // bool
 | 
					
						
							|  |  |  |     if (_xidregistry_add_type(xidregistry, &PyBool_Type, _bool_shared) != 0) { | 
					
						
							|  |  |  |         Py_FatalError("could not register bool for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-31 23:17:20 +09:00
										 |  |  |     // float
 | 
					
						
							|  |  |  |     if (_xidregistry_add_type(xidregistry, &PyFloat_Type, _float_shared) != 0) { | 
					
						
							|  |  |  |         Py_FatalError("could not register float for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-08 02:58:29 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // tuple
 | 
					
						
							|  |  |  |     if (_xidregistry_add_type(xidregistry, &PyTuple_Type, _tuple_shared) != 0) { | 
					
						
							|  |  |  |         Py_FatalError("could not register tuple for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* registry lifecycle */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _xidregistry_init(struct _xidregistry *registry) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (registry->initialized) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     registry->initialized = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (registry->global) { | 
					
						
							|  |  |  |         // Registering the builtins is cheap so we don't bother doing it lazily.
 | 
					
						
							|  |  |  |         assert(registry->head == NULL); | 
					
						
							|  |  |  |         _register_builtins_for_crossinterpreter_data(registry); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _xidregistry_fini(struct _xidregistry *registry) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!registry->initialized) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     registry->initialized = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _xidregistry_clear(registry); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*************************/ | 
					
						
							|  |  |  | /* convenience utilities */ | 
					
						
							|  |  |  | /*************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | static const char * | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  | _copy_string_obj_raw(PyObject *strobj, Py_ssize_t *p_size) | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     Py_ssize_t size = -1; | 
					
						
							|  |  |  |     const char *str = PyUnicode_AsUTF8AndSize(strobj, &size); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     if (str == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     char *copied = PyMem_RawMalloc(size+1); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     if (copied == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     strcpy(copied, str); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     if (p_size != NULL) { | 
					
						
							|  |  |  |         *p_size = size; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     return copied; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  | _convert_exc_to_TracebackException(PyObject *exc, PyObject **p_tbexc) | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     PyObject *args = NULL; | 
					
						
							|  |  |  |     PyObject *kwargs = NULL; | 
					
						
							|  |  |  |     PyObject *create = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // This is inspired by _PyErr_Display().
 | 
					
						
							|  |  |  |     PyObject *tbmod = PyImport_ImportModule("traceback"); | 
					
						
							|  |  |  |     if (tbmod == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     PyObject *tbexc_type = PyObject_GetAttrString(tbmod, "TracebackException"); | 
					
						
							|  |  |  |     Py_DECREF(tbmod); | 
					
						
							|  |  |  |     if (tbexc_type == NULL) { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     create = PyObject_GetAttrString(tbexc_type, "from_exception"); | 
					
						
							|  |  |  |     Py_DECREF(tbexc_type); | 
					
						
							|  |  |  |     if (create == NULL) { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     args = PyTuple_Pack(1, exc); | 
					
						
							|  |  |  |     if (args == NULL) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     kwargs = PyDict_New(); | 
					
						
							|  |  |  |     if (kwargs == NULL) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (PyDict_SetItemString(kwargs, "save_exc_type", Py_False) < 0) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (PyDict_SetItemString(kwargs, "lookup_lines", Py_False) < 0) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PyObject *tbexc = PyObject_Call(create, args, kwargs); | 
					
						
							|  |  |  |     Py_DECREF(args); | 
					
						
							|  |  |  |     Py_DECREF(kwargs); | 
					
						
							|  |  |  |     Py_DECREF(create); | 
					
						
							|  |  |  |     if (tbexc == NULL) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     *p_tbexc = tbexc; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     Py_XDECREF(args); | 
					
						
							|  |  |  |     Py_XDECREF(kwargs); | 
					
						
							|  |  |  |     Py_XDECREF(create); | 
					
						
							|  |  |  |     return -1; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static const char * | 
					
						
							|  |  |  | _format_TracebackException(PyObject *tbexc) | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     PyObject *lines = PyObject_CallMethod(tbexc, "format", NULL); | 
					
						
							|  |  |  |     if (lines == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     PyObject *formatted_obj = PyUnicode_Join(&_Py_STR(empty), lines); | 
					
						
							|  |  |  |     Py_DECREF(lines); | 
					
						
							|  |  |  |     if (formatted_obj == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Py_ssize_t size = -1; | 
					
						
							|  |  |  |     const char *formatted = _copy_string_obj_raw(formatted_obj, &size); | 
					
						
							|  |  |  |     Py_DECREF(formatted_obj); | 
					
						
							|  |  |  |     // We remove trailing the newline added by TracebackException.format().
 | 
					
						
							|  |  |  |     assert(formatted[size-1] == '\n'); | 
					
						
							|  |  |  |     ((char *)formatted)[size-1] = '\0'; | 
					
						
							|  |  |  |     return formatted; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | _release_xid_data(_PyCrossInterpreterData *data, int rawfree) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *exc = PyErr_GetRaisedException(); | 
					
						
							|  |  |  |     int res = rawfree | 
					
						
							|  |  |  |         ? _PyCrossInterpreterData_Release(data) | 
					
						
							|  |  |  |         : _PyCrossInterpreterData_ReleaseAndRawFree(data); | 
					
						
							|  |  |  |     if (res < 0) { | 
					
						
							|  |  |  |         /* The owning interpreter is already destroyed. */ | 
					
						
							|  |  |  |         _PyCrossInterpreterData_Clear(NULL, data); | 
					
						
							|  |  |  |         // XXX Emit a warning?
 | 
					
						
							|  |  |  |         PyErr_Clear(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyErr_SetRaisedException(exc); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | /***********************/ | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | /* exception snapshots */ | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | /***********************/ | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | _excinfo_init_type(struct _excinfo_type *info, PyObject *exc) | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     /* Note that this copies directly rather than into an intermediate
 | 
					
						
							|  |  |  |        struct and does not clear on error.  If we need that then we | 
					
						
							|  |  |  |        should have a separate function to wrap this one | 
					
						
							|  |  |  |        and do all that there. */ | 
					
						
							|  |  |  |     PyObject *strobj = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PyTypeObject *type = Py_TYPE(exc); | 
					
						
							|  |  |  |     if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { | 
					
						
							|  |  |  |         assert(_Py_IsImmortal((PyObject *)type)); | 
					
						
							|  |  |  |         info->builtin = type; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     else { | 
					
						
							|  |  |  |         // Only builtin types are preserved.
 | 
					
						
							|  |  |  |         info->builtin = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // __name__
 | 
					
						
							|  |  |  |     strobj = PyType_GetName(type); | 
					
						
							|  |  |  |     if (strobj == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     info->name = _copy_string_obj_raw(strobj, NULL); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     Py_DECREF(strobj); | 
					
						
							|  |  |  |     if (info->name == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     // __qualname__
 | 
					
						
							|  |  |  |     strobj = PyType_GetQualName(type); | 
					
						
							|  |  |  |     if (strobj == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     info->qualname = _copy_string_obj_raw(strobj, NULL); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     Py_DECREF(strobj); | 
					
						
							|  |  |  |     if (info->name == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // __module__
 | 
					
						
							|  |  |  |     strobj = _PyType_GetModuleName(type); | 
					
						
							|  |  |  |     if (strobj == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     info->module = _copy_string_obj_raw(strobj, NULL); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     Py_DECREF(strobj); | 
					
						
							|  |  |  |     if (info->name == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | _excinfo_clear_type(struct _excinfo_type *info) | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     if (info->builtin != NULL) { | 
					
						
							|  |  |  |         assert(info->builtin->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN); | 
					
						
							|  |  |  |         assert(_Py_IsImmortal((PyObject *)info->builtin)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (info->name != NULL) { | 
					
						
							|  |  |  |         PyMem_RawFree((void *)info->name); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (info->qualname != NULL) { | 
					
						
							|  |  |  |         PyMem_RawFree((void *)info->qualname); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (info->module != NULL) { | 
					
						
							|  |  |  |         PyMem_RawFree((void *)info->module); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *info = (struct _excinfo_type){NULL}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _excinfo_normalize_type(struct _excinfo_type *info, | 
					
						
							|  |  |  |                         const char **p_module, const char **p_qualname) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (info->name == NULL) { | 
					
						
							|  |  |  |         assert(info->builtin == NULL); | 
					
						
							|  |  |  |         assert(info->qualname == NULL); | 
					
						
							|  |  |  |         assert(info->module == NULL); | 
					
						
							|  |  |  |         // This is inspired by TracebackException.format_exception_only().
 | 
					
						
							|  |  |  |         *p_module = NULL; | 
					
						
							|  |  |  |         *p_qualname = NULL; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const char *module = info->module; | 
					
						
							|  |  |  |     const char *qualname = info->qualname; | 
					
						
							|  |  |  |     if (qualname == NULL) { | 
					
						
							|  |  |  |         qualname = info->name; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     assert(module != NULL); | 
					
						
							|  |  |  |     if (strcmp(module, "builtins") == 0) { | 
					
						
							|  |  |  |         module = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (strcmp(module, "__main__") == 0) { | 
					
						
							|  |  |  |         module = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *p_qualname = qualname; | 
					
						
							|  |  |  |     *p_module = module; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _PyXI_excinfo_Clear(_PyXI_excinfo *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _excinfo_clear_type(&info->type); | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     if (info->msg != NULL) { | 
					
						
							|  |  |  |         PyMem_RawFree((void *)info->msg); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     if (info->errdisplay != NULL) { | 
					
						
							|  |  |  |         PyMem_RawFree((void *)info->errdisplay); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     *info = (_PyXI_excinfo){{NULL}}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | _PyXI_excinfo_format(_PyXI_excinfo *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     const char *module, *qualname; | 
					
						
							|  |  |  |     _excinfo_normalize_type(&info->type, &module, &qualname); | 
					
						
							|  |  |  |     if (qualname != NULL) { | 
					
						
							|  |  |  |         if (module != NULL) { | 
					
						
							|  |  |  |             if (info->msg != NULL) { | 
					
						
							|  |  |  |                 return PyUnicode_FromFormat("%s.%s: %s", | 
					
						
							|  |  |  |                                             module, qualname, info->msg); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 return PyUnicode_FromFormat("%s.%s", module, qualname); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             if (info->msg != NULL) { | 
					
						
							|  |  |  |                 return PyUnicode_FromFormat("%s: %s", qualname, info->msg); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 return PyUnicode_FromString(qualname); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (info->msg != NULL) { | 
					
						
							|  |  |  |         return PyUnicode_FromString(info->msg); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         Py_RETURN_NONE; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char * | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | _PyXI_excinfo_InitFromException(_PyXI_excinfo *info, PyObject *exc) | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     assert(exc != NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     if (PyErr_GivenExceptionMatches(exc, PyExc_MemoryError)) { | 
					
						
							|  |  |  |         _PyXI_excinfo_Clear(info); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const char *failure = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (_excinfo_init_type(&info->type, exc) < 0) { | 
					
						
							|  |  |  |         failure = "error while initializing exception type snapshot"; | 
					
						
							|  |  |  |         goto error; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Extract the exception message.
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     PyObject *msgobj = PyObject_Str(exc); | 
					
						
							|  |  |  |     if (msgobj == NULL) { | 
					
						
							|  |  |  |         failure = "error while formatting exception"; | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     info->msg = _copy_string_obj_raw(msgobj, NULL); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     Py_DECREF(msgobj); | 
					
						
							|  |  |  |     if (info->msg == NULL) { | 
					
						
							|  |  |  |         failure = "error while copying exception message"; | 
					
						
							|  |  |  |         goto error; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     // Pickle a traceback.TracebackException.
 | 
					
						
							|  |  |  |     PyObject *tbexc = NULL; | 
					
						
							|  |  |  |     if (_convert_exc_to_TracebackException(exc, &tbexc) < 0) { | 
					
						
							|  |  |  | #ifdef Py_DEBUG
 | 
					
						
							|  |  |  |         PyErr_FormatUnraisable("Exception ignored while creating TracebackException"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |         PyErr_Clear(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |         info->errdisplay = _format_TracebackException(tbexc); | 
					
						
							|  |  |  |         Py_DECREF(tbexc); | 
					
						
							|  |  |  |         if (info->errdisplay == NULL) { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | #ifdef Py_DEBUG
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |             PyErr_FormatUnraisable("Exception ignored while formating TracebackException"); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  |             PyErr_Clear(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     assert(failure != NULL); | 
					
						
							|  |  |  |     _PyXI_excinfo_Clear(info); | 
					
						
							|  |  |  |     return failure; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | _PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     PyObject *tbexc = NULL; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     if (info->errdisplay != NULL) { | 
					
						
							|  |  |  |         tbexc = PyUnicode_FromString(info->errdisplay); | 
					
						
							|  |  |  |         if (tbexc == NULL) { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |             PyErr_Clear(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     PyObject *formatted = _PyXI_excinfo_format(info); | 
					
						
							|  |  |  |     PyErr_SetObject(exctype, formatted); | 
					
						
							|  |  |  |     Py_DECREF(formatted); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (tbexc != NULL) { | 
					
						
							|  |  |  |         PyObject *exc = PyErr_GetRaisedException(); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |         if (PyObject_SetAttrString(exc, "_errdisplay", tbexc) < 0) { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | #ifdef Py_DEBUG
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |             PyErr_FormatUnraisable("Exception ignored when setting _errdisplay"); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  |             PyErr_Clear(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         Py_DECREF(tbexc); | 
					
						
							|  |  |  |         PyErr_SetRaisedException(exc); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | _PyXI_excinfo_TypeAsObject(_PyXI_excinfo *info) | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     PyObject *ns = _PyNamespace_New(NULL); | 
					
						
							|  |  |  |     if (ns == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     int empty = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (info->type.name != NULL) { | 
					
						
							|  |  |  |         PyObject *name = PyUnicode_FromString(info->type.name); | 
					
						
							|  |  |  |         if (name == NULL) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         int res = PyObject_SetAttrString(ns, "__name__", name); | 
					
						
							|  |  |  |         Py_DECREF(name); | 
					
						
							|  |  |  |         if (res < 0) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         empty = 0; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (info->type.qualname != NULL) { | 
					
						
							|  |  |  |         PyObject *qualname = PyUnicode_FromString(info->type.qualname); | 
					
						
							|  |  |  |         if (qualname == NULL) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         int res = PyObject_SetAttrString(ns, "__qualname__", qualname); | 
					
						
							|  |  |  |         Py_DECREF(qualname); | 
					
						
							|  |  |  |         if (res < 0) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         empty = 0; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (info->type.module != NULL) { | 
					
						
							|  |  |  |         PyObject *module = PyUnicode_FromString(info->type.module); | 
					
						
							|  |  |  |         if (module == NULL) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         int res = PyObject_SetAttrString(ns, "__module__", module); | 
					
						
							|  |  |  |         Py_DECREF(module); | 
					
						
							|  |  |  |         if (res < 0) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         empty = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (empty) { | 
					
						
							|  |  |  |         Py_CLEAR(ns); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ns; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     Py_DECREF(ns); | 
					
						
							|  |  |  |     return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | _PyXI_excinfo_AsObject(_PyXI_excinfo *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *ns = _PyNamespace_New(NULL); | 
					
						
							|  |  |  |     if (ns == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     int res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PyObject *type = _PyXI_excinfo_TypeAsObject(info); | 
					
						
							|  |  |  |     if (type == NULL) { | 
					
						
							|  |  |  |         if (PyErr_Occurred()) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         type = Py_NewRef(Py_None); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     res = PyObject_SetAttrString(ns, "type", type); | 
					
						
							|  |  |  |     Py_DECREF(type); | 
					
						
							|  |  |  |     if (res < 0) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PyObject *msg = info->msg != NULL | 
					
						
							|  |  |  |         ? PyUnicode_FromString(info->msg) | 
					
						
							|  |  |  |         : Py_NewRef(Py_None); | 
					
						
							|  |  |  |     if (msg == NULL) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     res = PyObject_SetAttrString(ns, "msg", msg); | 
					
						
							|  |  |  |     Py_DECREF(msg); | 
					
						
							|  |  |  |     if (res < 0) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     PyObject *formatted = _PyXI_excinfo_format(info); | 
					
						
							|  |  |  |     if (formatted == NULL) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     res = PyObject_SetAttrString(ns, "formatted", formatted); | 
					
						
							|  |  |  |     Py_DECREF(formatted); | 
					
						
							|  |  |  |     if (res < 0) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     if (info->errdisplay != NULL) { | 
					
						
							|  |  |  |         PyObject *tbexc = PyUnicode_FromString(info->errdisplay); | 
					
						
							|  |  |  |         if (tbexc == NULL) { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |             PyErr_Clear(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |             res = PyObject_SetAttrString(ns, "errdisplay", tbexc); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |             Py_DECREF(tbexc); | 
					
						
							|  |  |  |             if (res < 0) { | 
					
						
							|  |  |  |                 goto error; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     return ns; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     Py_DECREF(ns); | 
					
						
							|  |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | /***************************/ | 
					
						
							|  |  |  | /* short-term data sharing */ | 
					
						
							|  |  |  | /***************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* error codes */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(!PyErr_Occurred()); | 
					
						
							|  |  |  |     switch (code) { | 
					
						
							|  |  |  |     case _PyXI_ERR_NO_ERROR:  // fall through
 | 
					
						
							|  |  |  |     case _PyXI_ERR_UNCAUGHT_EXCEPTION: | 
					
						
							|  |  |  |         // There is nothing to apply.
 | 
					
						
							|  |  |  | #ifdef Py_DEBUG
 | 
					
						
							|  |  |  |         Py_UNREACHABLE(); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     case _PyXI_ERR_OTHER: | 
					
						
							|  |  |  |         // XXX msg?
 | 
					
						
							|  |  |  |         PyErr_SetNone(PyExc_RuntimeError); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case _PyXI_ERR_NO_MEMORY: | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case _PyXI_ERR_ALREADY_RUNNING: | 
					
						
							|  |  |  |         assert(interp != NULL); | 
					
						
							|  |  |  |         assert(_PyInterpreterState_IsRunningMain(interp)); | 
					
						
							|  |  |  |         _PyInterpreterState_FailIfRunningMain(interp); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case _PyXI_ERR_MAIN_NS_FAILURE: | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_RuntimeError, | 
					
						
							|  |  |  |                         "failed to get __main__ namespace"); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case _PyXI_ERR_APPLY_NS_FAILURE: | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_RuntimeError, | 
					
						
							|  |  |  |                         "failed to apply namespace to __main__"); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case _PyXI_ERR_NOT_SHAREABLE: | 
					
						
							|  |  |  |         _set_xid_lookup_failure(interp, NULL, NULL); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  | #ifdef Py_DEBUG
 | 
					
						
							|  |  |  |         Py_UNREACHABLE(); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |         PyErr_Format(PyExc_RuntimeError, "unsupported error code %d", code); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert(PyErr_Occurred()); | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* shared exceptions */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char * | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | _PyXI_InitError(_PyXI_error *error, PyObject *excobj, _PyXI_errcode code) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     if (error->interp == NULL) { | 
					
						
							|  |  |  |         error->interp = PyInterpreterState_Get(); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const char *failure = NULL; | 
					
						
							|  |  |  |     if (code == _PyXI_ERR_UNCAUGHT_EXCEPTION) { | 
					
						
							|  |  |  |         // There is an unhandled exception we need to propagate.
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         failure = _PyXI_excinfo_InitFromException(&error->uncaught, excobj); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         if (failure != NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |             // We failed to initialize error->uncaught.
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |             // XXX Print the excobj/traceback?  Emit a warning?
 | 
					
						
							|  |  |  |             // XXX Print the current exception/traceback?
 | 
					
						
							|  |  |  |             if (PyErr_ExceptionMatches(PyExc_MemoryError)) { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |                 error->code = _PyXI_ERR_NO_MEMORY; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |                 error->code = _PyXI_ERR_OTHER; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |             } | 
					
						
							|  |  |  |             PyErr_Clear(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |             error->code = code; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         assert(error->code != _PyXI_ERR_NO_ERROR); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         // There is an error code we need to propagate.
 | 
					
						
							|  |  |  |         assert(excobj == NULL); | 
					
						
							|  |  |  |         assert(code != _PyXI_ERR_NO_ERROR); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         error->code = code; | 
					
						
							|  |  |  |         _PyXI_excinfo_Clear(&error->uncaught); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     return failure; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | PyObject * | 
					
						
							|  |  |  | _PyXI_ApplyError(_PyXI_error *error) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     if (error->code == _PyXI_ERR_UNCAUGHT_EXCEPTION) { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         // Raise an exception that proxies the propagated exception.
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |        return _PyXI_excinfo_AsObject(&error->uncaught); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     else if (error->code == _PyXI_ERR_NOT_SHAREABLE) { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         // Propagate the exception directly.
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         _set_xid_lookup_failure(error->interp, NULL, error->uncaught.msg); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         // Raise an exception corresponding to the code.
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         assert(error->code != _PyXI_ERR_NO_ERROR); | 
					
						
							|  |  |  |         (void)_PyXI_ApplyErrorCode(error->code, error->interp); | 
					
						
							|  |  |  |         if (error->uncaught.type.name != NULL || error->uncaught.msg != NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |             // __context__ will be set to a proxy of the propagated exception.
 | 
					
						
							|  |  |  |             PyObject *exc = PyErr_GetRaisedException(); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |             _PyXI_excinfo_Apply(&error->uncaught, PyExc_RuntimeError); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |             PyObject *exc2 = PyErr_GetRaisedException(); | 
					
						
							|  |  |  |             PyException_SetContext(exc, exc2); | 
					
						
							|  |  |  |             PyErr_SetRaisedException(exc); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert(PyErr_Occurred()); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* shared namespaces */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | /* Shared namespaces are expected to have relatively short lifetimes.
 | 
					
						
							|  |  |  |    This means dealloc of a shared namespace will normally happen "soon". | 
					
						
							|  |  |  |    Namespace items hold cross-interpreter data, which must get released. | 
					
						
							|  |  |  |    If the namespace/items are cleared in a different interpreter than | 
					
						
							|  |  |  |    where the items' cross-interpreter data was set then that will cause | 
					
						
							|  |  |  |    pending calls to be used to release the cross-interpreter data. | 
					
						
							|  |  |  |    The tricky bit is that the pending calls can happen sufficiently | 
					
						
							|  |  |  |    later that the namespace/items might already be deallocated.  This is | 
					
						
							|  |  |  |    a problem if the cross-interpreter data is allocated as part of a | 
					
						
							|  |  |  |    namespace item.  If that's the case then we must ensure the shared | 
					
						
							|  |  |  |    namespace is only cleared/freed *after* that data has been released. */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | typedef struct _sharednsitem { | 
					
						
							|  |  |  |     const char *name; | 
					
						
							|  |  |  |     _PyCrossInterpreterData *data; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     // We could have a "PyCrossInterpreterData _data" field, so it would
 | 
					
						
							|  |  |  |     // be allocated as part of the item and avoid an extra allocation.
 | 
					
						
							|  |  |  |     // However, doing so adds a bunch of complexity because we must
 | 
					
						
							|  |  |  |     // ensure the item isn't freed before a pending call might happen
 | 
					
						
							|  |  |  |     // in a different interpreter to release the XI data.
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } _PyXI_namespace_item; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | _sharednsitem_is_initialized(_PyXI_namespace_item *item) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (item->name != NULL) { | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | _sharednsitem_init(_PyXI_namespace_item *item, PyObject *key) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     item->name = _copy_string_obj_raw(key, NULL); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     if (item->name == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         assert(!_sharednsitem_is_initialized(item)); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     item->data = NULL; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     assert(_sharednsitem_is_initialized(item)); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | _sharednsitem_has_value(_PyXI_namespace_item *item, int64_t *p_interpid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (item->data == NULL) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (p_interpid != NULL) { | 
					
						
							|  |  |  |         *p_interpid = item->data->interpid; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     assert(_sharednsitem_is_initialized(item)); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     assert(item->data == NULL); | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     item->data = PyMem_RawMalloc(sizeof(_PyCrossInterpreterData)); | 
					
						
							|  |  |  |     if (item->data == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     if (_PyObject_GetCrossInterpreterData(value, item->data) != 0) { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         PyMem_RawFree(item->data); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         item->data = NULL; | 
					
						
							|  |  |  |         // The caller may want to propagate PyExc_NotShareableError
 | 
					
						
							|  |  |  |         // if currently switched between interpreters.
 | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | _sharednsitem_clear_value(_PyXI_namespace_item *item) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     _PyCrossInterpreterData *data = item->data; | 
					
						
							|  |  |  |     if (data != NULL) { | 
					
						
							|  |  |  |         item->data = NULL; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         int rawfree = 1; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         (void)_release_xid_data(data, rawfree); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _sharednsitem_clear(_PyXI_namespace_item *item) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (item->name != NULL) { | 
					
						
							|  |  |  |         PyMem_RawFree((void *)item->name); | 
					
						
							|  |  |  |         item->name = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     _sharednsitem_clear_value(item); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _sharednsitem_copy_from_ns(struct _sharednsitem *item, PyObject *ns) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(item->name != NULL); | 
					
						
							|  |  |  |     assert(item->data == NULL); | 
					
						
							|  |  |  |     PyObject *value = PyDict_GetItemString(ns, item->name);  // borrowed
 | 
					
						
							|  |  |  |     if (value == NULL) { | 
					
						
							|  |  |  |         if (PyErr_Occurred()) { | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // When applied, this item will be set to the default (or fail).
 | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (_sharednsitem_set_value(item, value) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _sharednsitem_apply(_PyXI_namespace_item *item, PyObject *ns, PyObject *dflt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *name = PyUnicode_FromString(item->name); | 
					
						
							|  |  |  |     if (name == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *value; | 
					
						
							|  |  |  |     if (item->data != NULL) { | 
					
						
							|  |  |  |         value = _PyCrossInterpreterData_NewObject(item->data); | 
					
						
							|  |  |  |         if (value == NULL) { | 
					
						
							|  |  |  |             Py_DECREF(name); | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         value = Py_NewRef(dflt); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     int res = PyDict_SetItem(ns, name, value); | 
					
						
							|  |  |  |     Py_DECREF(name); | 
					
						
							|  |  |  |     Py_DECREF(value); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct _sharedns { | 
					
						
							|  |  |  |     Py_ssize_t len; | 
					
						
							|  |  |  |     _PyXI_namespace_item *items; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _PyXI_namespace * | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | _sharedns_new(void) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     _PyXI_namespace *ns = PyMem_RawCalloc(sizeof(_PyXI_namespace), 1); | 
					
						
							|  |  |  |     if (ns == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     *ns = (_PyXI_namespace){ 0 }; | 
					
						
							|  |  |  |     return ns; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _sharedns_is_initialized(_PyXI_namespace *ns) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (ns->len == 0) { | 
					
						
							|  |  |  |         assert(ns->items == NULL); | 
					
						
							|  |  |  |         return 0; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     assert(ns->len > 0); | 
					
						
							|  |  |  |     assert(ns->items != NULL); | 
					
						
							|  |  |  |     assert(_sharednsitem_is_initialized(&ns->items[0])); | 
					
						
							|  |  |  |     assert(ns->len == 1 | 
					
						
							|  |  |  |            || _sharednsitem_is_initialized(&ns->items[ns->len - 1])); | 
					
						
							|  |  |  |     return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define HAS_COMPLETE_DATA 1
 | 
					
						
							|  |  |  | #define HAS_PARTIAL_DATA 2
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _sharedns_has_xidata(_PyXI_namespace *ns, int64_t *p_interpid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // We expect _PyXI_namespace to always be initialized.
 | 
					
						
							|  |  |  |     assert(_sharedns_is_initialized(ns)); | 
					
						
							|  |  |  |     int res = 0; | 
					
						
							|  |  |  |     _PyXI_namespace_item *item0 = &ns->items[0]; | 
					
						
							|  |  |  |     if (!_sharednsitem_is_initialized(item0)) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     int64_t interpid0 = -1; | 
					
						
							|  |  |  |     if (!_sharednsitem_has_value(item0, &interpid0)) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (ns->len > 1) { | 
					
						
							|  |  |  |         // At this point we know it is has at least partial data.
 | 
					
						
							|  |  |  |         _PyXI_namespace_item *itemN = &ns->items[ns->len-1]; | 
					
						
							|  |  |  |         if (!_sharednsitem_is_initialized(itemN)) { | 
					
						
							|  |  |  |             res = HAS_PARTIAL_DATA; | 
					
						
							|  |  |  |             goto finally; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         int64_t interpidN = -1; | 
					
						
							|  |  |  |         if (!_sharednsitem_has_value(itemN, &interpidN)) { | 
					
						
							|  |  |  |             res = HAS_PARTIAL_DATA; | 
					
						
							|  |  |  |             goto finally; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         assert(interpidN == interpid0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     res = HAS_COMPLETE_DATA; | 
					
						
							|  |  |  |     *p_interpid = interpid0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | finally: | 
					
						
							|  |  |  |     return res; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | _sharedns_clear(_PyXI_namespace *ns) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     if (!_sharedns_is_initialized(ns)) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // If the cross-interpreter data were allocated as part of
 | 
					
						
							|  |  |  |     // _PyXI_namespace_item (instead of dynamically), this is where
 | 
					
						
							|  |  |  |     // we would need verify that we are clearing the items in the
 | 
					
						
							|  |  |  |     // correct interpreter, to avoid a race with releasing the XI data
 | 
					
						
							|  |  |  |     // via a pending call.  See _sharedns_has_xidata().
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     for (Py_ssize_t i=0; i < ns->len; i++) { | 
					
						
							|  |  |  |         _sharednsitem_clear(&ns->items[i]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyMem_RawFree(ns->items); | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     ns->items = NULL; | 
					
						
							|  |  |  |     ns->len = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _sharedns_free(_PyXI_namespace *ns) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _sharedns_clear(ns); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     PyMem_RawFree(ns); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | _sharedns_init(_PyXI_namespace *ns, PyObject *names) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(!_sharedns_is_initialized(ns)); | 
					
						
							|  |  |  |     assert(names != NULL); | 
					
						
							|  |  |  |     Py_ssize_t len = PyDict_CheckExact(names) | 
					
						
							|  |  |  |         ? PyDict_Size(names) | 
					
						
							|  |  |  |         : PySequence_Size(names); | 
					
						
							|  |  |  |     if (len < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (len == 0) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_ValueError, "empty namespaces not allowed"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert(len > 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Allocate the items.
 | 
					
						
							|  |  |  |     _PyXI_namespace_item *items = | 
					
						
							|  |  |  |             PyMem_RawCalloc(sizeof(struct _sharednsitem), len); | 
					
						
							|  |  |  |     if (items == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Fill in the names.
 | 
					
						
							|  |  |  |     Py_ssize_t i = -1; | 
					
						
							|  |  |  |     if (PyDict_CheckExact(names)) { | 
					
						
							|  |  |  |         Py_ssize_t pos = 0; | 
					
						
							|  |  |  |         for (i=0; i < len; i++) { | 
					
						
							|  |  |  |             PyObject *key; | 
					
						
							|  |  |  |             if (!PyDict_Next(names, &pos, &key, NULL)) { | 
					
						
							|  |  |  |                 // This should not be possible.
 | 
					
						
							|  |  |  |                 assert(0); | 
					
						
							|  |  |  |                 goto error; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (_sharednsitem_init(&items[i], key) < 0) { | 
					
						
							|  |  |  |                 goto error; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (PySequence_Check(names)) { | 
					
						
							|  |  |  |         for (i=0; i < len; i++) { | 
					
						
							|  |  |  |             PyObject *key = PySequence_GetItem(names, i); | 
					
						
							|  |  |  |             if (key == NULL) { | 
					
						
							|  |  |  |                 goto error; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             int res = _sharednsitem_init(&items[i], key); | 
					
						
							|  |  |  |             Py_DECREF(key); | 
					
						
							|  |  |  |             if (res < 0) { | 
					
						
							|  |  |  |                 goto error; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_NotImplementedError, | 
					
						
							|  |  |  |                         "non-sequence namespace not supported"); | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ns->items = items; | 
					
						
							|  |  |  |     ns->len = len; | 
					
						
							|  |  |  |     assert(_sharedns_is_initialized(ns)); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     for (Py_ssize_t j=0; j < i; j++) { | 
					
						
							|  |  |  |         _sharednsitem_clear(&items[j]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyMem_RawFree(items); | 
					
						
							|  |  |  |     assert(!_sharedns_is_initialized(ns)); | 
					
						
							|  |  |  |     return -1; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXI_FreeNamespace(_PyXI_namespace *ns) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     if (!_sharedns_is_initialized(ns)) { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     int64_t interpid = -1; | 
					
						
							|  |  |  |     if (!_sharedns_has_xidata(ns, &interpid)) { | 
					
						
							|  |  |  |         _sharedns_free(ns); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (interpid == PyInterpreterState_GetID(_PyInterpreterState_GET())) { | 
					
						
							|  |  |  |         _sharedns_free(ns); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         // If we weren't always dynamically allocating the cross-interpreter
 | 
					
						
							|  |  |  |         // data in each item then we would need to using a pending call
 | 
					
						
							|  |  |  |         // to call _sharedns_free(), to avoid the race between freeing
 | 
					
						
							|  |  |  |         // the shared namespace and releasing the XI data.
 | 
					
						
							|  |  |  |         _sharedns_free(ns); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _PyXI_namespace * | 
					
						
							|  |  |  | _PyXI_NamespaceFromNames(PyObject *names) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (names == NULL || names == Py_None) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     _PyXI_namespace *ns = _sharedns_new(); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     if (ns == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (_sharedns_init(ns, names) < 0) { | 
					
						
							|  |  |  |         PyMem_RawFree(ns); | 
					
						
							|  |  |  |         if (PySequence_Size(names) == 0) { | 
					
						
							|  |  |  |             PyErr_Clear(); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     return ns; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-03 13:37:10 -06:00
										 |  |  | #ifndef NDEBUG
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | static int _session_is_active(_PyXI_session *); | 
					
						
							| 
									
										
										
										
											2023-11-03 13:37:10 -06:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | static void _propagate_not_shareable_error(_PyXI_session *); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | int | 
					
						
							|  |  |  | _PyXI_FillNamespaceFromDict(_PyXI_namespace *ns, PyObject *nsobj, | 
					
						
							|  |  |  |                             _PyXI_session *session) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // session must be entered already, if provided.
 | 
					
						
							|  |  |  |     assert(session == NULL || _session_is_active(session)); | 
					
						
							|  |  |  |     assert(_sharedns_is_initialized(ns)); | 
					
						
							|  |  |  |     for (Py_ssize_t i=0; i < ns->len; i++) { | 
					
						
							|  |  |  |         _PyXI_namespace_item *item = &ns->items[i]; | 
					
						
							|  |  |  |         if (_sharednsitem_copy_from_ns(item, nsobj) < 0) { | 
					
						
							|  |  |  |             _propagate_not_shareable_error(session); | 
					
						
							|  |  |  |             // Clear out the ones we set so far.
 | 
					
						
							|  |  |  |             for (Py_ssize_t j=0; j < i; j++) { | 
					
						
							|  |  |  |                 _sharednsitem_clear_value(&ns->items[j]); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | // All items are expected to be shareable.
 | 
					
						
							|  |  |  | static _PyXI_namespace * | 
					
						
							|  |  |  | _PyXI_NamespaceFromDict(PyObject *nsobj, _PyXI_session *session) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // session must be entered already, if provided.
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     assert(session == NULL || _session_is_active(session)); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     if (nsobj == NULL || nsobj == Py_None) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!PyDict_CheckExact(nsobj)) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_TypeError, "expected a dict"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     _PyXI_namespace *ns = _sharedns_new(); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     if (ns == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     if (_sharedns_init(ns, nsobj) < 0) { | 
					
						
							|  |  |  |         if (PyDict_Size(nsobj) == 0) { | 
					
						
							|  |  |  |             PyMem_RawFree(ns); | 
					
						
							|  |  |  |             PyErr_Clear(); | 
					
						
							|  |  |  |             return NULL; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         goto error; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (_PyXI_FillNamespaceFromDict(ns, nsobj, session) < 0) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     return ns; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     assert(PyErr_Occurred() | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |            || (session != NULL && session->error_override != NULL)); | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     _sharedns_free(ns); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyXI_ApplyNamespace(_PyXI_namespace *ns, PyObject *nsobj, PyObject *dflt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     for (Py_ssize_t i=0; i < ns->len; i++) { | 
					
						
							|  |  |  |         if (_sharednsitem_apply(&ns->items[i], nsobj, dflt) != 0) { | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**********************/ | 
					
						
							|  |  |  | /* high-level helpers */ | 
					
						
							|  |  |  | /**********************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* enter/exit a cross-interpreter session */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _enter_session(_PyXI_session *session, PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Set here and cleared in _exit_session().
 | 
					
						
							|  |  |  |     assert(!session->own_init_tstate); | 
					
						
							|  |  |  |     assert(session->init_tstate == NULL); | 
					
						
							|  |  |  |     assert(session->prev_tstate == NULL); | 
					
						
							|  |  |  |     // Set elsewhere and cleared in _exit_session().
 | 
					
						
							|  |  |  |     assert(!session->running); | 
					
						
							|  |  |  |     assert(session->main_ns == NULL); | 
					
						
							|  |  |  |     // Set elsewhere and cleared in _capture_current_exception().
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     assert(session->error_override == NULL); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     // Set elsewhere and cleared in _PyXI_ApplyCapturedException().
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     assert(session->error == NULL); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Switch to interpreter.
 | 
					
						
							|  |  |  |     PyThreadState *tstate = PyThreadState_Get(); | 
					
						
							|  |  |  |     PyThreadState *prev = tstate; | 
					
						
							|  |  |  |     if (interp != tstate->interp) { | 
					
						
							|  |  |  |         tstate = PyThreadState_New(interp); | 
					
						
							|  |  |  |         tstate->_whence = _PyThreadState_WHENCE_EXEC; | 
					
						
							|  |  |  |         // XXX Possible GILState issues?
 | 
					
						
							|  |  |  |         session->prev_tstate = PyThreadState_Swap(tstate); | 
					
						
							|  |  |  |         assert(session->prev_tstate == prev); | 
					
						
							|  |  |  |         session->own_init_tstate = 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     session->init_tstate = tstate; | 
					
						
							|  |  |  |     session->prev_tstate = prev; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _exit_session(_PyXI_session *session) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyThreadState *tstate = session->init_tstate; | 
					
						
							|  |  |  |     assert(tstate != NULL); | 
					
						
							|  |  |  |     assert(PyThreadState_Get() == tstate); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Release any of the entered interpreters resources.
 | 
					
						
							|  |  |  |     if (session->main_ns != NULL) { | 
					
						
							|  |  |  |         Py_CLEAR(session->main_ns); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Ensure this thread no longer owns __main__.
 | 
					
						
							|  |  |  |     if (session->running) { | 
					
						
							|  |  |  |         _PyInterpreterState_SetNotRunningMain(tstate->interp); | 
					
						
							|  |  |  |         assert(!PyErr_Occurred()); | 
					
						
							|  |  |  |         session->running = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Switch back.
 | 
					
						
							|  |  |  |     assert(session->prev_tstate != NULL); | 
					
						
							|  |  |  |     if (session->prev_tstate != session->init_tstate) { | 
					
						
							|  |  |  |         assert(session->own_init_tstate); | 
					
						
							|  |  |  |         session->own_init_tstate = 0; | 
					
						
							|  |  |  |         PyThreadState_Clear(tstate); | 
					
						
							|  |  |  |         PyThreadState_Swap(session->prev_tstate); | 
					
						
							|  |  |  |         PyThreadState_Delete(tstate); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         assert(!session->own_init_tstate); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     session->prev_tstate = NULL; | 
					
						
							|  |  |  |     session->init_tstate = NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-03 13:37:10 -06:00
										 |  |  | #ifndef NDEBUG
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | _session_is_active(_PyXI_session *session) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return (session->init_tstate != NULL); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-11-03 13:37:10 -06:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | static void | 
					
						
							|  |  |  | _propagate_not_shareable_error(_PyXI_session *session) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (session == NULL) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyInterpreterState *interp = _PyInterpreterState_GET(); | 
					
						
							|  |  |  |     if (PyErr_ExceptionMatches(_get_not_shareable_error_type(interp))) { | 
					
						
							|  |  |  |         // We want to propagate the exception directly.
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         session->_error_override = _PyXI_ERR_NOT_SHAREABLE; | 
					
						
							|  |  |  |         session->error_override = &session->_error_override; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _capture_current_exception(_PyXI_session *session) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     assert(session->error == NULL); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     if (!PyErr_Occurred()) { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         assert(session->error_override == NULL); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle the exception override.
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     _PyXI_errcode *override = session->error_override; | 
					
						
							|  |  |  |     session->error_override = NULL; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     _PyXI_errcode errcode = override != NULL | 
					
						
							|  |  |  |         ? *override | 
					
						
							|  |  |  |         : _PyXI_ERR_UNCAUGHT_EXCEPTION; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Pop the exception object.
 | 
					
						
							|  |  |  |     PyObject *excval = NULL; | 
					
						
							|  |  |  |     if (errcode == _PyXI_ERR_UNCAUGHT_EXCEPTION) { | 
					
						
							|  |  |  |         // We want to actually capture the current exception.
 | 
					
						
							|  |  |  |         excval = PyErr_GetRaisedException(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (errcode == _PyXI_ERR_ALREADY_RUNNING) { | 
					
						
							|  |  |  |         // We don't need the exception info.
 | 
					
						
							|  |  |  |         PyErr_Clear(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         // We could do a variety of things here, depending on errcode.
 | 
					
						
							|  |  |  |         // However, for now we simply capture the exception and save
 | 
					
						
							|  |  |  |         // the errcode.
 | 
					
						
							|  |  |  |         excval = PyErr_GetRaisedException(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Capture the exception.
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     _PyXI_error *err = &session->_error; | 
					
						
							|  |  |  |     *err = (_PyXI_error){ | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         .interp = session->init_tstate->interp, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     const char *failure; | 
					
						
							|  |  |  |     if (excval == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         failure = _PyXI_InitError(err, NULL, errcode); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         failure = _PyXI_InitError(err, excval, _PyXI_ERR_UNCAUGHT_EXCEPTION); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |         Py_DECREF(excval); | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         if (failure == NULL && override != NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |             err->code = errcode; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle capture failure.
 | 
					
						
							|  |  |  |     if (failure != NULL) { | 
					
						
							|  |  |  |         // XXX Make this error message more generic.
 | 
					
						
							|  |  |  |         fprintf(stderr, | 
					
						
							|  |  |  |                 "RunFailedError: script raised an uncaught exception (%s)", | 
					
						
							|  |  |  |                 failure); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         err = NULL; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Finished!
 | 
					
						
							|  |  |  |     assert(!PyErr_Occurred()); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     session->error  = err; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | PyObject * | 
					
						
							|  |  |  | _PyXI_ApplyCapturedException(_PyXI_session *session) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     assert(!PyErr_Occurred()); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     assert(session->error != NULL); | 
					
						
							|  |  |  |     PyObject *res = _PyXI_ApplyError(session->error); | 
					
						
							|  |  |  |     assert((res == NULL) != (PyErr_Occurred() == NULL)); | 
					
						
							|  |  |  |     session->error = NULL; | 
					
						
							|  |  |  |     return res; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyXI_HasCapturedException(_PyXI_session *session) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     return session->error != NULL; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyXI_Enter(_PyXI_session *session, | 
					
						
							|  |  |  |             PyInterpreterState *interp, PyObject *nsupdates) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Convert the attrs for cross-interpreter use.
 | 
					
						
							|  |  |  |     _PyXI_namespace *sharedns = NULL; | 
					
						
							|  |  |  |     if (nsupdates != NULL) { | 
					
						
							|  |  |  |         sharedns = _PyXI_NamespaceFromDict(nsupdates, NULL); | 
					
						
							|  |  |  |         if (sharedns == NULL && PyErr_Occurred()) { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |             assert(session->error == NULL); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Switch to the requested interpreter (if necessary).
 | 
					
						
							|  |  |  |     _enter_session(session, interp); | 
					
						
							|  |  |  |     _PyXI_errcode errcode = _PyXI_ERR_UNCAUGHT_EXCEPTION; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Ensure this thread owns __main__.
 | 
					
						
							|  |  |  |     if (_PyInterpreterState_SetRunningMain(interp) < 0) { | 
					
						
							|  |  |  |         // In the case where we didn't switch interpreters, it would
 | 
					
						
							|  |  |  |         // be more efficient to leave the exception in place and return
 | 
					
						
							|  |  |  |         // immediately.  However, life is simpler if we don't.
 | 
					
						
							|  |  |  |         errcode = _PyXI_ERR_ALREADY_RUNNING; | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     session->running = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Cache __main__.__dict__.
 | 
					
						
							|  |  |  |     PyObject *main_mod = PyUnstable_InterpreterState_GetMainModule(interp); | 
					
						
							|  |  |  |     if (main_mod == NULL) { | 
					
						
							|  |  |  |         errcode = _PyXI_ERR_MAIN_NS_FAILURE; | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *ns = PyModule_GetDict(main_mod);  // borrowed
 | 
					
						
							|  |  |  |     Py_DECREF(main_mod); | 
					
						
							|  |  |  |     if (ns == NULL) { | 
					
						
							|  |  |  |         errcode = _PyXI_ERR_MAIN_NS_FAILURE; | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     session->main_ns = Py_NewRef(ns); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Apply the cross-interpreter data.
 | 
					
						
							|  |  |  |     if (sharedns != NULL) { | 
					
						
							|  |  |  |         if (_PyXI_ApplyNamespace(sharedns, ns, NULL) < 0) { | 
					
						
							|  |  |  |             errcode = _PyXI_ERR_APPLY_NS_FAILURE; | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         _PyXI_FreeNamespace(sharedns); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     errcode = _PyXI_ERR_NO_ERROR; | 
					
						
							|  |  |  |     assert(!PyErr_Occurred()); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     assert(PyErr_Occurred()); | 
					
						
							|  |  |  |     // We want to propagate all exceptions here directly (best effort).
 | 
					
						
							|  |  |  |     assert(errcode != _PyXI_ERR_UNCAUGHT_EXCEPTION); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     session->error_override = &errcode; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     _capture_current_exception(session); | 
					
						
							|  |  |  |     _exit_session(session); | 
					
						
							|  |  |  |     if (sharedns != NULL) { | 
					
						
							|  |  |  |         _PyXI_FreeNamespace(sharedns); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXI_Exit(_PyXI_session *session) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _capture_current_exception(session); | 
					
						
							|  |  |  |     _exit_session(session); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*********************/ | 
					
						
							|  |  |  | /* runtime lifecycle */ | 
					
						
							|  |  |  | /*********************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyStatus | 
					
						
							|  |  |  | _PyXI_Init(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyStatus status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Initialize the XID registry.
 | 
					
						
							|  |  |  |     if (_Py_IsMainInterpreter(interp)) { | 
					
						
							|  |  |  |         _xidregistry_init(_get_global_xidregistry(interp->runtime)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _xidregistry_init(_get_xidregistry(interp)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Initialize exceptions (heap types).
 | 
					
						
							|  |  |  |     status = _init_not_shareable_error_type(interp); | 
					
						
							|  |  |  |     if (_PyStatus_EXCEPTION(status)) { | 
					
						
							|  |  |  |         return status; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return _PyStatus_OK(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // _PyXI_Fini() must be called before the interpreter is cleared,
 | 
					
						
							|  |  |  | // since we must clear some heap objects.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXI_Fini(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Finalize exceptions (heap types).
 | 
					
						
							|  |  |  |     _fini_not_shareable_error_type(interp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Finalize the XID registry.
 | 
					
						
							|  |  |  |     _xidregistry_fini(_get_xidregistry(interp)); | 
					
						
							|  |  |  |     if (_Py_IsMainInterpreter(interp)) { | 
					
						
							|  |  |  |         _xidregistry_fini(_get_global_xidregistry(interp->runtime)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | PyStatus | 
					
						
							|  |  |  | _PyXI_InitTypes(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (init_exceptions(interp) < 0) { | 
					
						
							|  |  |  |         return _PyStatus_ERR("failed to initialize an exception type"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return _PyStatus_OK(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXI_FiniTypes(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     fini_exceptions(interp); | 
					
						
							|  |  |  | } |