Skip to content

Commit b936cf4

Browse files
authored
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.
1 parent 844f4c2 commit b936cf4

File tree

7 files changed

+99
-51
lines changed

7 files changed

+99
-51
lines changed

Include/internal/pycore_initconfig.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@ struct pyruntimestate;
2222
#endif
2323

2424
#define _PyStatus_OK() \
25-
(PyStatus){._type = _PyStatus_TYPE_OK,}
25+
(PyStatus){._type = _PyStatus_TYPE_OK}
2626
/* other fields are set to 0 */
2727
#define _PyStatus_ERR(ERR_MSG) \
2828
(PyStatus){ \
2929
._type = _PyStatus_TYPE_ERROR, \
3030
.func = _PyStatus_GET_FUNC(), \
3131
.err_msg = (ERR_MSG)}
3232
/* other fields are set to 0 */
33-
#define _PyStatus_NO_MEMORY() _PyStatus_ERR("memory allocation failed")
33+
#define _PyStatus_NO_MEMORY_ERRMSG "memory allocation failed"
34+
#define _PyStatus_NO_MEMORY() _PyStatus_ERR(_PyStatus_NO_MEMORY_ERRMSG)
3435
#define _PyStatus_EXIT(EXITCODE) \
3536
(PyStatus){ \
3637
._type = _PyStatus_TYPE_EXIT, \
@@ -45,7 +46,7 @@ struct pyruntimestate;
4546
do { (err).func = _PyStatus_GET_FUNC(); } while (0)
4647

4748
// Export for '_testinternalcapi' shared extension
48-
PyAPI_FUNC(PyObject *) _PyErr_SetFromPyStatus(PyStatus status);
49+
PyAPI_FUNC(void) _PyErr_SetFromPyStatus(PyStatus status);
4950

5051

5152
/* --- PyWideStringList ------------------------------------------------ */

Include/internal/pycore_interp.h

+4
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,10 @@ might not be allowed in the current interpreter (i.e. os.fork() would fail).
311311
extern int _PyInterpreterState_HasFeature(PyInterpreterState *interp,
312312
unsigned long feature);
313313

314+
PyAPI_FUNC(PyStatus) _PyInterpreterState_New(
315+
PyThreadState *tstate,
316+
PyInterpreterState **pinterp);
317+
314318

315319
#ifdef __cplusplus
316320
}

Include/internal/pycore_object.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ _PyType_HasFeature(PyTypeObject *type, unsigned long feature) {
182182

183183
extern void _PyType_InitCache(PyInterpreterState *interp);
184184

185-
extern void _PyObject_InitState(PyInterpreterState *interp);
185+
extern PyStatus _PyObject_InitState(PyInterpreterState *interp);
186186
extern void _PyObject_FiniState(PyInterpreterState *interp);
187187
extern bool _PyRefchain_IsTraced(PyInterpreterState *interp, PyObject *obj);
188188

Objects/object.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -2034,7 +2034,7 @@ PyObject _Py_NotImplementedStruct = {
20342034
};
20352035

20362036

2037-
void
2037+
PyStatus
20382038
_PyObject_InitState(PyInterpreterState *interp)
20392039
{
20402040
#ifdef Py_TRACE_REFS
@@ -2048,9 +2048,10 @@ _PyObject_InitState(PyInterpreterState *interp)
20482048
_Py_hashtable_hash_ptr, _Py_hashtable_compare_direct,
20492049
NULL, NULL, &alloc);
20502050
if (REFCHAIN(interp) == NULL) {
2051-
Py_FatalError("_PyObject_InitState() memory allocation failure");
2051+
return _PyStatus_NO_MEMORY();
20522052
}
20532053
#endif
2054+
return _PyStatus_OK();
20542055
}
20552056

20562057
void

Python/initconfig.c

+20-7
Original file line numberDiff line numberDiff line change
@@ -335,21 +335,34 @@ int PyStatus_IsExit(PyStatus status)
335335
int PyStatus_Exception(PyStatus status)
336336
{ return _PyStatus_EXCEPTION(status); }
337337

338-
PyObject*
338+
void
339339
_PyErr_SetFromPyStatus(PyStatus status)
340340
{
341341
if (!_PyStatus_IS_ERROR(status)) {
342342
PyErr_Format(PyExc_SystemError,
343-
"%s() expects an error PyStatus",
344-
_PyStatus_GET_FUNC());
343+
"_PyErr_SetFromPyStatus() status is not an error");
344+
return;
345345
}
346-
else if (status.func) {
347-
PyErr_Format(PyExc_ValueError, "%s: %s", status.func, status.err_msg);
346+
347+
const char *err_msg = status.err_msg;
348+
if (err_msg == NULL || strlen(err_msg) == 0) {
349+
PyErr_Format(PyExc_SystemError,
350+
"_PyErr_SetFromPyStatus() status has no error message");
351+
return;
352+
}
353+
354+
if (strcmp(err_msg, _PyStatus_NO_MEMORY_ERRMSG) == 0) {
355+
PyErr_NoMemory();
356+
return;
357+
}
358+
359+
const char *func = status.func;
360+
if (func) {
361+
PyErr_Format(PyExc_RuntimeError, "%s: %s", func, err_msg);
348362
}
349363
else {
350-
PyErr_Format(PyExc_ValueError, "%s", status.err_msg);
364+
PyErr_Format(PyExc_RuntimeError, "%s", err_msg);
351365
}
352-
return NULL;
353366
}
354367

355368

Python/pylifecycle.c

+5-3
Original file line numberDiff line numberDiff line change
@@ -629,10 +629,12 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
629629
PyThreadState **tstate_p)
630630
{
631631
PyStatus status;
632-
PyInterpreterState *interp = PyInterpreterState_New();
633-
if (interp == NULL) {
634-
return _PyStatus_ERR("can't make main interpreter");
632+
PyInterpreterState *interp;
633+
status = _PyInterpreterState_New(NULL, &interp);
634+
if (_PyStatus_EXCEPTION(status)) {
635+
return status;
635636
}
637+
assert(interp != NULL);
636638
assert(_Py_IsMainInterpreter(interp));
637639

638640
status = _PyConfig_Copy(&interp->config, src_config);

Python/pystate.c

+62-35
Original file line numberDiff line numberDiff line change
@@ -426,13 +426,11 @@ init_runtime(_PyRuntimeState *runtime,
426426
Py_ssize_t unicode_next_index,
427427
PyThread_type_lock locks[NUMLOCKS])
428428
{
429-
if (runtime->_initialized) {
430-
Py_FatalError("runtime already initialized");
431-
}
432-
assert(!runtime->preinitializing &&
433-
!runtime->preinitialized &&
434-
!runtime->core_initialized &&
435-
!runtime->initialized);
429+
assert(!runtime->preinitializing);
430+
assert(!runtime->preinitialized);
431+
assert(!runtime->core_initialized);
432+
assert(!runtime->initialized);
433+
assert(!runtime->_initialized);
436434

437435
runtime->open_code_hook = open_code_hook;
438436
runtime->open_code_userdata = open_code_userdata;
@@ -476,6 +474,7 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
476474
// Py_Initialize() must be running again.
477475
// Reset to _PyRuntimeState_INIT.
478476
memcpy(runtime, &initial, sizeof(*runtime));
477+
assert(!runtime->_initialized);
479478
}
480479

481480
if (gilstate_tss_init(runtime) != 0) {
@@ -647,14 +646,14 @@ free_interpreter(PyInterpreterState *interp)
647646
main interpreter. We fix those fields here, in addition
648647
to the other dynamically initialized fields.
649648
*/
650-
static void
649+
static PyStatus
651650
init_interpreter(PyInterpreterState *interp,
652651
_PyRuntimeState *runtime, int64_t id,
653652
PyInterpreterState *next,
654653
PyThread_type_lock pending_lock)
655654
{
656655
if (interp->_initialized) {
657-
Py_FatalError("interpreter already initialized");
656+
return _PyStatus_ERR("interpreter already initialized");
658657
}
659658

660659
assert(runtime != NULL);
@@ -675,7 +674,10 @@ init_interpreter(PyInterpreterState *interp,
675674
memcpy(&interp->obmalloc.pools.used, temp, sizeof(temp));
676675
}
677676

678-
_PyObject_InitState(interp);
677+
PyStatus status = _PyObject_InitState(interp);
678+
if (_PyStatus_EXCEPTION(status)) {
679+
return status;
680+
}
679681

680682
_PyEval_InitState(interp, pending_lock);
681683
_PyGC_InitState(&interp->gc);
@@ -701,42 +703,44 @@ init_interpreter(PyInterpreterState *interp,
701703
}
702704
interp->f_opcode_trace_set = false;
703705
interp->_initialized = 1;
706+
return _PyStatus_OK();
704707
}
705708

706-
PyInterpreterState *
707-
PyInterpreterState_New(void)
709+
710+
PyStatus
711+
_PyInterpreterState_New(PyThreadState *tstate, PyInterpreterState **pinterp)
708712
{
709-
PyInterpreterState *interp;
713+
*pinterp = NULL;
714+
715+
// Don't get runtime from tstate since tstate can be NULL
710716
_PyRuntimeState *runtime = &_PyRuntime;
711-
PyThreadState *tstate = current_fast_get(runtime);
712717

713-
/* tstate is NULL when Py_InitializeFromConfig() calls
714-
PyInterpreterState_New() to create the main interpreter. */
715-
if (_PySys_Audit(tstate, "cpython.PyInterpreterState_New", NULL) < 0) {
716-
return NULL;
718+
// tstate is NULL when pycore_create_interpreter() calls
719+
// _PyInterpreterState_New() to create the main interpreter.
720+
if (tstate != NULL) {
721+
if (_PySys_Audit(tstate, "cpython.PyInterpreterState_New", NULL) < 0) {
722+
return _PyStatus_ERR("sys.audit failed");
723+
}
717724
}
718725

719726
PyThread_type_lock pending_lock = PyThread_allocate_lock();
720727
if (pending_lock == NULL) {
721-
if (tstate != NULL) {
722-
_PyErr_NoMemory(tstate);
723-
}
724-
return NULL;
728+
return _PyStatus_NO_MEMORY();
725729
}
726730

727-
/* Don't get runtime from tstate since tstate can be NULL. */
728-
struct pyinterpreters *interpreters = &runtime->interpreters;
729-
730731
/* We completely serialize creation of multiple interpreters, since
731732
it simplifies things here and blocking concurrent calls isn't a problem.
732733
Regardless, we must fully block subinterpreter creation until
733734
after the main interpreter is created. */
734735
HEAD_LOCK(runtime);
735736

737+
struct pyinterpreters *interpreters = &runtime->interpreters;
736738
int64_t id = interpreters->next_id;
737739
interpreters->next_id += 1;
738740

739741
// Allocate the interpreter and add it to the runtime state.
742+
PyInterpreterState *interp;
743+
PyStatus status;
740744
PyInterpreterState *old_head = interpreters->head;
741745
if (old_head == NULL) {
742746
// We are creating the main interpreter.
@@ -755,36 +759,59 @@ PyInterpreterState_New(void)
755759

756760
interp = alloc_interpreter();
757761
if (interp == NULL) {
762+
status = _PyStatus_NO_MEMORY();
758763
goto error;
759764
}
760765
// Set to _PyInterpreterState_INIT.
761-
memcpy(interp, &initial._main_interpreter,
762-
sizeof(*interp));
766+
memcpy(interp, &initial._main_interpreter, sizeof(*interp));
763767

764768
if (id < 0) {
765769
/* overflow or Py_Initialize() not called yet! */
766-
if (tstate != NULL) {
767-
_PyErr_SetString(tstate, PyExc_RuntimeError,
768-
"failed to get an interpreter ID");
769-
}
770+
status = _PyStatus_ERR("failed to get an interpreter ID");
770771
goto error;
771772
}
772773
}
773774
interpreters->head = interp;
774775

775-
init_interpreter(interp, runtime, id, old_head, pending_lock);
776+
status = init_interpreter(interp, runtime,
777+
id, old_head, pending_lock);
778+
if (_PyStatus_EXCEPTION(status)) {
779+
goto error;
780+
}
781+
pending_lock = NULL;
776782

777783
HEAD_UNLOCK(runtime);
778-
return interp;
784+
785+
assert(interp != NULL);
786+
*pinterp = interp;
787+
return _PyStatus_OK();
779788

780789
error:
781790
HEAD_UNLOCK(runtime);
782791

783-
PyThread_free_lock(pending_lock);
792+
if (pending_lock != NULL) {
793+
PyThread_free_lock(pending_lock);
794+
}
784795
if (interp != NULL) {
785796
free_interpreter(interp);
786797
}
787-
return NULL;
798+
return status;
799+
}
800+
801+
802+
PyInterpreterState *
803+
PyInterpreterState_New(void)
804+
{
805+
// tstate can be NULL
806+
PyThreadState *tstate = current_fast_get(&_PyRuntime);
807+
808+
PyInterpreterState *interp;
809+
PyStatus status = _PyInterpreterState_New(tstate, &interp);
810+
if (_PyStatus_EXCEPTION(status)) {
811+
Py_ExitStatusException(status);
812+
}
813+
assert(interp != NULL);
814+
return interp;
788815
}
789816

790817

0 commit comments

Comments
 (0)