| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | #include "pycore_weakref.h"       // _PyWeakref_GET_REF()
 | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  | typedef struct _dlcontext { | 
					
						
							|  |  |  |     _PyXIData_lookup_t *global; | 
					
						
							|  |  |  |     _PyXIData_lookup_t *local; | 
					
						
							|  |  |  | } dlcontext_t; | 
					
						
							| 
									
										
										
										
											2024-11-11 15:58:46 -07:00
										 |  |  | typedef _PyXIData_registry_t dlregistry_t; | 
					
						
							|  |  |  | typedef _PyXIData_regitem_t dlregitem_t; | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // forward
 | 
					
						
							|  |  |  | static void _xidregistry_init(dlregistry_t *); | 
					
						
							|  |  |  | static void _xidregistry_fini(dlregistry_t *); | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  | static _PyXIData_getdata_t _lookup_getdata_from_registry( | 
					
						
							|  |  |  |                                             dlcontext_t *, PyObject *); | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* used in crossinterp.c */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | xid_lookup_init(_PyXIData_lookup_t *state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _xidregistry_init(&state->registry); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | xid_lookup_fini(_PyXIData_lookup_t *state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _xidregistry_fini(&state->registry); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | get_lookup_context(PyThreadState *tstate, dlcontext_t *res) | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |     _PyXI_global_state_t *global = _PyXI_GET_GLOBAL_STATE(tstate->interp); | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  |     if (global == NULL) { | 
					
						
							|  |  |  |         assert(PyErr_Occurred()); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |     _PyXI_state_t *local = _PyXI_GET_STATE(tstate->interp); | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  |     if (local == NULL) { | 
					
						
							|  |  |  |         assert(PyErr_Occurred()); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *res = (dlcontext_t){ | 
					
						
							|  |  |  |         .global = &global->data_lookup, | 
					
						
							|  |  |  |         .local = &local->data_lookup, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  | static _PyXIData_getdata_t | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  | lookup_getdata(dlcontext_t *ctx, PyObject *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |    /* Cross-interpreter objects are looked up by exact match on the class.
 | 
					
						
							|  |  |  |       We can reassess this policy when we move from a global registry to a | 
					
						
							|  |  |  |       tp_* slot. */ | 
					
						
							|  |  |  |     return _lookup_getdata_from_registry(ctx, obj); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* exported API */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyObject * | 
					
						
							|  |  |  | _PyXIData_GetNotShareableErrorType(PyThreadState *tstate) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *exctype = get_notshareableerror_type(tstate); | 
					
						
							|  |  |  |     assert(exctype != NULL); | 
					
						
							|  |  |  |     return exctype; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXIData_SetNotShareableError(PyThreadState *tstate, const char *msg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *cause = NULL; | 
					
						
							|  |  |  |     set_notshareableerror(tstate, cause, 1, msg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXIData_FormatNotShareableError(PyThreadState *tstate, | 
					
						
							|  |  |  |                                   const char *format, ...) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *cause = NULL; | 
					
						
							|  |  |  |     va_list vargs; | 
					
						
							|  |  |  |     va_start(vargs, format); | 
					
						
							|  |  |  |     format_notshareableerror_v(tstate, cause, 1, format, vargs); | 
					
						
							|  |  |  |     va_end(vargs); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-14 04:08:25 +02:00
										 |  |  | int | 
					
						
							|  |  |  | _PyXI_UnwrapNotShareableError(PyThreadState * tstate, _PyXI_failure *failure) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *exctype = get_notshareableerror_type(tstate); | 
					
						
							|  |  |  |     assert(exctype != NULL); | 
					
						
							|  |  |  |     if (!_PyErr_ExceptionMatches(tstate, exctype)) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *exc = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |     if (failure != NULL) { | 
					
						
							|  |  |  |         _PyXI_errcode code = _PyXI_ERR_NOT_SHAREABLE; | 
					
						
							|  |  |  |         if (_PyXI_InitFailure(failure, code, exc) < 0) { | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *cause = PyException_GetCause(exc); | 
					
						
							|  |  |  |     if (cause != NULL) { | 
					
						
							|  |  |  |         Py_DECREF(exc); | 
					
						
							|  |  |  |         exc = cause; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         assert(PyException_GetContext(exc) == NULL); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _PyErr_SetRaisedException(tstate, exc); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  | _PyXIData_getdata_t | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  | _PyXIData_Lookup(PyThreadState *tstate, PyObject *obj) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |     dlcontext_t ctx; | 
					
						
							|  |  |  |     if (get_lookup_context(tstate, &ctx) < 0) { | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |         return (_PyXIData_getdata_t){0}; | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     return lookup_getdata(&ctx, obj); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /***********************************************/ | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  | /* a registry of {type -> _PyXIData_getdata_t} */ | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | /***********************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  | /* For now we use a global registry of shareable classes.
 | 
					
						
							|  |  |  |    An alternative would be to add a tp_* slot for a class's | 
					
						
							|  |  |  |    _PyXIData_getdata_t.  It would be simpler and more efficient. */ | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* registry lifecycle */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | static void _register_builtins_for_crossinterpreter_data(dlregistry_t *); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | _xidregistry_init(dlregistry_t *registry) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     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); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | static void _xidregistry_clear(dlregistry_t *); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | _xidregistry_fini(dlregistry_t *registry) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     if (!registry->initialized) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     registry->initialized = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _xidregistry_clear(registry); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* registry thread safety */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | _xidregistry_lock(dlregistry_t *registry) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     if (registry->global) { | 
					
						
							|  |  |  |         PyMutex_Lock(®istry->mutex); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // else: Within an interpreter we rely on the GIL instead of a separate lock.
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | _xidregistry_unlock(dlregistry_t *registry) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     if (registry->global) { | 
					
						
							|  |  |  |         PyMutex_Unlock(®istry->mutex); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* accessing the registry */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | static inline dlregistry_t * | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  | _get_xidregistry_for_type(dlcontext_t *ctx, PyTypeObject *cls) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) { | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  |         return &ctx->local->registry; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  |     return &ctx->global->registry; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | static dlregitem_t* _xidregistry_remove_entry(dlregistry_t *, dlregitem_t *); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | static dlregitem_t * | 
					
						
							|  |  |  | _xidregistry_find_type(dlregistry_t *xidregistry, PyTypeObject *cls) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     dlregitem_t *cur = xidregistry->head; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  | static _PyXIData_getdata_t | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  | _lookup_getdata_from_registry(dlcontext_t *ctx, PyObject *obj) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     PyTypeObject *cls = Py_TYPE(obj); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  |     dlregistry_t *xidregistry = _get_xidregistry_for_type(ctx, cls); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     _xidregistry_lock(xidregistry); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls); | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |     _PyXIData_getdata_t getdata = matched != NULL | 
					
						
							|  |  |  |         ? matched->getdata | 
					
						
							|  |  |  |         : (_PyXIData_getdata_t){0}; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     _xidregistry_unlock(xidregistry); | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |     return getdata; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* updating the registry */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | _xidregistry_add_type(dlregistry_t *xidregistry, | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |                       PyTypeObject *cls, _PyXIData_getdata_t getdata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     dlregitem_t *newhead = PyMem_RawMalloc(sizeof(dlregitem_t)); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     if (newhead == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |     assert((getdata.basic == NULL) != (getdata.fallback == NULL)); | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     *newhead = (dlregitem_t){ | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         // 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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | static dlregitem_t * | 
					
						
							|  |  |  | _xidregistry_remove_entry(dlregistry_t *xidregistry, dlregitem_t *entry) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     dlregitem_t *next = entry->next; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | _xidregistry_clear(dlregistry_t *xidregistry) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     dlregitem_t *cur = xidregistry->head; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     xidregistry->head = NULL; | 
					
						
							|  |  |  |     while (cur != NULL) { | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |         dlregitem_t *next = cur->next; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         Py_XDECREF(cur->weakref); | 
					
						
							|  |  |  |         PyMem_RawFree(cur); | 
					
						
							|  |  |  |         cur = next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  | _PyXIData_RegisterClass(PyThreadState *tstate, | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |                         PyTypeObject *cls, _PyXIData_getdata_t getdata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     if (!PyType_Check(cls)) { | 
					
						
							|  |  |  |         PyErr_Format(PyExc_ValueError, "only classes may be registered"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |     if (getdata.basic == NULL && getdata.fallback == NULL) { | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         PyErr_Format(PyExc_ValueError, "missing 'getdata' func"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int res = 0; | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |     dlcontext_t ctx; | 
					
						
							|  |  |  |     if (get_lookup_context(tstate, &ctx) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     dlregistry_t *xidregistry = _get_xidregistry_for_type(&ctx, cls); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     _xidregistry_lock(xidregistry); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     if (matched != NULL) { | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |         assert(matched->getdata.basic == getdata.basic); | 
					
						
							|  |  |  |         assert(matched->getdata.fallback == getdata.fallback); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         matched->refcount += 1; | 
					
						
							|  |  |  |         goto finally; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = _xidregistry_add_type(xidregistry, cls, getdata); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | finally: | 
					
						
							|  |  |  |     _xidregistry_unlock(xidregistry); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  | _PyXIData_UnregisterClass(PyThreadState *tstate, PyTypeObject *cls) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     int res = 0; | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |     dlcontext_t ctx; | 
					
						
							|  |  |  |     if (get_lookup_context(tstate, &ctx) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     dlregistry_t *xidregistry = _get_xidregistry_for_type(&ctx, cls); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     _xidregistry_lock(xidregistry); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     if (matched != NULL) { | 
					
						
							|  |  |  |         assert(matched->refcount > 0); | 
					
						
							|  |  |  |         matched->refcount -= 1; | 
					
						
							|  |  |  |         if (matched->refcount == 0) { | 
					
						
							|  |  |  |             (void)_xidregistry_remove_entry(xidregistry, matched); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         res = 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _xidregistry_unlock(xidregistry); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /********************************************/ | 
					
						
							|  |  |  | /* cross-interpreter data for builtin types */ | 
					
						
							|  |  |  | /********************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // bytes
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-28 12:52:36 -06:00
										 |  |  | int | 
					
						
							|  |  |  | _PyBytes_GetData(PyObject *obj, _PyBytes_data_t *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!PyBytes_Check(obj)) { | 
					
						
							|  |  |  |         PyErr_Format(PyExc_TypeError, "expected bytes, got %R", obj); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     char *bytes; | 
					
						
							|  |  |  |     Py_ssize_t len; | 
					
						
							| 
									
										
										
										
											2025-04-28 12:52:36 -06:00
										 |  |  |     if (PyBytes_AsStringAndSize(obj, &bytes, &len) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *data = (_PyBytes_data_t){ | 
					
						
							|  |  |  |         .bytes = bytes, | 
					
						
							|  |  |  |         .len = len, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-28 12:52:36 -06:00
										 |  |  | PyObject * | 
					
						
							|  |  |  | _PyBytes_FromData(_PyBytes_data_t *data) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 12:52:36 -06:00
										 |  |  |     return PyBytes_FromStringAndSize(data->bytes, data->len); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyObject * | 
					
						
							|  |  |  | _PyBytes_FromXIData(_PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyBytes_data_t *data = (_PyBytes_data_t *)xidata->data; | 
					
						
							|  |  |  |     assert(_PyXIData_OBJ(xidata) != NULL | 
					
						
							|  |  |  |             && PyBytes_Check(_PyXIData_OBJ(xidata))); | 
					
						
							|  |  |  |     return _PyBytes_FromData(data); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-04-28 12:52:36 -06:00
										 |  |  | _bytes_shared(PyThreadState *tstate, | 
					
						
							|  |  |  |               PyObject *obj, size_t size, xid_newobjfunc newfunc, | 
					
						
							|  |  |  |               _PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 12:52:36 -06:00
										 |  |  |     assert(size >= sizeof(_PyBytes_data_t)); | 
					
						
							|  |  |  |     assert(newfunc != NULL); | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     if (_PyXIData_InitWithSize( | 
					
						
							| 
									
										
										
										
											2025-04-28 12:52:36 -06:00
										 |  |  |                         xidata, tstate->interp, size, obj, newfunc) < 0) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-28 12:52:36 -06:00
										 |  |  |     _PyBytes_data_t *data = (_PyBytes_data_t *)xidata->data; | 
					
						
							|  |  |  |     if (_PyBytes_GetData(obj, data) < 0) { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         _PyXIData_Clear(tstate->interp, xidata); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-28 12:52:36 -06:00
										 |  |  | int | 
					
						
							|  |  |  | _PyBytes_GetXIData(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!PyBytes_Check(obj)) { | 
					
						
							|  |  |  |         PyErr_Format(PyExc_TypeError, "expected bytes, got %R", obj); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     size_t size = sizeof(_PyBytes_data_t); | 
					
						
							|  |  |  |     return _bytes_shared(tstate, obj, size, _PyBytes_FromXIData, xidata); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _PyBytes_data_t * | 
					
						
							|  |  |  | _PyBytes_GetXIDataWrapped(PyThreadState *tstate, | 
					
						
							|  |  |  |                           PyObject *obj, size_t size, xid_newobjfunc newfunc, | 
					
						
							|  |  |  |                           _PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!PyBytes_Check(obj)) { | 
					
						
							|  |  |  |         PyErr_Format(PyExc_TypeError, "expected bytes, got %R", obj); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (size < sizeof(_PyBytes_data_t)) { | 
					
						
							|  |  |  |         PyErr_Format(PyExc_ValueError, "expected size >= %d, got %d", | 
					
						
							|  |  |  |                      sizeof(_PyBytes_data_t), size); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (newfunc == NULL) { | 
					
						
							|  |  |  |         if (size == sizeof(_PyBytes_data_t)) { | 
					
						
							|  |  |  |             PyErr_SetString(PyExc_ValueError, "missing new_object func"); | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         newfunc = _PyBytes_FromXIData; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (_bytes_shared(tstate, obj, size, newfunc, xidata) < 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return (_PyBytes_data_t *)xidata->data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | // str
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct _shared_str_data { | 
					
						
							|  |  |  |     int kind; | 
					
						
							|  |  |  |     const void *buffer; | 
					
						
							|  |  |  |     Py_ssize_t len; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _new_str_object(_PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     struct _shared_str_data *shared = (struct _shared_str_data *)(xidata->data); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     return PyUnicode_FromKindAndData(shared->kind, shared->buffer, shared->len); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _str_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     if (_PyXIData_InitWithSize( | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |             xidata, tstate->interp, sizeof(struct _shared_str_data), obj, | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |             _new_str_object | 
					
						
							|  |  |  |             ) < 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     struct _shared_str_data *shared = (struct _shared_str_data *)xidata->data; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     shared->kind = PyUnicode_KIND(obj); | 
					
						
							|  |  |  |     shared->buffer = PyUnicode_DATA(obj); | 
					
						
							|  |  |  |     shared->len = PyUnicode_GET_LENGTH(obj); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // int
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _new_long_object(_PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     return PyLong_FromSsize_t((Py_ssize_t)(xidata->data)); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _long_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     /* 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; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     _PyXIData_Init(xidata, tstate->interp, (void *)value, NULL, _new_long_object); | 
					
						
							|  |  |  |     // xidata->obj and xidata->free remain NULL
 | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // float
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _new_float_object(_PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     double * value_ptr = xidata->data; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     return PyFloat_FromDouble(*value_ptr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _float_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     if (_PyXIData_InitWithSize( | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |             xidata, tstate->interp, sizeof(double), NULL, | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |             _new_float_object | 
					
						
							|  |  |  |             ) < 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     double *shared = (double *)xidata->data; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     *shared = PyFloat_AsDouble(obj); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // None
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _new_none_object(_PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     // XXX Singleton refcounts are problematic across interpreters...
 | 
					
						
							|  |  |  |     return Py_NewRef(Py_None); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _none_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     _PyXIData_Init(xidata, tstate->interp, NULL, NULL, _new_none_object); | 
					
						
							|  |  |  |     // xidata->data, xidata->obj and xidata->free remain NULL
 | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // bool
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _new_bool_object(_PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     if (xidata->data){ | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         Py_RETURN_TRUE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_RETURN_FALSE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _bool_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     _PyXIData_Init(xidata, tstate->interp, | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |             (void *) (Py_IsTrue(obj) ? (uintptr_t) 1 : (uintptr_t) 0), NULL, | 
					
						
							|  |  |  |             _new_bool_object); | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     // xidata->obj and xidata->free remain NULL
 | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // tuple
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct _shared_tuple_data { | 
					
						
							|  |  |  |     Py_ssize_t len; | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     _PyXIData_t **items; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _new_tuple_object(_PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(xidata->data); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     PyObject *tuple = PyTuple_New(shared->len); | 
					
						
							|  |  |  |     if (tuple == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (Py_ssize_t i = 0; i < shared->len; i++) { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         PyObject *item = _PyXIData_NewObject(shared->items[i]); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         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++) { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         if (shared->items[i] != NULL) { | 
					
						
							|  |  |  |             assert(_PyXIData_INTERPID(shared->items[i]) == interpid); | 
					
						
							|  |  |  |             _PyXIData_Release(shared->items[i]); | 
					
						
							|  |  |  |             PyMem_RawFree(shared->items[i]); | 
					
						
							|  |  |  |             shared->items[i] = NULL; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     PyMem_Free(shared->items); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     PyMem_RawFree(shared); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  | _tuple_shared(PyThreadState *tstate, PyObject *obj, xidata_fallback_t fallback, | 
					
						
							|  |  |  |               _PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     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; | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     shared->items = (_PyXIData_t **) PyMem_Calloc(shared->len, sizeof(_PyXIData_t *)); | 
					
						
							|  |  |  |     if (shared->items == NULL) { | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (Py_ssize_t i = 0; i < shared->len; i++) { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         _PyXIData_t *xidata_i = _PyXIData_New(); | 
					
						
							|  |  |  |         if (xidata_i == NULL) { | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |             goto error;  // PyErr_NoMemory already set
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         PyObject *item = PyTuple_GET_ITEM(obj, i); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         int res = -1; | 
					
						
							|  |  |  |         if (!_Py_EnterRecursiveCallTstate(tstate, " while sharing a tuple")) { | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |             res = _PyObject_GetXIData(tstate, item, fallback, xidata_i); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |             _Py_LeaveRecursiveCallTstate(tstate); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (res < 0) { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |             PyMem_RawFree(xidata_i); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         shared->items[i] = xidata_i; | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     _PyXIData_Init(xidata, tstate->interp, shared, obj, _new_tuple_object); | 
					
						
							|  |  |  |     _PyXIData_SET_FREE(xidata, _tuple_shared_free); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     _tuple_shared_free(shared); | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-05 17:46:03 -06:00
										 |  |  | // code
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyObject * | 
					
						
							|  |  |  | _PyCode_FromXIData(_PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return _PyMarshal_ReadObjectFromXIData(xidata); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyCode_GetXIData(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!PyCode_Check(obj)) { | 
					
						
							|  |  |  |         _PyXIData_FormatNotShareableError(tstate, "expected code, got %R", obj); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (_PyMarshal_GetXIData(tstate, obj, xidata) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert(_PyXIData_CHECK_NEW_OBJECT(xidata, _PyMarshal_ReadObjectFromXIData)); | 
					
						
							|  |  |  |     _PyXIData_SET_NEW_OBJECT(xidata, _PyCode_FromXIData); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-13 00:37:55 +02:00
										 |  |  | // function
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyObject * | 
					
						
							|  |  |  | _PyFunction_FromXIData(_PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // For now "stateless" functions are the only ones we must accommodate.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PyObject *code = _PyMarshal_ReadObjectFromXIData(xidata); | 
					
						
							|  |  |  |     if (code == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // Create a new function.
 | 
					
						
							| 
									
										
										
										
											2025-06-17 01:59:30 +02:00
										 |  |  |     // For stateless functions (no globals) we use __main__ as __globals__,
 | 
					
						
							|  |  |  |     // just like we do for builtins like exec().
 | 
					
						
							| 
									
										
										
										
											2025-05-13 00:37:55 +02:00
										 |  |  |     assert(PyCode_Check(code)); | 
					
						
							| 
									
										
										
										
											2025-06-17 01:59:30 +02:00
										 |  |  |     PyThreadState *tstate = _PyThreadState_GET(); | 
					
						
							|  |  |  |     PyObject *globals = _PyEval_GetGlobalsFromRunningMain(tstate);  // borrowed
 | 
					
						
							| 
									
										
										
										
											2025-05-13 00:37:55 +02:00
										 |  |  |     if (globals == NULL) { | 
					
						
							| 
									
										
										
										
											2025-06-17 01:59:30 +02:00
										 |  |  |         if (_PyErr_Occurred(tstate)) { | 
					
						
							|  |  |  |             Py_DECREF(code); | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         globals = PyDict_New(); | 
					
						
							|  |  |  |         if (globals == NULL) { | 
					
						
							|  |  |  |             Py_DECREF(code); | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-05-13 00:37:55 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-17 01:59:30 +02:00
										 |  |  |     else { | 
					
						
							|  |  |  |         Py_INCREF(globals); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (_PyEval_EnsureBuiltins(tstate, globals, NULL) < 0) { | 
					
						
							| 
									
										
										
										
											2025-05-27 18:07:44 +02:00
										 |  |  |         Py_DECREF(code); | 
					
						
							|  |  |  |         Py_DECREF(globals); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-13 00:37:55 +02:00
										 |  |  |     PyObject *func = PyFunction_New(code, globals); | 
					
						
							|  |  |  |     Py_DECREF(code); | 
					
						
							|  |  |  |     Py_DECREF(globals); | 
					
						
							|  |  |  |     return func; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyFunction_GetXIData(PyThreadState *tstate, PyObject *func, | 
					
						
							|  |  |  |                       _PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!PyFunction_Check(func)) { | 
					
						
							|  |  |  |         const char *msg = "expected a function, got %R"; | 
					
						
							|  |  |  |         format_notshareableerror(tstate, NULL, 0, msg, func); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (_PyFunction_VerifyStateless(tstate, func) < 0) { | 
					
						
							|  |  |  |         PyObject *cause = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |         assert(cause != NULL); | 
					
						
							|  |  |  |         const char *msg = "only stateless functions are shareable"; | 
					
						
							|  |  |  |         set_notshareableerror(tstate, cause, 0, msg); | 
					
						
							|  |  |  |         Py_DECREF(cause); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *code = PyFunction_GET_CODE(func); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Ideally code objects would be immortal and directly shareable.
 | 
					
						
							|  |  |  |     // In the meantime, we use marshal.
 | 
					
						
							|  |  |  |     if (_PyMarshal_GetXIData(tstate, code, xidata) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // Replace _PyMarshal_ReadObjectFromXIData.
 | 
					
						
							|  |  |  |     // (_PyFunction_FromXIData() will call it.)
 | 
					
						
							|  |  |  |     _PyXIData_SET_NEW_OBJECT(xidata, _PyFunction_FromXIData); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-05 17:46:03 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | // registration
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | _register_builtins_for_crossinterpreter_data(dlregistry_t *xidregistry) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  | #define REGISTER(TYPE, GETDATA) \
 | 
					
						
							|  |  |  |     _xidregistry_add_type(xidregistry, (PyTypeObject *)TYPE, \ | 
					
						
							|  |  |  |                           ((_PyXIData_getdata_t){.basic=(GETDATA)})) | 
					
						
							|  |  |  | #define REGISTER_FALLBACK(TYPE, GETDATA) \
 | 
					
						
							|  |  |  |     _xidregistry_add_type(xidregistry, (PyTypeObject *)TYPE, \ | 
					
						
							|  |  |  |                           ((_PyXIData_getdata_t){.fallback=(GETDATA)})) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     // None
 | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |     if (REGISTER(Py_TYPE(Py_None), _none_shared) != 0) { | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         Py_FatalError("could not register None for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // int
 | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |     if (REGISTER(&PyLong_Type, _long_shared) != 0) { | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         Py_FatalError("could not register int for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // bytes
 | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |     if (REGISTER(&PyBytes_Type, _PyBytes_GetXIData) != 0) { | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         Py_FatalError("could not register bytes for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // str
 | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |     if (REGISTER(&PyUnicode_Type, _str_shared) != 0) { | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         Py_FatalError("could not register str for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // bool
 | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |     if (REGISTER(&PyBool_Type, _bool_shared) != 0) { | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         Py_FatalError("could not register bool for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // float
 | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |     if (REGISTER(&PyFloat_Type, _float_shared) != 0) { | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         Py_FatalError("could not register float for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // tuple
 | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  |     if (REGISTER_FALLBACK(&PyTuple_Type, _tuple_shared) != 0) { | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         Py_FatalError("could not register tuple for cross-interpreter sharing"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-13 00:37:55 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // For now, we do not register PyCode_Type or PyFunction_Type.
 | 
					
						
							| 
									
										
										
										
											2025-05-21 16:47:56 +02:00
										 |  |  | #undef REGISTER
 | 
					
						
							|  |  |  | #undef REGISTER_FALLBACK
 | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | } |