mirror of
https://github.com/python/cpython.git
synced 2025-09-19 15:10:58 +00:00
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:
parent
844f4c2e12
commit
b936cf4fe0
7 changed files with 101 additions and 53 deletions
|
@ -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 ------------------------------------------------ */
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
101
Python/pystate.c
101
Python/pystate.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue