gh-108634: PyInterpreterState_New() no longer calls Py_FatalError() (#108748)

pycore_create_interpreter() now returns a status, rather than
calling Py_FatalError().

* PyInterpreterState_New() now calls Py_ExitStatusException() instead
  of calling Py_FatalError() directly.
* Replace Py_FatalError() with PyStatus in init_interpreter() and
  _PyObject_InitState().
* _PyErr_SetFromPyStatus() now raises RuntimeError, instead of
  ValueError. It can now call PyErr_NoMemory(), raise MemoryError,
  if it detects _PyStatus_NO_MEMORY() error message.
This commit is contained in:
Victor Stinner 2023-09-01 12:43:30 +02:00 committed by GitHub
parent 844f4c2e12
commit b936cf4fe0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 101 additions and 53 deletions

View file

@ -22,7 +22,7 @@ struct pyruntimestate;
#endif #endif
#define _PyStatus_OK() \ #define _PyStatus_OK() \
(PyStatus){._type = _PyStatus_TYPE_OK,} (PyStatus){._type = _PyStatus_TYPE_OK}
/* other fields are set to 0 */ /* other fields are set to 0 */
#define _PyStatus_ERR(ERR_MSG) \ #define _PyStatus_ERR(ERR_MSG) \
(PyStatus){ \ (PyStatus){ \
@ -30,7 +30,8 @@ struct pyruntimestate;
.func = _PyStatus_GET_FUNC(), \ .func = _PyStatus_GET_FUNC(), \
.err_msg = (ERR_MSG)} .err_msg = (ERR_MSG)}
/* other fields are set to 0 */ /* other fields are set to 0 */
#define _PyStatus_NO_MEMORY() _PyStatus_ERR("memory allocation failed") #define _PyStatus_NO_MEMORY_ERRMSG "memory allocation failed"
#define _PyStatus_NO_MEMORY() _PyStatus_ERR(_PyStatus_NO_MEMORY_ERRMSG)
#define _PyStatus_EXIT(EXITCODE) \ #define _PyStatus_EXIT(EXITCODE) \
(PyStatus){ \ (PyStatus){ \
._type = _PyStatus_TYPE_EXIT, \ ._type = _PyStatus_TYPE_EXIT, \
@ -45,7 +46,7 @@ struct pyruntimestate;
do { (err).func = _PyStatus_GET_FUNC(); } while (0) do { (err).func = _PyStatus_GET_FUNC(); } while (0)
// Export for '_testinternalcapi' shared extension // Export for '_testinternalcapi' shared extension
PyAPI_FUNC(PyObject *) _PyErr_SetFromPyStatus(PyStatus status); PyAPI_FUNC(void) _PyErr_SetFromPyStatus(PyStatus status);
/* --- PyWideStringList ------------------------------------------------ */ /* --- PyWideStringList ------------------------------------------------ */

View file

@ -311,6 +311,10 @@ might not be allowed in the current interpreter (i.e. os.fork() would fail).
extern int _PyInterpreterState_HasFeature(PyInterpreterState *interp, extern int _PyInterpreterState_HasFeature(PyInterpreterState *interp,
unsigned long feature); unsigned long feature);
PyAPI_FUNC(PyStatus) _PyInterpreterState_New(
PyThreadState *tstate,
PyInterpreterState **pinterp);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -182,7 +182,7 @@ _PyType_HasFeature(PyTypeObject *type, unsigned long feature) {
extern void _PyType_InitCache(PyInterpreterState *interp); extern void _PyType_InitCache(PyInterpreterState *interp);
extern void _PyObject_InitState(PyInterpreterState *interp); extern PyStatus _PyObject_InitState(PyInterpreterState *interp);
extern void _PyObject_FiniState(PyInterpreterState *interp); extern void _PyObject_FiniState(PyInterpreterState *interp);
extern bool _PyRefchain_IsTraced(PyInterpreterState *interp, PyObject *obj); extern bool _PyRefchain_IsTraced(PyInterpreterState *interp, PyObject *obj);

View file

@ -2034,7 +2034,7 @@ PyObject _Py_NotImplementedStruct = {
}; };
void PyStatus
_PyObject_InitState(PyInterpreterState *interp) _PyObject_InitState(PyInterpreterState *interp)
{ {
#ifdef Py_TRACE_REFS #ifdef Py_TRACE_REFS
@ -2048,9 +2048,10 @@ _PyObject_InitState(PyInterpreterState *interp)
_Py_hashtable_hash_ptr, _Py_hashtable_compare_direct, _Py_hashtable_hash_ptr, _Py_hashtable_compare_direct,
NULL, NULL, &alloc); NULL, NULL, &alloc);
if (REFCHAIN(interp) == NULL) { if (REFCHAIN(interp) == NULL) {
Py_FatalError("_PyObject_InitState() memory allocation failure"); return _PyStatus_NO_MEMORY();
} }
#endif #endif
return _PyStatus_OK();
} }
void void

View file

@ -335,21 +335,34 @@ int PyStatus_IsExit(PyStatus status)
int PyStatus_Exception(PyStatus status) int PyStatus_Exception(PyStatus status)
{ return _PyStatus_EXCEPTION(status); } { return _PyStatus_EXCEPTION(status); }
PyObject* void
_PyErr_SetFromPyStatus(PyStatus status) _PyErr_SetFromPyStatus(PyStatus status)
{ {
if (!_PyStatus_IS_ERROR(status)) { if (!_PyStatus_IS_ERROR(status)) {
PyErr_Format(PyExc_SystemError, PyErr_Format(PyExc_SystemError,
"%s() expects an error PyStatus", "_PyErr_SetFromPyStatus() status is not an error");
_PyStatus_GET_FUNC()); return;
} }
else if (status.func) {
PyErr_Format(PyExc_ValueError, "%s: %s", status.func, status.err_msg); const char *err_msg = status.err_msg;
if (err_msg == NULL || strlen(err_msg) == 0) {
PyErr_Format(PyExc_SystemError,
"_PyErr_SetFromPyStatus() status has no error message");
return;
}
if (strcmp(err_msg, _PyStatus_NO_MEMORY_ERRMSG) == 0) {
PyErr_NoMemory();
return;
}
const char *func = status.func;
if (func) {
PyErr_Format(PyExc_RuntimeError, "%s: %s", func, err_msg);
} }
else { else {
PyErr_Format(PyExc_ValueError, "%s", status.err_msg); PyErr_Format(PyExc_RuntimeError, "%s", err_msg);
} }
return NULL;
} }

View file

@ -629,10 +629,12 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
PyThreadState **tstate_p) PyThreadState **tstate_p)
{ {
PyStatus status; PyStatus status;
PyInterpreterState *interp = PyInterpreterState_New(); PyInterpreterState *interp;
if (interp == NULL) { status = _PyInterpreterState_New(NULL, &interp);
return _PyStatus_ERR("can't make main interpreter"); if (_PyStatus_EXCEPTION(status)) {
return status;
} }
assert(interp != NULL);
assert(_Py_IsMainInterpreter(interp)); assert(_Py_IsMainInterpreter(interp));
status = _PyConfig_Copy(&interp->config, src_config); status = _PyConfig_Copy(&interp->config, src_config);

View file

@ -426,13 +426,11 @@ init_runtime(_PyRuntimeState *runtime,
Py_ssize_t unicode_next_index, Py_ssize_t unicode_next_index,
PyThread_type_lock locks[NUMLOCKS]) PyThread_type_lock locks[NUMLOCKS])
{ {
if (runtime->_initialized) { assert(!runtime->preinitializing);
Py_FatalError("runtime already initialized"); assert(!runtime->preinitialized);
} assert(!runtime->core_initialized);
assert(!runtime->preinitializing && assert(!runtime->initialized);
!runtime->preinitialized && assert(!runtime->_initialized);
!runtime->core_initialized &&
!runtime->initialized);
runtime->open_code_hook = open_code_hook; runtime->open_code_hook = open_code_hook;
runtime->open_code_userdata = open_code_userdata; runtime->open_code_userdata = open_code_userdata;
@ -476,6 +474,7 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
// Py_Initialize() must be running again. // Py_Initialize() must be running again.
// Reset to _PyRuntimeState_INIT. // Reset to _PyRuntimeState_INIT.
memcpy(runtime, &initial, sizeof(*runtime)); memcpy(runtime, &initial, sizeof(*runtime));
assert(!runtime->_initialized);
} }
if (gilstate_tss_init(runtime) != 0) { if (gilstate_tss_init(runtime) != 0) {
@ -647,14 +646,14 @@ free_interpreter(PyInterpreterState *interp)
main interpreter. We fix those fields here, in addition main interpreter. We fix those fields here, in addition
to the other dynamically initialized fields. to the other dynamically initialized fields.
*/ */
static void static PyStatus
init_interpreter(PyInterpreterState *interp, init_interpreter(PyInterpreterState *interp,
_PyRuntimeState *runtime, int64_t id, _PyRuntimeState *runtime, int64_t id,
PyInterpreterState *next, PyInterpreterState *next,
PyThread_type_lock pending_lock) PyThread_type_lock pending_lock)
{ {
if (interp->_initialized) { if (interp->_initialized) {
Py_FatalError("interpreter already initialized"); return _PyStatus_ERR("interpreter already initialized");
} }
assert(runtime != NULL); assert(runtime != NULL);
@ -675,7 +674,10 @@ init_interpreter(PyInterpreterState *interp,
memcpy(&interp->obmalloc.pools.used, temp, sizeof(temp)); memcpy(&interp->obmalloc.pools.used, temp, sizeof(temp));
} }
_PyObject_InitState(interp); PyStatus status = _PyObject_InitState(interp);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
_PyEval_InitState(interp, pending_lock); _PyEval_InitState(interp, pending_lock);
_PyGC_InitState(&interp->gc); _PyGC_InitState(&interp->gc);
@ -701,42 +703,44 @@ init_interpreter(PyInterpreterState *interp,
} }
interp->f_opcode_trace_set = false; interp->f_opcode_trace_set = false;
interp->_initialized = 1; interp->_initialized = 1;
return _PyStatus_OK();
} }
PyInterpreterState *
PyInterpreterState_New(void)
{
PyInterpreterState *interp;
_PyRuntimeState *runtime = &_PyRuntime;
PyThreadState *tstate = current_fast_get(runtime);
/* tstate is NULL when Py_InitializeFromConfig() calls PyStatus
PyInterpreterState_New() to create the main interpreter. */ _PyInterpreterState_New(PyThreadState *tstate, PyInterpreterState **pinterp)
if (_PySys_Audit(tstate, "cpython.PyInterpreterState_New", NULL) < 0) { {
return NULL; *pinterp = NULL;
// Don't get runtime from tstate since tstate can be NULL
_PyRuntimeState *runtime = &_PyRuntime;
// tstate is NULL when pycore_create_interpreter() calls
// _PyInterpreterState_New() to create the main interpreter.
if (tstate != NULL) {
if (_PySys_Audit(tstate, "cpython.PyInterpreterState_New", NULL) < 0) {
return _PyStatus_ERR("sys.audit failed");
}
} }
PyThread_type_lock pending_lock = PyThread_allocate_lock(); PyThread_type_lock pending_lock = PyThread_allocate_lock();
if (pending_lock == NULL) { if (pending_lock == NULL) {
if (tstate != NULL) { return _PyStatus_NO_MEMORY();
_PyErr_NoMemory(tstate);
}
return NULL;
} }
/* Don't get runtime from tstate since tstate can be NULL. */
struct pyinterpreters *interpreters = &runtime->interpreters;
/* We completely serialize creation of multiple interpreters, since /* We completely serialize creation of multiple interpreters, since
it simplifies things here and blocking concurrent calls isn't a problem. it simplifies things here and blocking concurrent calls isn't a problem.
Regardless, we must fully block subinterpreter creation until Regardless, we must fully block subinterpreter creation until
after the main interpreter is created. */ after the main interpreter is created. */
HEAD_LOCK(runtime); HEAD_LOCK(runtime);
struct pyinterpreters *interpreters = &runtime->interpreters;
int64_t id = interpreters->next_id; int64_t id = interpreters->next_id;
interpreters->next_id += 1; interpreters->next_id += 1;
// Allocate the interpreter and add it to the runtime state. // Allocate the interpreter and add it to the runtime state.
PyInterpreterState *interp;
PyStatus status;
PyInterpreterState *old_head = interpreters->head; PyInterpreterState *old_head = interpreters->head;
if (old_head == NULL) { if (old_head == NULL) {
// We are creating the main interpreter. // We are creating the main interpreter.
@ -755,36 +759,59 @@ PyInterpreterState_New(void)
interp = alloc_interpreter(); interp = alloc_interpreter();
if (interp == NULL) { if (interp == NULL) {
status = _PyStatus_NO_MEMORY();
goto error; goto error;
} }
// Set to _PyInterpreterState_INIT. // Set to _PyInterpreterState_INIT.
memcpy(interp, &initial._main_interpreter, memcpy(interp, &initial._main_interpreter, sizeof(*interp));
sizeof(*interp));
if (id < 0) { if (id < 0) {
/* overflow or Py_Initialize() not called yet! */ /* overflow or Py_Initialize() not called yet! */
if (tstate != NULL) { status = _PyStatus_ERR("failed to get an interpreter ID");
_PyErr_SetString(tstate, PyExc_RuntimeError,
"failed to get an interpreter ID");
}
goto error; goto error;
} }
} }
interpreters->head = interp; interpreters->head = interp;
init_interpreter(interp, runtime, id, old_head, pending_lock); status = init_interpreter(interp, runtime,
id, old_head, pending_lock);
if (_PyStatus_EXCEPTION(status)) {
goto error;
}
pending_lock = NULL;
HEAD_UNLOCK(runtime); HEAD_UNLOCK(runtime);
return interp;
assert(interp != NULL);
*pinterp = interp;
return _PyStatus_OK();
error: error:
HEAD_UNLOCK(runtime); HEAD_UNLOCK(runtime);
PyThread_free_lock(pending_lock); if (pending_lock != NULL) {
PyThread_free_lock(pending_lock);
}
if (interp != NULL) { if (interp != NULL) {
free_interpreter(interp); free_interpreter(interp);
} }
return NULL; return status;
}
PyInterpreterState *
PyInterpreterState_New(void)
{
// tstate can be NULL
PyThreadState *tstate = current_fast_get(&_PyRuntime);
PyInterpreterState *interp;
PyStatus status = _PyInterpreterState_New(tstate, &interp);
if (_PyStatus_EXCEPTION(status)) {
Py_ExitStatusException(status);
}
assert(interp != NULL);
return interp;
} }