/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│ │ vi: set et ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi │ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Python 3 │ │ https://docs.python.org/3/license.html │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "third_party/python/Include/fileobject.h" #include "third_party/python/Include/grammar.h" #include "third_party/python/Include/import.h" #include "third_party/python/Include/modsupport.h" #include "third_party/python/Include/object.h" #include "third_party/python/Include/objimpl.h" #include "third_party/python/Include/pydebug.h" #include "third_party/python/Include/pyerrors.h" #include "third_party/python/Include/pylifecycle.h" #include "third_party/python/Include/pymem.h" #include "third_party/python/Include/pystate.h" #include "third_party/python/Include/yoink.h" #include "third_party/python/pyconfig.h" /* Undo the effect of Py_Initialize(). Beware: if multiple interpreter and/or thread states exist, these are not wiped out; only the current thread and interpreter state are deleted. But since everything else is deleted, those other interpreter and thread states should no longer be used. (XXX We should do better, e.g. wipe out all interpreters and threads.) Locking: as above. */ #ifdef WITH_THREAD extern void wait_for_thread_shutdown(void); extern void _PyGILState_Init(PyInterpreterState *, PyThreadState *); extern void _PyGILState_Fini(void); #endif /* WITH_THREAD */ int Py_FinalizeEx(void) { PyInterpreterState *interp; PyThreadState *tstate; int status = 0; if (!_Py_initialized) return status; #ifdef WITH_THREAD wait_for_thread_shutdown(); #endif /* The interpreter is still entirely intact at this point, and the * exit funcs may be relying on that. In particular, if some thread * or exit func is still waiting to do an import, the import machinery * expects Py_IsInitialized() to return true. So don't say the * interpreter is uninitialized until after the exit funcs have run. * Note that Threading.py uses an exit func to do a join on all the * threads created thru it, so this also protects pending imports in * the threads created via Threading. */ _Py_CallExitFuncs(); /* Get current thread state and interpreter pointer */ tstate = PyThreadState_GET(); interp = tstate->interp; /* Remaining threads (e.g. daemon threads) will automatically exit after taking the GIL (in PyEval_RestoreThread()). */ _Py_Finalizing = tstate; _Py_initialized = 0; /* Flush sys.stdout and sys.stderr */ if (_Py_FlushStdFiles() < 0) { status = -1; } /* Disable signal handling */ PyOS_FiniInterrupts(); /* Collect garbage. This may call finalizers; it's nice to call these * before all modules are destroyed. * XXX If a __del__ or weakref callback is triggered here, and tries to * XXX import a module, bad things can happen, because Python no * XXX longer believes it's initialized. * XXX Fatal Python error: Interpreter not initialized (version mismatch?) * XXX is easy to provoke that way. I've also seen, e.g., * XXX Exception exceptions.ImportError: 'No module named sha' * XXX in ignored * XXX but I'm unclear on exactly how that one happens. In any case, * XXX I haven't seen a real-life report of either of these. */ _PyGC_CollectIfEnabled(); #ifdef COUNT_ALLOCS /* With COUNT_ALLOCS, it helps to run GC multiple times: each collection might release some types from the type list, so they become garbage. */ while (_PyGC_CollectIfEnabled() > 0) /* nothing */; #endif /* Destroy all modules */ PyImport_Cleanup(); _PyImportLookupTables_Cleanup(); /* Flush sys.stdout and sys.stderr (again, in case more was printed) */ if (_Py_FlushStdFiles() < 0) { status = -1; } /* Collect final garbage. This disposes of cycles created by * class definitions, for example. * XXX This is disabled because it caused too many problems. If * XXX a __del__ or weakref callback triggers here, Python code has * XXX a hard time running, because even the sys module has been * XXX cleared out (sys.stdout is gone, sys.excepthook is gone, etc). * XXX One symptom is a sequence of information-free messages * XXX coming from threads (if a __del__ or callback is invoked, * XXX other threads can execute too, and any exception they encounter * XXX triggers a comedy of errors as subsystem after subsystem * XXX fails to find what it *expects* to find in sys to help report * XXX the exception and consequent unexpected failures). I've also * XXX seen segfaults then, after adding print statements to the * XXX Python code getting called. */ #if 0 _PyGC_CollectIfEnabled(); #endif #if IsModeDbg() /* Disable tracemalloc after all Python objects have been destroyed, so it is possible to use tracemalloc in objects destructor. */ _PyTraceMalloc_Fini(); #endif /* Destroy the database used by _PyImport_{Fixup,Find}Extension */ _PyImport_Fini(); /* Cleanup typeobject.c's internal caches. */ _PyType_Fini(); /* unload faulthandler module */ _PyFaulthandler_Fini(); /* Debugging stuff */ #ifdef COUNT_ALLOCS dump_counts(stderr); #endif /* dump hash stats */ _PyHash_Fini(); _PY_DEBUG_PRINT_TOTAL_REFS(); #ifdef Py_TRACE_REFS /* Display all objects still alive -- this can invoke arbitrary * __repr__ overrides, so requires a mostly-intact interpreter. * Alas, a lot of stuff may still be alive now that will be cleaned * up later. */ if (Py_GETENV("PYTHONDUMPREFS")) _Py_PrintReferences(stderr); #endif /* Py_TRACE_REFS */ /* Clear interpreter state and all thread states. */ PyInterpreterState_Clear(interp); /* Now we decref the exception classes. After this point nothing can raise an exception. That's okay, because each Fini() method below has been checked to make sure no exceptions are ever raised. */ _PyExc_Fini(); /* Sundry finalizers */ PyMethod_Fini(); PyFrame_Fini(); PyCFunction_Fini(); PyTuple_Fini(); PyList_Fini(); PySet_Fini(); PyBytes_Fini(); PyByteArray_Fini(); PyLong_Fini(); PyFloat_Fini(); PyDict_Fini(); PySlice_Fini(); _PyGC_Fini(); _PyRandom_Fini(); _PyArg_Fini(); PyAsyncGen_Fini(); /* Cleanup Unicode implementation */ _PyUnicode_Fini(); /* reset file system default encoding */ if (!Py_HasFileSystemDefaultEncoding && Py_FileSystemDefaultEncoding) { PyMem_RawFree((char*)Py_FileSystemDefaultEncoding); Py_FileSystemDefaultEncoding = NULL; } /* XXX Still allocated: - various static ad-hoc pointers to interned strings - int and float free list blocks - whatever various modules and libraries allocate */ PyGrammar_RemoveAccelerators(&_PyParser_Grammar); /* Cleanup auto-thread-state */ #ifdef WITH_THREAD _PyGILState_Fini(); #endif /* WITH_THREAD */ /* Delete current thread. After this, many C API calls become crashy. */ PyThreadState_Swap(NULL); PyInterpreterState_Delete(interp); #ifdef Py_TRACE_REFS /* Display addresses (& refcnts) of all objects still alive. * An address can be used to find the repr of the object, printed * above by _Py_PrintReferences. */ if (Py_GETENV("PYTHONDUMPREFS")) _Py_PrintReferenceAddresses(stderr); #endif /* Py_TRACE_REFS */ #ifdef WITH_PYMALLOC #if IsModeDbg() if (_PyMem_PymallocEnabled()) { char *opt = Py_GETENV("PYTHONMALLOCSTATS"); if (opt != NULL && *opt != '\0') _PyObject_DebugMallocStats(stderr); } #endif #endif _Py_CallLlExitFuncs(); return status; } void Py_Finalize(void) { Py_FinalizeEx(); }