mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	gh-101659: Isolate "obmalloc" State to Each Interpreter (gh-101660)
This is strictly about moving the "obmalloc" runtime state from `_PyRuntimeState` to `PyInterpreterState`. Doing so improves isolation between interpreters, specifically most of the memory (incl. objects) allocated for each interpreter's use. This is important for a per-interpreter GIL, but such isolation is valuable even without it. FWIW, a per-interpreter obmalloc is the proverbial canary-in-the-coalmine when it comes to the isolation of objects between interpreters. Any object that leaks (unintentionally) to another interpreter is highly likely to cause a crash (on debug builds at least). That's a useful thing to know, relative to interpreter isolation.
This commit is contained in:
		
							parent
							
								
									01be52e42e
								
							
						
					
					
						commit
						df3173d28e
					
				
					 20 changed files with 322 additions and 73 deletions
				
			
		|  | @ -547,11 +547,21 @@ pycore_init_runtime(_PyRuntimeState *runtime, | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void | ||||
| static PyStatus | ||||
| init_interp_settings(PyInterpreterState *interp, const _PyInterpreterConfig *config) | ||||
| { | ||||
|     assert(interp->feature_flags == 0); | ||||
| 
 | ||||
|     if (config->use_main_obmalloc) { | ||||
|         interp->feature_flags |= Py_RTFLAGS_USE_MAIN_OBMALLOC; | ||||
|     } | ||||
|     else if (!config->check_multi_interp_extensions) { | ||||
|         /* The reason: PyModuleDef.m_base.m_copy leaks objects between
 | ||||
|            interpreters. */ | ||||
|         return _PyStatus_ERR("per-interpreter obmalloc does not support " | ||||
|                              "single-phase init extension modules"); | ||||
|     } | ||||
| 
 | ||||
|     if (config->allow_fork) { | ||||
|         interp->feature_flags |= Py_RTFLAGS_FORK; | ||||
|     } | ||||
|  | @ -570,6 +580,8 @@ init_interp_settings(PyInterpreterState *interp, const _PyInterpreterConfig *con | |||
|     if (config->check_multi_interp_extensions) { | ||||
|         interp->feature_flags |= Py_RTFLAGS_MULTI_INTERP_EXTENSIONS; | ||||
|     } | ||||
| 
 | ||||
|     return _PyStatus_OK(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -622,7 +634,10 @@ pycore_create_interpreter(_PyRuntimeState *runtime, | |||
|     } | ||||
| 
 | ||||
|     const _PyInterpreterConfig config = _PyInterpreterConfig_LEGACY_INIT; | ||||
|     init_interp_settings(interp, &config); | ||||
|     status = init_interp_settings(interp, &config); | ||||
|     if (_PyStatus_EXCEPTION(status)) { | ||||
|         return status; | ||||
|     } | ||||
| 
 | ||||
|     PyThreadState *tstate = _PyThreadState_New(interp); | ||||
|     if (tstate == NULL) { | ||||
|  | @ -1668,6 +1683,8 @@ finalize_interp_types(PyInterpreterState *interp) | |||
|     _PyFloat_FiniType(interp); | ||||
|     _PyLong_FiniTypes(interp); | ||||
|     _PyThread_FiniType(interp); | ||||
|     // XXX fini collections module static types (_PyStaticType_Dealloc())
 | ||||
|     // XXX fini IO module static types (_PyStaticType_Dealloc())
 | ||||
|     _PyErr_FiniTypes(interp); | ||||
|     _PyTypes_FiniTypes(interp); | ||||
| 
 | ||||
|  | @ -1936,6 +1953,7 @@ Py_FinalizeEx(void) | |||
|     } | ||||
|     _Py_FinalizeRefTotal(runtime); | ||||
| #endif | ||||
|     _Py_FinalizeAllocatedBlocks(runtime); | ||||
| 
 | ||||
| #ifdef Py_TRACE_REFS | ||||
|     /* Display addresses (& refcnts) of all objects still alive.
 | ||||
|  | @ -2036,7 +2054,10 @@ new_interpreter(PyThreadState **tstate_p, const _PyInterpreterConfig *config) | |||
|         goto error; | ||||
|     } | ||||
| 
 | ||||
|     init_interp_settings(interp, config); | ||||
|     status = init_interp_settings(interp, config); | ||||
|     if (_PyStatus_EXCEPTION(status)) { | ||||
|         goto error; | ||||
|     } | ||||
| 
 | ||||
|     status = init_interp_create_gil(tstate); | ||||
|     if (_PyStatus_EXCEPTION(status)) { | ||||
|  |  | |||
|  | @ -671,6 +671,14 @@ init_interpreter(PyInterpreterState *interp, | |||
|     assert(next != NULL || (interp == runtime->interpreters.main)); | ||||
|     interp->next = next; | ||||
| 
 | ||||
|     /* Initialize obmalloc, but only for subinterpreters,
 | ||||
|        since the main interpreter is initialized statically. */ | ||||
|     if (interp != &runtime->_main_interpreter) { | ||||
|         poolp temp[OBMALLOC_USED_POOLS_SIZE] = \ | ||||
|                 _obmalloc_pools_INIT(interp->obmalloc.pools); | ||||
|         memcpy(&interp->obmalloc.pools.used, temp, sizeof(temp)); | ||||
|     } | ||||
| 
 | ||||
|     _PyEval_InitState(&interp->ceval, pending_lock); | ||||
|     _PyGC_InitState(&interp->gc); | ||||
|     PyConfig_InitPythonConfig(&interp->config); | ||||
|  | @ -941,11 +949,12 @@ PyInterpreterState_Delete(PyInterpreterState *interp) | |||
| 
 | ||||
|     _PyEval_FiniState(&interp->ceval); | ||||
| 
 | ||||
| #ifdef Py_REF_DEBUG | ||||
|     // XXX This call should be done at the end of clear_interpreter(),
 | ||||
|     // XXX These two calls should be done at the end of clear_interpreter(),
 | ||||
|     // but currently some objects get decref'ed after that.
 | ||||
| #ifdef Py_REF_DEBUG | ||||
|     _PyInterpreterState_FinalizeRefTotal(interp); | ||||
| #endif | ||||
|     _PyInterpreterState_FinalizeAllocatedBlocks(interp); | ||||
| 
 | ||||
|     HEAD_LOCK(runtime); | ||||
|     PyInterpreterState **p; | ||||
|  | @ -2320,11 +2329,11 @@ _PyCrossInterpreterData_InitWithSize(_PyCrossInterpreterData *data, | |||
|     // 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); | ||||
|     data->data = PyMem_RawMalloc(size); | ||||
|     if (data->data == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|     data->free = PyMem_Free; | ||||
|     data->free = PyMem_RawFree; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1871,7 +1871,9 @@ static Py_ssize_t | |||
| sys_getallocatedblocks_impl(PyObject *module) | ||||
| /*[clinic end generated code: output=f0c4e873f0b6dcf7 input=dab13ee346a0673e]*/ | ||||
| { | ||||
|     return _Py_GetAllocatedBlocks(); | ||||
|     // It might make sense to return the count
 | ||||
|     // for just the current interpreter.
 | ||||
|     return _Py_GetGlobalAllocatedBlocks(); | ||||
| } | ||||
| 
 | ||||
| /*[clinic input]
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Eric Snow
						Eric Snow