Skip to content

Commit

Permalink
bpo-39984: Pass tstate to _PyEval_SignalAsyncExc() (GH-19049)
Browse files Browse the repository at this point in the history
_PyEval_SignalAsyncExc() and _PyEval_FiniThreads() now expect tstate,
instead of ceval.
  • Loading branch information
vstinner authored Mar 18, 2020
1 parent 611836a commit 56bfdeb
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 26 deletions.
6 changes: 2 additions & 4 deletions Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,15 @@ struct _frame;
extern void _Py_FinishPendingCalls(PyThreadState *tstate);
extern void _PyEval_InitRuntimeState(struct _ceval_runtime_state *);
extern void _PyEval_InitState(struct _ceval_state *);
extern void _PyEval_FiniThreads(
struct _ceval_runtime_state *ceval);
extern void _PyEval_FiniThreads(PyThreadState *tstate);
PyAPI_FUNC(void) _PyEval_SignalReceived(
struct _ceval_runtime_state *ceval);
PyAPI_FUNC(int) _PyEval_AddPendingCall(
PyThreadState *tstate,
struct _ceval_runtime_state *ceval,
int (*func)(void *),
void *arg);
PyAPI_FUNC(void) _PyEval_SignalAsyncExc(
struct _ceval_runtime_state *ceval);
PyAPI_FUNC(void) _PyEval_SignalAsyncExc(PyThreadState *tstate);
PyAPI_FUNC(void) _PyEval_ReInitThreads(
struct pyruntimestate *runtime);
PyAPI_FUNC(void) _PyEval_SetCoroutineOriginTrackingDepth(
Expand Down
11 changes: 7 additions & 4 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,9 @@ PyEval_InitThreads(void)
}

void
_PyEval_FiniThreads(struct _ceval_runtime_state *ceval)
_PyEval_FiniThreads(PyThreadState *tstate)
{
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
struct _gil_runtime_state *gil = &ceval->gil;
if (!gil_created(gil)) {
return;
Expand Down Expand Up @@ -356,10 +357,11 @@ void
_PyEval_ReInitThreads(_PyRuntimeState *runtime)
{
struct _ceval_runtime_state *ceval = &runtime->ceval;
if (!gil_created(&ceval->gil)) {
struct _gil_runtime_state *gil = &runtime->ceval.gil;
if (!gil_created(gil)) {
return;
}
recreate_gil(&ceval->gil);
recreate_gil(gil);
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
ensure_tstate_not_null(__func__, tstate);

Expand All @@ -379,8 +381,9 @@ _PyEval_ReInitThreads(_PyRuntimeState *runtime)
raised. */

void
_PyEval_SignalAsyncExc(struct _ceval_runtime_state *ceval)
_PyEval_SignalAsyncExc(PyThreadState *tstate)
{
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
SIGNAL_ASYNC_EXC(ceval);
}

Expand Down
2 changes: 1 addition & 1 deletion Python/ceval_gil.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ take_gil(PyThreadState *tstate)

/* Don't access tstate if the thread must exit */
if (!must_exit && tstate->async_exc != NULL) {
_PyEval_SignalAsyncExc(ceval);
_PyEval_SignalAsyncExc(tstate);
}

MUTEX_UNLOCK(gil->mutex);
Expand Down
2 changes: 1 addition & 1 deletion Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
another running thread (see issue #9901).
Instead we destroy the previously created GIL here, which ensures
that we can call Py_Initialize / Py_FinalizeEx multiple times. */
_PyEval_FiniThreads(&runtime->ceval);
_PyEval_FiniThreads(tstate);

/* Auto-thread-state API */
status = _PyGILState_Init(tstate);
Expand Down
35 changes: 19 additions & 16 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1034,23 +1034,26 @@ PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc)
* head_mutex for the duration.
*/
HEAD_LOCK(runtime);
for (PyThreadState *p = interp->tstate_head; p != NULL; p = p->next) {
if (p->thread_id == id) {
/* Tricky: we need to decref the current value
* (if any) in p->async_exc, but that can in turn
* allow arbitrary Python code to run, including
* perhaps calls to this function. To prevent
* deadlock, we need to release head_mutex before
* the decref.
*/
PyObject *old_exc = p->async_exc;
Py_XINCREF(exc);
p->async_exc = exc;
HEAD_UNLOCK(runtime);
Py_XDECREF(old_exc);
_PyEval_SignalAsyncExc(&runtime->ceval);
return 1;
for (PyThreadState *tstate = interp->tstate_head; tstate != NULL; tstate = tstate->next) {
if (tstate->thread_id != id) {
continue;
}

/* Tricky: we need to decref the current value
* (if any) in tstate->async_exc, but that can in turn
* allow arbitrary Python code to run, including
* perhaps calls to this function. To prevent
* deadlock, we need to release head_mutex before
* the decref.
*/
PyObject *old_exc = tstate->async_exc;
Py_XINCREF(exc);
tstate->async_exc = exc;
HEAD_UNLOCK(runtime);

Py_XDECREF(old_exc);
_PyEval_SignalAsyncExc(tstate);
return 1;
}
HEAD_UNLOCK(runtime);
return 0;
Expand Down

0 comments on commit 56bfdeb

Please sign in to comment.