gh-104812: Run Pending Calls in any Thread (gh-104813) · python/cpython@757b402

16 files changed

lines changed

Original file line numberDiff line numberDiff line change

@@ -22,6 +22,8 @@ PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _P

2222

PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds);

2323

PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void);

2424
25+

PyAPI_FUNC(int) _PyEval_MakePendingCalls(PyThreadState *);

26+
2527

PyAPI_FUNC(Py_ssize_t) PyUnstable_Eval_RequestCodeExtraIndex(freefunc);

2628

// Old name -- remove when this API changes:

2729

_Py_DEPRECATED_EXTERNALLY(3.12) static inline Py_ssize_t

Original file line numberDiff line numberDiff line change

@@ -27,7 +27,8 @@ PyAPI_FUNC(void) _PyEval_SignalReceived(PyInterpreterState *interp);

2727

PyAPI_FUNC(int) _PyEval_AddPendingCall(

2828

PyInterpreterState *interp,

2929

int (*func)(void *),

30-

void *arg);

30+

void *arg,

31+

int mainthreadonly);

3132

PyAPI_FUNC(void) _PyEval_SignalAsyncExc(PyInterpreterState *interp);

3233

#ifdef HAVE_FORK

3334

extern PyStatus _PyEval_ReInitThreads(PyThreadState *tstate);

Original file line numberDiff line numberDiff line change

@@ -13,6 +13,24 @@ extern "C" {

1313

#include "pycore_gil.h" // struct _gil_runtime_state

1414
1515
16+

struct _pending_calls {

17+

int busy;

18+

PyThread_type_lock lock;

19+

/* Request for running pending calls. */

20+

_Py_atomic_int calls_to_do;

21+

/* Request for looking at the `async_exc` field of the current

22+

thread state.

23+

Guarded by the GIL. */

24+

int async_exc;

25+

#define NPENDINGCALLS 32

26+

struct _pending_call {

27+

int (*func)(void *);

28+

void *arg;

29+

} calls[NPENDINGCALLS];

30+

int first;

31+

int last;

32+

};

33+
1634

typedef enum {

1735

PERF_STATUS_FAILED = -1, // Perf trampoline is in an invalid state

1836

PERF_STATUS_NO_INIT = 0, // Perf trampoline is not initialized

@@ -49,6 +67,8 @@ struct _ceval_runtime_state {

4967

the main thread of the main interpreter can handle signals: see

5068

_Py_ThreadCanHandleSignals(). */

5169

_Py_atomic_int signals_pending;

70+

/* Pending calls to be made only on the main thread. */

71+

struct _pending_calls pending_mainthread;

5272

};

5373
5474

#ifdef PY_HAVE_PERF_TRAMPOLINE

@@ -62,24 +82,6 @@ struct _ceval_runtime_state {

6282

#endif

6383
6484
65-

struct _pending_calls {

66-

int busy;

67-

PyThread_type_lock lock;

68-

/* Request for running pending calls. */

69-

_Py_atomic_int calls_to_do;

70-

/* Request for looking at the `async_exc` field of the current

71-

thread state.

72-

Guarded by the GIL. */

73-

int async_exc;

74-

#define NPENDINGCALLS 32

75-

struct {

76-

int (*func)(void *);

77-

void *arg;

78-

} calls[NPENDINGCALLS];

79-

int first;

80-

int last;

81-

};

82-
8385

struct _ceval_state {

8486

/* This single variable consolidates all requests to break out of

8587

the fast path in the eval loop. */

Original file line numberDiff line numberDiff line change

@@ -60,14 +60,6 @@ _Py_ThreadCanHandleSignals(PyInterpreterState *interp)

6060

}

6161
6262
63-

/* Only execute pending calls on the main thread. */

64-

static inline int

65-

_Py_ThreadCanHandlePendingCalls(void)

66-

{

67-

return _Py_IsMainThread();

68-

}

69-
70-
7163

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

7264

and interpreter state */

7365
Original file line numberDiff line numberDiff line change

@@ -115,7 +115,11 @@ def join_thread(thread, timeout=None):

115115
116116

@contextlib.contextmanager

117117

def start_threads(threads, unlock=None):

118-

import faulthandler

118+

try:

119+

import faulthandler

120+

except ImportError:

121+

# It isn't supported on subinterpreters yet.

122+

faulthandler = None

119123

threads = list(threads)

120124

started = []

121125

try:

@@ -147,7 +151,8 @@ def start_threads(threads, unlock=None):

147151

finally:

148152

started = [t for t in started if t.is_alive()]

149153

if started:

150-

faulthandler.dump_traceback(sys.stdout)

154+

if faulthandler is not None:

155+

faulthandler.dump_traceback(sys.stdout)

151156

raise AssertionError('Unable to join %d threads' % len(started))

152157
153158