mirror of
				https://github.com/python/cpython.git
				synced 2025-10-26 11:14:33 +00:00 
			
		
		
		
	
		
			
	
	
		
			595 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			595 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | 
 | ||
|  | static crossinterpdatafunc _lookup_getdata_from_registry( | ||
|  |                                             PyInterpreterState *, PyObject *); | ||
|  | 
 | ||
|  | static crossinterpdatafunc | ||
|  | 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); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /***********************************************/ | ||
|  | /* a 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.  */ | ||
|  | 
 | ||
|  | 
 | ||
|  | /* registry lifecycle */ | ||
|  | 
 | ||
|  | static void _register_builtins_for_crossinterpreter_data(struct _xidregistry *); | ||
|  | 
 | ||
|  | 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_clear(struct _xidregistry *); | ||
|  | 
 | ||
|  | static void | ||
|  | _xidregistry_fini(struct _xidregistry *registry) | ||
|  | { | ||
|  |     if (!registry->initialized) { | ||
|  |         return; | ||
|  |     } | ||
|  |     registry->initialized = 0; | ||
|  | 
 | ||
|  |     _xidregistry_clear(registry); | ||
|  | } | ||
|  | 
 | ||
|  | static inline struct _xidregistry * _get_global_xidregistry(_PyRuntimeState *); | ||
|  | static inline struct _xidregistry * _get_xidregistry(PyInterpreterState *); | ||
|  | 
 | ||
|  | static void | ||
|  | xid_lookup_init(PyInterpreterState *interp) | ||
|  | { | ||
|  |     if (_Py_IsMainInterpreter(interp)) { | ||
|  |         _xidregistry_init(_get_global_xidregistry(interp->runtime)); | ||
|  |     } | ||
|  |     _xidregistry_init(_get_xidregistry(interp)); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | xid_lookup_fini(PyInterpreterState *interp) | ||
|  | { | ||
|  |     _xidregistry_fini(_get_xidregistry(interp)); | ||
|  |     if (_Py_IsMainInterpreter(interp)) { | ||
|  |         _xidregistry_fini(_get_global_xidregistry(interp->runtime)); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* registry thread safety */ | ||
|  | 
 | ||
|  | static void | ||
|  | _xidregistry_lock(struct _xidregistry *registry) | ||
|  | { | ||
|  |     if (registry->global) { | ||
|  |         PyMutex_Lock(®istry->mutex); | ||
|  |     } | ||
|  |     // else: Within an interpreter we rely on the GIL instead of a separate lock.
 | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | _xidregistry_unlock(struct _xidregistry *registry) | ||
|  | { | ||
|  |     if (registry->global) { | ||
|  |         PyMutex_Unlock(®istry->mutex); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* accessing the registry */ | ||
|  | 
 | ||
|  | 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; | ||
|  | } | ||
|  | 
 | ||
|  | static struct _xidregitem * _xidregistry_remove_entry( | ||
|  |         struct _xidregistry *, struct _xidregitem *); | ||
|  | 
 | ||
|  | 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; | ||
|  | } | ||
|  | 
 | ||
|  | static crossinterpdatafunc | ||
|  | _lookup_getdata_from_registry(PyInterpreterState *interp, PyObject *obj) | ||
|  | { | ||
|  |     PyTypeObject *cls = Py_TYPE(obj); | ||
|  | 
 | ||
|  |     struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls); | ||
|  |     _xidregistry_lock(xidregistry); | ||
|  | 
 | ||
|  |     struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls); | ||
|  |     crossinterpdatafunc func = matched != NULL ? matched->getdata : NULL; | ||
|  | 
 | ||
|  |     _xidregistry_unlock(xidregistry); | ||
|  |     return func; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* updating the registry */ | ||
|  | 
 | ||
|  | 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; | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | _xidregistry_clear(struct _xidregistry *xidregistry) | ||
|  | { | ||
|  |     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; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | 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(); | ||
|  |     struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls); | ||
|  |     _xidregistry_lock(xidregistry); | ||
|  | 
 | ||
|  |     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: | ||
|  |     _xidregistry_unlock(xidregistry); | ||
|  |     return res; | ||
|  | } | ||
|  | 
 | ||
|  | int | ||
|  | _PyCrossInterpreterData_UnregisterClass(PyTypeObject *cls) | ||
|  | { | ||
|  |     int res = 0; | ||
|  |     PyInterpreterState *interp = _PyInterpreterState_GET(); | ||
|  |     struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls); | ||
|  |     _xidregistry_lock(xidregistry); | ||
|  | 
 | ||
|  |     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; | ||
|  |     } | ||
|  | 
 | ||
|  |     _xidregistry_unlock(xidregistry); | ||
|  |     return res; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /********************************************/ | ||
|  | /* cross-interpreter data for builtin types */ | ||
|  | /********************************************/ | ||
|  | 
 | ||
|  | // bytes
 | ||
|  | 
 | ||
|  | 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; | ||
|  | } | ||
|  | 
 | ||
|  | // str
 | ||
|  | 
 | ||
|  | 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; | ||
|  | } | ||
|  | 
 | ||
|  | // int
 | ||
|  | 
 | ||
|  | 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; | ||
|  | } | ||
|  | 
 | ||
|  | // float
 | ||
|  | 
 | ||
|  | 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; | ||
|  | } | ||
|  | 
 | ||
|  | // None
 | ||
|  | 
 | ||
|  | 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; | ||
|  | } | ||
|  | 
 | ||
|  | // bool
 | ||
|  | 
 | ||
|  | 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; | ||
|  | } | ||
|  | 
 | ||
|  | // tuple
 | ||
|  | 
 | ||
|  | 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(_PyCrossInterpreterData_INTERPID(shared->data[i]) == 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; | ||
|  | } | ||
|  | 
 | ||
|  | // registration
 | ||
|  | 
 | ||
|  | 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"); | ||
|  |     } | ||
|  | 
 | ||
|  |     // bool
 | ||
|  |     if (_xidregistry_add_type(xidregistry, &PyBool_Type, _bool_shared) != 0) { | ||
|  |         Py_FatalError("could not register bool for cross-interpreter sharing"); | ||
|  |     } | ||
|  | 
 | ||
|  |     // float
 | ||
|  |     if (_xidregistry_add_type(xidregistry, &PyFloat_Type, _float_shared) != 0) { | ||
|  |         Py_FatalError("could not register float for cross-interpreter sharing"); | ||
|  |     } | ||
|  | 
 | ||
|  |     // tuple
 | ||
|  |     if (_xidregistry_add_type(xidregistry, &PyTuple_Type, _tuple_shared) != 0) { | ||
|  |         Py_FatalError("could not register tuple for cross-interpreter sharing"); | ||
|  |     } | ||
|  | } |