mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	gh-99741: Implement Multi-Phase Init for the _xxsubinterpreters Module (gh-99742)
_xxsubinterpreters is an internal module used for testing. https://github.com/python/cpython/issues/99741
This commit is contained in:
		
							parent
							
								
									51ee0a29e9
								
							
						
					
					
						commit
						530cc9dbb6
					
				
					 6 changed files with 384 additions and 190 deletions
				
			
		
							
								
								
									
										186
									
								
								Python/pystate.c
									
										
									
									
									
								
							
							
						
						
									
										186
									
								
								Python/pystate.c
									
										
									
									
									
								
							|  | @ -1789,30 +1789,78 @@ PyGILState_Release(PyGILState_STATE oldstate) | |||
| 
 | ||||
| /* cross-interpreter data */ | ||||
| 
 | ||||
| crossinterpdatafunc _PyCrossInterpreterData_Lookup(PyObject *); | ||||
| 
 | ||||
| /* This is a separate func from _PyCrossInterpreterData_Lookup in order
 | ||||
|    to keep the registry code separate. */ | ||||
| static crossinterpdatafunc | ||||
| _lookup_getdata(PyObject *obj) | ||||
| static inline void | ||||
| _xidata_init(_PyCrossInterpreterData *data) | ||||
| { | ||||
|     crossinterpdatafunc getdata = _PyCrossInterpreterData_Lookup(obj); | ||||
|     if (getdata == NULL && PyErr_Occurred() == 0) | ||||
|         PyErr_Format(PyExc_ValueError, | ||||
|                      "%S does not support cross-interpreter data", obj); | ||||
|     return getdata; | ||||
|     // If the value is being reused
 | ||||
|     // then _xidata_clear() should have been called already.
 | ||||
|     assert(data->data == NULL); | ||||
|     assert(data->obj == NULL); | ||||
|     *data = (_PyCrossInterpreterData){0}; | ||||
|     data->interp = -1; | ||||
| } | ||||
| 
 | ||||
| static inline void | ||||
| _xidata_clear(_PyCrossInterpreterData *data) | ||||
| { | ||||
|     if (data->free != NULL) { | ||||
|         data->free(data->data); | ||||
|     } | ||||
|     data->data = NULL; | ||||
|     Py_CLEAR(data->obj); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| _PyCrossInterpreterData_Init(_PyCrossInterpreterData *data, | ||||
|                              PyInterpreterState *interp, | ||||
|                              void *shared, PyObject *obj, | ||||
|                              xid_newobjectfunc new_object) | ||||
| { | ||||
|     assert(data != NULL); | ||||
|     assert(new_object != NULL); | ||||
|     _xidata_init(data); | ||||
|     data->data = shared; | ||||
|     if (obj != NULL) { | ||||
|         assert(interp != NULL); | ||||
|         // released in _PyCrossInterpreterData_Clear()
 | ||||
|         data->obj = Py_NewRef(obj); | ||||
|     } | ||||
|     // Ideally every object would know its owning interpreter.
 | ||||
|     // Until then, we have to rely on the caller to identify it
 | ||||
|     // (but we don't need it in all cases).
 | ||||
|     data->interp = (interp != NULL) ? interp->id : -1; | ||||
|     data->new_object = new_object; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| _PyObject_CheckCrossInterpreterData(PyObject *obj) | ||||
| _PyCrossInterpreterData_InitWithSize(_PyCrossInterpreterData *data, | ||||
|                                      PyInterpreterState *interp, | ||||
|                                      const size_t size, PyObject *obj, | ||||
|                                      xid_newobjectfunc new_object) | ||||
| { | ||||
|     crossinterpdatafunc getdata = _lookup_getdata(obj); | ||||
|     if (getdata == NULL) { | ||||
|     assert(size > 0); | ||||
|     // For now we always free the shared data in the same interpreter
 | ||||
|     // where it was allocated, so the interpreter is required.
 | ||||
|     assert(interp != NULL); | ||||
|     _PyCrossInterpreterData_Init(data, interp, NULL, obj, new_object); | ||||
|     data->data = PyMem_Malloc(size); | ||||
|     if (data->data == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|     data->free = PyMem_Free; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| _PyCrossInterpreterData_Clear(PyInterpreterState *interp, | ||||
|                               _PyCrossInterpreterData *data) | ||||
| { | ||||
|     assert(data != NULL); | ||||
|     // This must be called in the owning interpreter.
 | ||||
|     assert(interp == NULL || data->interp == interp->id); | ||||
|     _xidata_clear(data); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| _check_xidata(PyThreadState *tstate, _PyCrossInterpreterData *data) | ||||
| { | ||||
|  | @ -1835,6 +1883,30 @@ _check_xidata(PyThreadState *tstate, _PyCrossInterpreterData *data) | |||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| crossinterpdatafunc _PyCrossInterpreterData_Lookup(PyObject *); | ||||
| 
 | ||||
| /* This is a separate func from _PyCrossInterpreterData_Lookup in order
 | ||||
|    to keep the registry code separate. */ | ||||
| static crossinterpdatafunc | ||||
| _lookup_getdata(PyObject *obj) | ||||
| { | ||||
|     crossinterpdatafunc getdata = _PyCrossInterpreterData_Lookup(obj); | ||||
|     if (getdata == NULL && PyErr_Occurred() == 0) | ||||
|         PyErr_Format(PyExc_ValueError, | ||||
|                      "%S does not support cross-interpreter data", obj); | ||||
|     return getdata; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| _PyObject_CheckCrossInterpreterData(PyObject *obj) | ||||
| { | ||||
|     crossinterpdatafunc getdata = _lookup_getdata(obj); | ||||
|     if (getdata == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data) | ||||
| { | ||||
|  | @ -1847,7 +1919,7 @@ _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data) | |||
| 
 | ||||
|     // Reset data before re-populating.
 | ||||
|     *data = (_PyCrossInterpreterData){0}; | ||||
|     data->free = PyMem_RawFree;  // Set a default that may be overridden.
 | ||||
|     data->interp = -1; | ||||
| 
 | ||||
|     // Call the "getdata" func for the object.
 | ||||
|     Py_INCREF(obj); | ||||
|  | @ -1856,7 +1928,7 @@ _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data) | |||
|         Py_DECREF(obj); | ||||
|         return -1; | ||||
|     } | ||||
|     int res = getdata(obj, data); | ||||
|     int res = getdata(tstate, obj, data); | ||||
|     Py_DECREF(obj); | ||||
|     if (res != 0) { | ||||
|         return -1; | ||||
|  | @ -1872,21 +1944,17 @@ _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data) | |||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| _release_xidata(void *arg) | ||||
| PyObject * | ||||
| _PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *data) | ||||
| { | ||||
|     _PyCrossInterpreterData *data = (_PyCrossInterpreterData *)arg; | ||||
|     if (data->free != NULL) { | ||||
|         data->free(data->data); | ||||
|     } | ||||
|     data->data = NULL; | ||||
|     Py_CLEAR(data->obj); | ||||
|     return data->new_object(data); | ||||
| } | ||||
| 
 | ||||
| typedef void (*releasefunc)(PyInterpreterState *, void *); | ||||
| 
 | ||||
| static void | ||||
| _call_in_interpreter(struct _gilstate_runtime_state *gilstate, | ||||
|                      PyInterpreterState *interp, | ||||
|                      void (*func)(void *), void *arg) | ||||
|                      PyInterpreterState *interp, releasefunc func, void *arg) | ||||
| { | ||||
|     /* We would use Py_AddPendingCall() if it weren't specific to the
 | ||||
|      * main interpreter (see bpo-33608).  In the meantime we take a | ||||
|  | @ -1902,7 +1970,7 @@ _call_in_interpreter(struct _gilstate_runtime_state *gilstate, | |||
| 
 | ||||
|     // XXX Once the GIL is per-interpreter, this should be called with the
 | ||||
|     // calling interpreter's GIL released and the target interpreter's held.
 | ||||
|     func(arg); | ||||
|     func(interp, arg); | ||||
| 
 | ||||
|     // Switch back.
 | ||||
|     if (save_tstate != NULL) { | ||||
|  | @ -1931,16 +1999,11 @@ _PyCrossInterpreterData_Release(_PyCrossInterpreterData *data) | |||
| 
 | ||||
|     // "Release" the data and/or the object.
 | ||||
|     struct _gilstate_runtime_state *gilstate = &_PyRuntime.gilstate; | ||||
|     _call_in_interpreter(gilstate, interp, _release_xidata, data); | ||||
|     _call_in_interpreter(gilstate, interp, | ||||
|                          (releasefunc)_PyCrossInterpreterData_Clear, data); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| PyObject * | ||||
| _PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *data) | ||||
| { | ||||
|     return data->new_object(data); | ||||
| } | ||||
| 
 | ||||
| /* registry of {type -> crossinterpdatafunc} */ | ||||
| 
 | ||||
| /* For now we use a global registry of shareable classes.  An
 | ||||
|  | @ -2091,16 +2154,21 @@ _new_bytes_object(_PyCrossInterpreterData *data) | |||
| } | ||||
| 
 | ||||
| static int | ||||
| _bytes_shared(PyObject *obj, _PyCrossInterpreterData *data) | ||||
| _bytes_shared(PyThreadState *tstate, PyObject *obj, | ||||
|               _PyCrossInterpreterData *data) | ||||
| { | ||||
|     struct _shared_bytes_data *shared = PyMem_NEW(struct _shared_bytes_data, 1); | ||||
|     if (PyBytes_AsStringAndSize(obj, &shared->bytes, &shared->len) < 0) { | ||||
|     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; | ||||
|     } | ||||
|     data->data = (void *)shared; | ||||
|     data->obj = Py_NewRef(obj);  // Will be "released" (decref'ed) when data released.
 | ||||
|     data->new_object = _new_bytes_object; | ||||
|     data->free = PyMem_Free; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -2118,16 +2186,20 @@ _new_str_object(_PyCrossInterpreterData *data) | |||
| } | ||||
| 
 | ||||
| static int | ||||
| _str_shared(PyObject *obj, _PyCrossInterpreterData *data) | ||||
| _str_shared(PyThreadState *tstate, PyObject *obj, | ||||
|             _PyCrossInterpreterData *data) | ||||
| { | ||||
|     struct _shared_str_data *shared = PyMem_NEW(struct _shared_str_data, 1); | ||||
|     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); | ||||
|     data->data = (void *)shared; | ||||
|     data->obj = Py_NewRef(obj);  // Will be "released" (decref'ed) when data released.
 | ||||
|     data->new_object = _new_str_object; | ||||
|     data->free = PyMem_Free; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -2138,7 +2210,8 @@ _new_long_object(_PyCrossInterpreterData *data) | |||
| } | ||||
| 
 | ||||
| static int | ||||
| _long_shared(PyObject *obj, _PyCrossInterpreterData *data) | ||||
| _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 | ||||
|  | @ -2151,10 +2224,9 @@ _long_shared(PyObject *obj, _PyCrossInterpreterData *data) | |||
|         } | ||||
|         return -1; | ||||
|     } | ||||
|     data->data = (void *)value; | ||||
|     data->obj = NULL; | ||||
|     data->new_object = _new_long_object; | ||||
|     data->free = NULL; | ||||
|     _PyCrossInterpreterData_Init(data, tstate->interp, (void *)value, NULL, | ||||
|             _new_long_object); | ||||
|     // data->obj and data->free remain NULL
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -2166,12 +2238,12 @@ _new_none_object(_PyCrossInterpreterData *data) | |||
| } | ||||
| 
 | ||||
| static int | ||||
| _none_shared(PyObject *obj, _PyCrossInterpreterData *data) | ||||
| _none_shared(PyThreadState *tstate, PyObject *obj, | ||||
|              _PyCrossInterpreterData *data) | ||||
| { | ||||
|     data->data = NULL; | ||||
|     // data->obj remains NULL
 | ||||
|     data->new_object = _new_none_object; | ||||
|     data->free = NULL;  // There is nothing to free.
 | ||||
|     _PyCrossInterpreterData_Init(data, tstate->interp, NULL, NULL, | ||||
|             _new_none_object); | ||||
|     // data->data, data->obj and data->free remain NULL
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Eric Snow
						Eric Snow