Skip to content
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

gh-104812: Run Pending Calls in any Thread #104813

Merged
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6e46450
Clear each pending call when popping it.
ericsnowcurrently May 22, 2023
3b18b7d
Call _Py_FinishPendingCalls() in Py_EndInterpreter().
ericsnowcurrently May 23, 2023
f8b483f
Factor out has_pending_calls().
ericsnowcurrently May 23, 2023
44df0bc
Skip the calls_to_do check in _Py_FinishPendingCalls().
ericsnowcurrently May 23, 2023
e2a0281
Factor out _PyEval_MakePendingCalls().
ericsnowcurrently May 23, 2023
61e5d3e
Explicitly restrict Py_MakePendingCalls() to the main thread.
ericsnowcurrently May 23, 2023
5af09f2
Factor out _make_pending_calls().
ericsnowcurrently May 23, 2023
72cc242
Add the mainthreadonly arg to _PyEval_AddPendingCall().
ericsnowcurrently May 23, 2023
88f7757
Always use the main interpreter for Py_AddPendingCall().
ericsnowcurrently May 23, 2023
d24fafb
Add _PyRuntime.ceval.pending_mainthread.
ericsnowcurrently May 23, 2023
1701fa3
Run per-interpreter pending calls in any thread.
ericsnowcurrently May 23, 2023
c6cfaca
Drop _Py_ThreadCanHandlePendingCalls().
ericsnowcurrently May 23, 2023
dc11024
Expand the handle_eval_breaker comment.
ericsnowcurrently May 25, 2023
6c3d06c
Do not require faulthandler for test.support.threading_helper.start_t…
ericsnowcurrently May 31, 2023
69ff9e6
Add tests.
ericsnowcurrently May 25, 2023
fdde46d
Add a NEWS entry.
ericsnowcurrently Jun 2, 2023
d9924b4
Merge branch 'main' into per-interpreter-pending-calls
ericsnowcurrently Jun 5, 2023
c1fb647
Skip the test if subinterpreters not supported.
ericsnowcurrently Jun 5, 2023
e06b6f7
Adjust UNSIGNAL_PENDING_CALLS().
ericsnowcurrently Jun 6, 2023
2fabab7
Be more careful in make_pending_calls().
ericsnowcurrently Jun 6, 2023
8445fc0
The main thread may be used by subinterpreters.
ericsnowcurrently Jun 6, 2023
a83a321
Factor out _next_pending_call().
ericsnowcurrently Jun 6, 2023
aca2a8c
has_pending_calls() -> maybe_has_pending_calls().
ericsnowcurrently Jun 6, 2023
fbf92e0
Merge branch 'main' into per-interpreter-pending-calls
ericsnowcurrently Jun 6, 2023
93e61c5
Merge branch 'main' into per-interpreter-pending-calls
ericsnowcurrently Jun 8, 2023
3e1bc1f
Merge branch 'main' into per-interpreter-pending-calls
ericsnowcurrently Jun 8, 2023
7b8b8da
Drop a dead import.
ericsnowcurrently Jun 12, 2023
90b3a1f
Clarify some comments.
ericsnowcurrently Jun 12, 2023
4f0068d
Add timeouts.
ericsnowcurrently Jun 12, 2023
d5d7b42
Implement the remaining tests.
ericsnowcurrently Jun 12, 2023
37d41cc
Drop prints.
ericsnowcurrently Jun 12, 2023
fc25a85
Ignore the global variable.
ericsnowcurrently Jun 13, 2023
177f161
Clarify comments.
ericsnowcurrently Jun 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions Include/cpython/ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _P
PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds);
PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void);

PyAPI_FUNC(int) _PyEval_MakePendingCalls(PyThreadState *);

PyAPI_FUNC(Py_ssize_t) PyUnstable_Eval_RequestCodeExtraIndex(freefunc);
// Old name -- remove when this API changes:
_Py_DEPRECATED_EXTERNALLY(3.12) static inline Py_ssize_t
Expand Down
3 changes: 2 additions & 1 deletion Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ PyAPI_FUNC(void) _PyEval_SignalReceived(PyInterpreterState *interp);
PyAPI_FUNC(int) _PyEval_AddPendingCall(
PyInterpreterState *interp,
int (*func)(void *),
void *arg);
void *arg,
int mainthreadonly);
PyAPI_FUNC(void) _PyEval_SignalAsyncExc(PyInterpreterState *interp);
#ifdef HAVE_FORK
extern PyStatus _PyEval_ReInitThreads(PyThreadState *tstate);
Expand Down
38 changes: 20 additions & 18 deletions Include/internal/pycore_ceval_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,24 @@ extern "C" {
#include "pycore_gil.h" // struct _gil_runtime_state


struct _pending_calls {
int busy;
PyThread_type_lock lock;
/* Request for running pending calls. */
_Py_atomic_int calls_to_do;
/* Request for looking at the `async_exc` field of the current
thread state.
Guarded by the GIL. */
int async_exc;
#define NPENDINGCALLS 32
struct _pending_call {
int (*func)(void *);
void *arg;
} calls[NPENDINGCALLS];
int first;
int last;
};

typedef enum {
PERF_STATUS_FAILED = -1, // Perf trampoline is in an invalid state
PERF_STATUS_NO_INIT = 0, // Perf trampoline is not initialized
Expand Down Expand Up @@ -49,6 +67,8 @@ struct _ceval_runtime_state {
the main thread of the main interpreter can handle signals: see
_Py_ThreadCanHandleSignals(). */
_Py_atomic_int signals_pending;
/* Pending calls to be made only on the main thread. */
struct _pending_calls pending_mainthread;
};

#ifdef PY_HAVE_PERF_TRAMPOLINE
Expand All @@ -62,24 +82,6 @@ struct _ceval_runtime_state {
#endif


struct _pending_calls {
int busy;
PyThread_type_lock lock;
/* Request for running pending calls. */
_Py_atomic_int calls_to_do;
/* Request for looking at the `async_exc` field of the current
thread state.
Guarded by the GIL. */
int async_exc;
#define NPENDINGCALLS 32
struct {
int (*func)(void *);
void *arg;
} calls[NPENDINGCALLS];
int first;
int last;
};

struct _ceval_state {
/* This single variable consolidates all requests to break out of
the fast path in the eval loop. */
Expand Down
8 changes: 0 additions & 8 deletions Include/internal/pycore_pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,6 @@ _Py_ThreadCanHandleSignals(PyInterpreterState *interp)
}


/* Only execute pending calls on the main thread. */
static inline int
_Py_ThreadCanHandlePendingCalls(void)
{
return _Py_IsMainThread();
}


/* Variable and static inline functions for in-line access to current thread
and interpreter state */

Expand Down
9 changes: 7 additions & 2 deletions Lib/test/support/threading_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ def join_thread(thread, timeout=None):

@contextlib.contextmanager
def start_threads(threads, unlock=None):
import faulthandler
try:
import faulthandler
except ImportError:
# It isn't supported on subinterpreters yet.
faulthandler = None
threads = list(threads)
started = []
try:
Expand Down Expand Up @@ -147,7 +151,8 @@ def start_threads(threads, unlock=None):
finally:
started = [t for t in started if t.is_alive()]
if started:
faulthandler.dump_traceback(sys.stdout)
if faulthandler is not None:
faulthandler.dump_traceback(sys.stdout)
raise AssertionError('Unable to join %d threads' % len(started))


Expand Down