mirror of
https://github.com/python/cpython.git
synced 2025-07-09 20:35:26 +00:00
bpo-45953: Statically allocate the main interpreter (and initial thread state). (gh-29883)
Previously, the main interpreter was allocated on the heap during runtime initialization. Here we instead embed it into _PyRuntimeState, which means it is statically allocated as part of the _PyRuntime global. The same goes for the initial thread state (of each interpreter, including the main one). Consequently there are fewer allocations during runtime/interpreter init, fewer possible failures, and better memory locality. FYI, this also helps efforts to consolidate globals, which in turns helps work on subinterpreter isolation. https://bugs.python.org/issue45953
This commit is contained in:
parent
0bbf30e2b9
commit
ed57b36c32
8 changed files with 115 additions and 34 deletions
|
@ -46,6 +46,10 @@ static PyThreadState *_PyGILState_GetThisThreadState(struct _gilstate_runtime_st
|
|||
static void _PyThreadState_Delete(PyThreadState *tstate, int check_current);
|
||||
|
||||
|
||||
/* We use "initial" if the runtime gets re-used
|
||||
(e.g. Py_Finalize() followed by Py_Initialize(). */
|
||||
static const _PyRuntimeState initial = _PyRuntimeState_INIT;
|
||||
|
||||
static int
|
||||
alloc_for_runtime(PyThread_type_lock *plock1, PyThread_type_lock *plock2,
|
||||
PyThread_type_lock *plock3)
|
||||
|
@ -91,9 +95,12 @@ init_runtime(_PyRuntimeState *runtime,
|
|||
PyThread_type_lock xidregistry_mutex)
|
||||
{
|
||||
if (runtime->_initialized) {
|
||||
_PyRuntimeState_reset(runtime);
|
||||
assert(!runtime->initialized);
|
||||
Py_FatalError("runtime already initialized");
|
||||
}
|
||||
assert(!runtime->preinitializing &&
|
||||
!runtime->preinitialized &&
|
||||
!runtime->core_initialized &&
|
||||
!runtime->initialized);
|
||||
|
||||
runtime->open_code_hook = open_code_hook;
|
||||
runtime->open_code_userdata = open_code_userdata;
|
||||
|
@ -144,6 +151,11 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
|
|||
return _PyStatus_NO_MEMORY();
|
||||
}
|
||||
|
||||
if (runtime->_initialized) {
|
||||
// Py_Initialize() must be running again.
|
||||
// Reset to _PyRuntimeState_INIT.
|
||||
memcpy(runtime, &initial, sizeof(*runtime));
|
||||
}
|
||||
init_runtime(runtime, open_code_hook, open_code_userdata, audit_hook_head,
|
||||
unicode_next_index, lock1, lock2, lock3);
|
||||
|
||||
|
@ -250,13 +262,15 @@ alloc_interpreter(void)
|
|||
static void
|
||||
free_interpreter(PyInterpreterState *interp)
|
||||
{
|
||||
PyMem_RawFree(interp);
|
||||
if (!interp->_static) {
|
||||
PyMem_RawFree(interp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the interpreter state to a minimal consistent state.
|
||||
Further init happens in pylifecycle.c before it can be used.
|
||||
All fields not initialized here are expected to be zeroed out,
|
||||
e.g. by PyMem_RawCalloc() or memset().
|
||||
e.g. by PyMem_RawCalloc() or memset(), or otherwise pre-initialized.
|
||||
The runtime state is not manipulated. Instead it is assumed that
|
||||
the interpreter is getting added to the runtime.
|
||||
*/
|
||||
|
@ -338,23 +352,23 @@ PyInterpreterState_New(void)
|
|||
assert(interpreters->main == NULL);
|
||||
assert(id == 0);
|
||||
|
||||
interp = alloc_interpreter();
|
||||
if (interp == NULL) {
|
||||
goto error;
|
||||
}
|
||||
interp = &runtime->_main_interpreter;
|
||||
assert(interp->id == 0);
|
||||
assert(interp->next == NULL);
|
||||
|
||||
interpreters->main = interp;
|
||||
}
|
||||
else {
|
||||
assert(id != 0);
|
||||
assert(interpreters->main != NULL);
|
||||
assert(id != 0);
|
||||
|
||||
interp = alloc_interpreter();
|
||||
if (interp == NULL) {
|
||||
goto error;
|
||||
}
|
||||
// Set to _PyInterpreterState_INIT.
|
||||
memcpy(interp, &initial._main_interpreter,
|
||||
sizeof(*interp));
|
||||
|
||||
if (id < 0) {
|
||||
/* overflow or Py_Initialize() not called yet! */
|
||||
|
@ -735,13 +749,15 @@ alloc_threadstate(void)
|
|||
static void
|
||||
free_threadstate(PyThreadState *tstate)
|
||||
{
|
||||
PyMem_RawFree(tstate);
|
||||
if (!tstate->_static) {
|
||||
PyMem_RawFree(tstate);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the thread state to a minimal consistent state.
|
||||
Further init happens in pylifecycle.c before it can be used.
|
||||
All fields not initialized here are expected to be zeroed out,
|
||||
e.g. by PyMem_RawCalloc() or memset().
|
||||
e.g. by PyMem_RawCalloc() or memset(), or otherwise pre-initialized.
|
||||
The interpreter state is not manipulated. Instead it is assumed that
|
||||
the thread is getting added to the interpreter.
|
||||
*/
|
||||
|
@ -808,10 +824,7 @@ new_threadstate(PyInterpreterState *interp)
|
|||
// It's the interpreter's initial thread state.
|
||||
assert(id == 1);
|
||||
|
||||
tstate = alloc_threadstate();
|
||||
if (tstate == NULL) {
|
||||
goto error;
|
||||
}
|
||||
tstate = &interp->_initial_thread;
|
||||
}
|
||||
else {
|
||||
// Every valid interpreter must have at least one thread.
|
||||
|
@ -822,6 +835,10 @@ new_threadstate(PyInterpreterState *interp)
|
|||
if (tstate == NULL) {
|
||||
goto error;
|
||||
}
|
||||
// Set to _PyThreadState_INIT.
|
||||
memcpy(tstate,
|
||||
&initial._main_interpreter._initial_thread,
|
||||
sizeof(*tstate));
|
||||
}
|
||||
interp->threads.head = tstate;
|
||||
|
||||
|
@ -1159,7 +1176,7 @@ _PyThreadState_DeleteExcept(_PyRuntimeState *runtime, PyThreadState *tstate)
|
|||
for (p = list; p; p = next) {
|
||||
next = p->next;
|
||||
PyThreadState_Clear(p);
|
||||
PyMem_RawFree(p);
|
||||
free_threadstate(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue