bpo-36356: Destroy the GIL at exit (GH-12453)

* Add _PyEval_FiniThreads2(). _PyEval_FiniThreads() now only clears
  the pending lock, whereas _PyEval_FiniThreads2() destroys the GIL.
* pymain_free() now calls _PyEval_FiniThreads2().
* Py_FinalizeEx() now calls _PyEval_FiniThreads().
This commit is contained in:
Victor Stinner 2019-04-29 11:15:56 +02:00 committed by GitHub
parent 7a5a1cfe04
commit b36e5d627d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 26 additions and 18 deletions

View File

@ -192,9 +192,6 @@ PyAPI_FUNC(void) PyEval_RestoreThread(PyThreadState *);
PyAPI_FUNC(int) PyEval_ThreadsInitialized(void); PyAPI_FUNC(int) PyEval_ThreadsInitialized(void);
PyAPI_FUNC(void) PyEval_InitThreads(void); PyAPI_FUNC(void) PyEval_InitThreads(void);
#ifndef Py_LIMITED_API
PyAPI_FUNC(void) _PyEval_FiniThreads(void);
#endif /* !Py_LIMITED_API */
PyAPI_FUNC(void) PyEval_AcquireLock(void) Py_DEPRECATED(3.2); PyAPI_FUNC(void) PyEval_AcquireLock(void) Py_DEPRECATED(3.2);
PyAPI_FUNC(void) PyEval_ReleaseLock(void) /* Py_DEPRECATED(3.2) */; PyAPI_FUNC(void) PyEval_ReleaseLock(void) /* Py_DEPRECATED(3.2) */;
PyAPI_FUNC(void) PyEval_AcquireThread(PyThreadState *tstate); PyAPI_FUNC(void) PyEval_AcquireThread(PyThreadState *tstate);

View File

@ -54,6 +54,9 @@ struct _ceval_runtime_state {
PyAPI_FUNC(void) _PyEval_Initialize(struct _ceval_runtime_state *); PyAPI_FUNC(void) _PyEval_Initialize(struct _ceval_runtime_state *);
PyAPI_FUNC(void) _PyEval_FiniThreads(void);
PyAPI_FUNC(void) _PyEval_FiniThreads2(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -1,6 +1,7 @@
/* Python interpreter main program */ /* Python interpreter main program */
#include "Python.h" #include "Python.h"
#include "pycore_ceval.h" /* _PyEval_FiniThreads2() */
#include "pycore_coreconfig.h" #include "pycore_coreconfig.h"
#include "pycore_pylifecycle.h" #include "pycore_pylifecycle.h"
#include "pycore_pymem.h" #include "pycore_pymem.h"
@ -525,15 +526,15 @@ done:
/* --- pymain_main() ---------------------------------------------- */ /* --- pymain_main() ---------------------------------------------- */
/* Free global variables which cannot be freed in Py_Finalize():
configuration options set before Py_Initialize() which should
remain valid after Py_Finalize(), since
Py_Initialize()-Py_Finalize() can be called multiple times. */
static void static void
pymain_free(void) pymain_free(void)
{ {
_PyImport_Fini2(); _PyImport_Fini2();
_PyEval_FiniThreads2();
/* Free global variables which cannot be freed in Py_Finalize():
configuration options set before Py_Initialize() which should
remain valid after Py_Finalize(), since
Py_Initialize()-Py_Finalize() can be called multiple times. */
_PyPathConfig_ClearGlobal(); _PyPathConfig_ClearGlobal();
_Py_ClearStandardStreamEncoding(); _Py_ClearStandardStreamEncoding();
_Py_ClearArgcArgv(); _Py_ClearArgcArgv();

View File

@ -188,8 +188,19 @@ PyEval_InitThreads(void)
} }
} }
void void
_PyEval_FiniThreads(void) _PyEval_FiniThreads(void)
{
if (_PyRuntime.ceval.pending.lock != NULL) {
PyThread_free_lock(_PyRuntime.ceval.pending.lock);
_PyRuntime.ceval.pending.lock = NULL;
}
}
void
_PyEval_FiniThreads2(void)
{ {
if (!gil_created()) { if (!gil_created()) {
return; return;
@ -197,11 +208,6 @@ _PyEval_FiniThreads(void)
destroy_gil(); destroy_gil();
assert(!gil_created()); assert(!gil_created());
if (_PyRuntime.ceval.pending.lock != NULL) {
PyThread_free_lock(_PyRuntime.ceval.pending.lock);
_PyRuntime.ceval.pending.lock = NULL;
}
} }
static inline void static inline void

View File

@ -4,8 +4,9 @@
#include "Python-ast.h" #include "Python-ast.h"
#undef Yield /* undefine macro conflicting with <winbase.h> */ #undef Yield /* undefine macro conflicting with <winbase.h> */
#include "pycore_coreconfig.h" #include "pycore_ceval.h" /* _PyEval_FiniThreads() */
#include "pycore_context.h" #include "pycore_context.h"
#include "pycore_coreconfig.h"
#include "pycore_fileutils.h" #include "pycore_fileutils.h"
#include "pycore_hamt.h" #include "pycore_hamt.h"
#include "pycore_pathconfig.h" #include "pycore_pathconfig.h"
@ -555,12 +556,11 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
return _Py_INIT_ERR("can't make first thread"); return _Py_INIT_ERR("can't make first thread");
(void) PyThreadState_Swap(tstate); (void) PyThreadState_Swap(tstate);
/* We can't call _PyEval_FiniThreads() in Py_FinalizeEx because /* Destroying the GIL in Py_FinalizeEx might fail when it is being
destroying the GIL might fail when it is being referenced from referenced from another running thread (see bpo-9901).
another running thread (see issue #9901).
Instead we destroy the previously created GIL here, which ensures Instead we destroy the previously created GIL here, which ensures
that we can call Py_Initialize / Py_FinalizeEx multiple times. */ that we can call Py_Initialize / Py_FinalizeEx multiple times. */
_PyEval_FiniThreads(); _PyEval_FiniThreads2();
/* Auto-thread-state API */ /* Auto-thread-state API */
_PyGILState_Init(runtime, interp, tstate); _PyGILState_Init(runtime, interp, tstate);
@ -1357,6 +1357,7 @@ Py_FinalizeEx(void)
call_ll_exitfuncs(runtime); call_ll_exitfuncs(runtime);
_PyEval_FiniThreads();
_PyRuntime_Finalize(); _PyRuntime_Finalize();
return status; return status;
} }