summaryrefslogtreecommitdiffstats
path: root/Python/pylifecycle.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/pylifecycle.c')
-rw-r--r--Python/pylifecycle.c154
1 files changed, 99 insertions, 55 deletions
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 4f5efc9..aaf5811 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -154,8 +154,8 @@ Py_SetStandardStreamEncoding(const char *encoding, const char *errors)
return 0;
}
-/* Global initializations. Can be undone by Py_Finalize(). Don't
- call this twice without an intervening Py_Finalize() call. When
+/* Global initializations. Can be undone by Py_FinalizeEx(). Don't
+ call this twice without an intervening Py_FinalizeEx() call. When
initializations fail, a fatal error is issued and the function does
not return. On return, the first thread and interpreter state have
been created.
@@ -327,11 +327,11 @@ _Py_InitializeEx_Private(int install_sigs, int install_importlib)
(void) PyThreadState_Swap(tstate);
#ifdef WITH_THREAD
- /* We can't call _PyEval_FiniThreads() in Py_Finalize because
+ /* We can't call _PyEval_FiniThreads() in Py_FinalizeEx because
destroying the GIL might fail when it is being referenced from
another running thread (see issue #9901).
Instead we destroy the previously created GIL here, which ensures
- that we can call Py_Initialize / Py_Finalize multiple times. */
+ that we can call Py_Initialize / Py_FinalizeEx multiple times. */
_PyEval_FiniThreads();
/* Auto-thread-state API */
@@ -477,28 +477,35 @@ file_is_closed(PyObject *fobj)
return r > 0;
}
-static void
+static int
flush_std_files(void)
{
PyObject *fout = _PySys_GetObjectId(&PyId_stdout);
PyObject *ferr = _PySys_GetObjectId(&PyId_stderr);
PyObject *tmp;
+ int status = 0;
if (fout != NULL && fout != Py_None && !file_is_closed(fout)) {
tmp = _PyObject_CallMethodId(fout, &PyId_flush, "");
- if (tmp == NULL)
+ if (tmp == NULL) {
PyErr_WriteUnraisable(fout);
+ status = -1;
+ }
else
Py_DECREF(tmp);
}
if (ferr != NULL && ferr != Py_None && !file_is_closed(ferr)) {
tmp = _PyObject_CallMethodId(ferr, &PyId_flush, "");
- if (tmp == NULL)
+ if (tmp == NULL) {
PyErr_Clear();
+ status = -1;
+ }
else
Py_DECREF(tmp);
}
+
+ return status;
}
/* Undo the effect of Py_Initialize().
@@ -515,14 +522,15 @@ flush_std_files(void)
*/
-void
-Py_Finalize(void)
+int
+Py_FinalizeEx(void)
{
PyInterpreterState *interp;
PyThreadState *tstate;
+ int status = 0;
if (!initialized)
- return;
+ return status;
wait_for_thread_shutdown();
@@ -547,7 +555,9 @@ Py_Finalize(void)
initialized = 0;
/* Flush sys.stdout and sys.stderr */
- flush_std_files();
+ if (flush_std_files() < 0) {
+ status = -1;
+ }
/* Disable signal handling */
PyOS_FiniInterrupts();
@@ -576,7 +586,9 @@ Py_Finalize(void)
PyImport_Cleanup();
/* Flush sys.stdout and sys.stderr (again, in case more was printed) */
- flush_std_files();
+ if (flush_std_files() < 0) {
+ status = -1;
+ }
/* Collect final garbage. This disposes of cycles created by
* class definitions, for example.
@@ -690,12 +702,22 @@ Py_Finalize(void)
if (Py_GETENV("PYTHONDUMPREFS"))
_Py_PrintReferenceAddresses(stderr);
#endif /* Py_TRACE_REFS */
-#ifdef PYMALLOC_DEBUG
- if (Py_GETENV("PYTHONMALLOCSTATS"))
- _PyObject_DebugMallocStats(stderr);
+#ifdef WITH_PYMALLOC
+ if (_PyMem_PymallocEnabled()) {
+ char *opt = Py_GETENV("PYTHONMALLOCSTATS");
+ if (opt != NULL && *opt != '\0')
+ _PyObject_DebugMallocStats(stderr);
+ }
#endif
call_ll_exitfuncs();
+ return status;
+}
+
+void
+Py_Finalize(void)
+{
+ Py_FinalizeEx();
}
/* Create and initialize a new interpreter and thread, and return the
@@ -777,7 +799,7 @@ Py_NewInterpreter(void)
if (initstdio() < 0)
Py_FatalError(
- "Py_Initialize: can't initialize sys standard streams");
+ "Py_Initialize: can't initialize sys standard streams");
initmain(interp);
if (!Py_NoSiteFlag)
initsite();
@@ -803,7 +825,7 @@ handle_error:
frames, and that it is its interpreter's only remaining thread.
It is a fatal error to violate these constraints.
- (Py_Finalize() doesn't have these constraints -- it zaps
+ (Py_FinalizeEx() doesn't have these constraints -- it zaps
everything, regardless.)
Locking: as above.
@@ -972,6 +994,9 @@ is_valid_fd(int fd)
if (fd < 0 || !_PyVerify_fd(fd))
return 0;
_Py_BEGIN_SUPPRESS_IPH
+ /* Prefer dup() over fstat(). fstat() can require input/output whereas
+ dup() doesn't, there is a low risk of EMFILE/ENFILE at Python
+ startup. */
fd2 = dup(fd);
if (fd2 >= 0)
close(fd2);
@@ -982,8 +1007,8 @@ is_valid_fd(int fd)
/* returns Py_None if the fd is not valid */
static PyObject*
create_stdio(PyObject* io,
- int fd, int write_mode, char* name,
- char* encoding, char* errors)
+ int fd, int write_mode, const char* name,
+ const char* encoding, const char* errors)
{
PyObject *buf = NULL, *stream = NULL, *text = NULL, *raw = NULL, *res;
const char* mode;
@@ -1013,7 +1038,8 @@ create_stdio(PyObject* io,
mode = "rb";
buf = _PyObject_CallMethodId(io, &PyId_open, "isiOOOi",
fd, mode, buffering,
- Py_None, Py_None, Py_None, 0);
+ Py_None, Py_None, /* encoding, errors */
+ Py_None, 0); /* newline, closefd */
if (buf == NULL)
goto error;
@@ -1241,31 +1267,62 @@ initstdio(void)
}
+static void
+_Py_FatalError_DumpTracebacks(int fd)
+{
+ PyThreadState *tstate;
+
+#ifdef WITH_THREAD
+ /* PyGILState_GetThisThreadState() works even if the GIL was released */
+ tstate = PyGILState_GetThisThreadState();
+#else
+ tstate = PyThreadState_GET();
+#endif
+ if (tstate == NULL) {
+ /* _Py_DumpTracebackThreads() requires the thread state to display
+ * frames */
+ return;
+ }
+
+ fputc('\n', stderr);
+ fflush(stderr);
+
+ /* display the current Python stack */
+ _Py_DumpTracebackThreads(fd, tstate->interp, tstate);
+}
+
/* Print the current exception (if an exception is set) with its traceback,
- * or display the current Python stack.
- *
- * Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is
- * called on catastrophic cases. */
+ or display the current Python stack.
-static void
-_Py_PrintFatalError(int fd)
+ Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is
+ called on catastrophic cases.
+
+ Return 1 if the traceback was displayed, 0 otherwise. */
+
+static int
+_Py_FatalError_PrintExc(int fd)
{
PyObject *ferr, *res;
PyObject *exception, *v, *tb;
int has_tb;
- PyThreadState *tstate;
+
+ if (PyThreadState_GET() == NULL) {
+ /* The GIL is released: trying to acquire it is likely to deadlock,
+ just give up. */
+ return 0;
+ }
PyErr_Fetch(&exception, &v, &tb);
if (exception == NULL) {
/* No current exception */
- goto display_stack;
+ return 0;
}
ferr = _PySys_GetObjectId(&PyId_stderr);
if (ferr == NULL || ferr == Py_None) {
/* sys.stderr is not set yet or set to None,
no need to try to display the exception */
- goto display_stack;
+ return 0;
}
PyErr_NormalizeException(&exception, &v, &tb);
@@ -1276,7 +1333,7 @@ _Py_PrintFatalError(int fd)
PyException_SetTraceback(v, tb);
if (exception == NULL) {
/* PyErr_NormalizeException() failed */
- goto display_stack;
+ return 0;
}
has_tb = (tb != Py_None);
@@ -1292,28 +1349,9 @@ _Py_PrintFatalError(int fd)
else
Py_DECREF(res);
- if (has_tb)
- return;
-
-display_stack:
-#ifdef WITH_THREAD
- /* PyGILState_GetThisThreadState() works even if the GIL was released */
- tstate = PyGILState_GetThisThreadState();
-#else
- tstate = PyThreadState_GET();
-#endif
- if (tstate == NULL) {
- /* _Py_DumpTracebackThreads() requires the thread state to display
- * frames */
- return;
- }
-
- fputc('\n', stderr);
- fflush(stderr);
-
- /* display the current Python stack */
- _Py_DumpTracebackThreads(fd, tstate->interp, tstate);
+ return has_tb;
}
+
/* Print fatal error message and abort */
void
@@ -1339,10 +1377,14 @@ Py_FatalError(const char *msg)
/* Print the exception (if an exception is set) with its traceback,
* or display the current Python stack. */
- _Py_PrintFatalError(fd);
+ if (!_Py_FatalError_PrintExc(fd))
+ _Py_FatalError_DumpTracebacks(fd);
- /* Flush sys.stdout and sys.stderr */
- flush_std_files();
+ /* Check if the current Python thread hold the GIL */
+ if (PyThreadState_GET() != NULL) {
+ /* Flush sys.stdout and sys.stderr */
+ flush_std_files();
+ }
/* The main purpose of faulthandler is to display the traceback. We already
* did our best to display it. So faulthandler can now be disabled.
@@ -1447,7 +1489,9 @@ call_ll_exitfuncs(void)
void
Py_Exit(int sts)
{
- Py_Finalize();
+ if (Py_FinalizeEx() < 0) {
+ sts = 120;
+ }
exit(sts);
}