-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
bpo-39877: take_gil() now exits the thread if finalizing #18854
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
take_gil() is now responsible to exit the thread if Python is finalizing. Not only exit before trying to acquire the GIL, but exit also once the GIL is succesfully acquired.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -180,17 +180,57 @@ drop_gil(struct _ceval_runtime_state *ceval, PyThreadState *tstate) | |
#endif | ||
} | ||
|
||
|
||
static inline int | ||
thread_must_exit(PyThreadState *tstate) | ||
{ | ||
/* bpo-39877: Access _PyRuntime directly rather than using | ||
tstate->interp->runtime to support calls from Python daemon threads. | ||
After Py_Finalize() has been called, tstate can be a dangling pointer: | ||
point to PyThreadState freed memory. */ | ||
_PyRuntimeState *runtime = &_PyRuntime; | ||
PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime); | ||
return (finalizing != NULL && finalizing != tstate); | ||
} | ||
|
||
|
||
/* Take the GIL. | ||
|
||
The function saves errno at entry and restores its value at exit. | ||
|
||
Exit immediately the thread if Py_Finalize() has been called and tstate is | ||
not the thread which called Py_Finalize(). For example, exit daemon threads | ||
which survive after Py_Finalize(). | ||
|
||
tstate must be non-NULL. */ | ||
static void | ||
take_gil(struct _ceval_runtime_state *ceval, PyThreadState *tstate) | ||
take_gil(PyThreadState *tstate) | ||
{ | ||
int err = errno; | ||
|
||
if (thread_must_exit(tstate)) { | ||
/* bpo-39877: If Py_Finalize() has been called and tstate is not the | ||
thread which called Py_Finalize(), exit immediately the thread. | ||
|
||
This code path can be reached by a daemon thread after Py_Finalize() | ||
completes. In this case, tstate is a dangling pointer: points to | ||
PyThreadState freed memory. | ||
|
||
When this function is called after Py_Finalize() completed, the GIL | ||
does no longer exist. */ | ||
PyThread_exit_thread(); | ||
} | ||
|
||
/* ensure that tstate is valid */ | ||
assert(!_PyMem_IsPtrFreed(tstate)); | ||
assert(!_PyMem_IsPtrFreed(tstate->interp)); | ||
|
||
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval; | ||
struct _gil_runtime_state *gil = &ceval->gil; | ||
|
||
/* Check someone has called PyEval_InitThreads() to create the lock */ | ||
assert(gil_created(gil)); | ||
|
||
MUTEX_LOCK(gil->mutex); | ||
|
||
if (!_Py_atomic_load_relaxed(&gil->locked)) { | ||
|
@@ -243,6 +283,18 @@ take_gil(struct _ceval_runtime_state *ceval, PyThreadState *tstate) | |
|
||
MUTEX_UNLOCK(gil->mutex); | ||
|
||
if (thread_must_exit(tstate)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure why you're doing this here. It doesn't seem necessary and, furthermore, it's unlikely to trigger. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code path is to prevent https://bugs.python.org/issue39877#msg363667 crash. Are you suggesting to move this code at line 249, after COND_TIMED_WAIT()? thread_must_exit() at entry is to prevent https://bugs.python.org/issue39877#msg363512 crash. |
||
/* bpo-36475: If Py_Finalize() has been called and tstate is not the | ||
thread which called Py_Finalize(), exit immediately the thread. | ||
|
||
This code path can be reached by a daemon thread which continues to | ||
run after wait_for_thread_shutdown() and before Py_Finalize() | ||
completes. For example, when _PyImport_Cleanup() executes Python | ||
code. */ | ||
drop_gil(ceval, tstate); | ||
PyThread_exit_thread(); | ||
} | ||
|
||
errno = err; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, but shouldn't you do this after
COND_TIMED_WAIT
below as well?