Skip to content

Commit 6a150bc

Browse files
bpo-33608: Factor out a private, per-interpreter _Py_AddPendingCall(). (gh-13714)
1 parent 218e47b commit 6a150bc

File tree

10 files changed

+320
-186
lines changed

10 files changed

+320
-186
lines changed

Include/internal/pycore_ceval.h

+8-5
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,22 @@ extern "C" {
1212
#include "pycore_pystate.h"
1313
#include "pythread.h"
1414

15-
PyAPI_FUNC(void) _Py_FinishPendingCalls(_PyRuntimeState *runtime);
1615
PyAPI_FUNC(void) _PyEval_Initialize(struct _ceval_runtime_state *);
1716
PyAPI_FUNC(void) _PyEval_FiniThreads(
18-
struct _ceval_runtime_state *ceval);
17+
struct _ceval_runtime_state *);
1918
PyAPI_FUNC(void) _PyEval_SignalReceived(
20-
struct _ceval_runtime_state *ceval);
19+
struct _ceval_runtime_state *);
2120
PyAPI_FUNC(int) _PyEval_AddPendingCall(
2221
PyThreadState *tstate,
23-
struct _ceval_runtime_state *ceval,
22+
struct _ceval_runtime_state *,
23+
struct _ceval_interpreter_state *,
24+
unsigned long thread_id,
2425
int (*func)(void *),
2526
void *arg);
27+
PyAPI_FUNC(void) _PyEval_FinishPendingCalls(PyInterpreterState *);
2628
PyAPI_FUNC(void) _PyEval_SignalAsyncExc(
27-
struct _ceval_runtime_state *ceval);
29+
struct _ceval_runtime_state *,
30+
struct _ceval_interpreter_state *);
2831
PyAPI_FUNC(void) _PyEval_ReInitThreads(
2932
_PyRuntimeState *runtime);
3033

Include/internal/pycore_pystate.h

+10-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct pyruntimestate;
2525

2626
/* ceval state */
2727

28-
struct _pending_calls {
28+
struct _ceval_pending_calls {
2929
int finishing;
3030
PyThread_type_lock lock;
3131
/* Request for running pending calls. */
@@ -36,6 +36,7 @@ struct _pending_calls {
3636
int async_exc;
3737
#define NPENDINGCALLS 32
3838
struct {
39+
unsigned long thread_id;
3940
int (*func)(void *);
4041
void *arg;
4142
} calls[NPENDINGCALLS];
@@ -53,15 +54,21 @@ struct _ceval_runtime_state {
5354
int tracing_possible;
5455
/* This single variable consolidates all requests to break out of
5556
the fast path in the eval loop. */
57+
// XXX This can move to _ceval_interpreter_state once all parts
58+
// from COMPUTE_EVAL_BREAKER have moved under PyInterpreterState.
5659
_Py_atomic_int eval_breaker;
5760
/* Request for dropping the GIL */
5861
_Py_atomic_int gil_drop_request;
59-
struct _pending_calls pending;
6062
/* Request for checking signals. */
6163
_Py_atomic_int signals_pending;
6264
struct _gil_runtime_state gil;
6365
};
6466

67+
struct _ceval_interpreter_state {
68+
struct _ceval_pending_calls pending;
69+
};
70+
71+
6572
/* interpreter state */
6673

6774
typedef PyObject* (*_PyFrameEvalFunction)(struct _frame *, int);
@@ -136,6 +143,7 @@ struct _is {
136143

137144
uint64_t tstate_next_unique_id;
138145

146+
struct _ceval_interpreter_state ceval;
139147
struct _warnings_runtime_state warnings;
140148

141149
PyObject *audit_hooks;

Lib/test/test_capi.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ def pendingcalls_wait(self, l, n, context = None):
431431
def test_pendingcalls_threaded(self):
432432

433433
#do every callback on a separate thread
434-
n = 32 #total callbacks
434+
n = 32 #total callbacks (see NPENDINGCALLS in pycore_ceval.h)
435435
threads = []
436436
class foo(object):pass
437437
context = foo()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
We added a new internal _Py_AddPendingCall() that operates relative to the
2+
provided interpreter. This allows us to use the existing implementation to
3+
ask another interpreter to do work that cannot be done in the current
4+
interpreter, like decref an object the other interpreter owns. The existing
5+
Py_AddPendingCall() only operates relative to the main interpreter.

Modules/_testcapimodule.c

+1
Original file line numberDiff line numberDiff line change
@@ -2677,6 +2677,7 @@ pending_threadfunc(PyObject *self, PyObject *arg)
26772677
Py_INCREF(callable);
26782678

26792679
Py_BEGIN_ALLOW_THREADS
2680+
/* XXX Use the internal _Py_AddPendingCall(). */
26802681
r = Py_AddPendingCall(&_pending_callback, callable);
26812682
Py_END_ALLOW_THREADS
26822683

Modules/signalmodule.c

+10-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <process.h>
2222
#endif
2323
#endif
24+
#include "internal/pycore_pystate.h"
2425

2526
#ifdef HAVE_SIGNAL_H
2627
#include <signal.h>
@@ -259,6 +260,7 @@ trip_signal(int sig_num)
259260
/* Notify ceval.c */
260261
_PyRuntimeState *runtime = &_PyRuntime;
261262
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
263+
PyInterpreterState *interp = runtime->interpreters.main;
262264
_PyEval_SignalReceived(&runtime->ceval);
263265

264266
/* And then write to the wakeup fd *after* setting all the globals and
@@ -299,7 +301,10 @@ trip_signal(int sig_num)
299301
{
300302
/* Py_AddPendingCall() isn't signal-safe, but we
301303
still use it for this exceptional case. */
302-
_PyEval_AddPendingCall(tstate, &runtime->ceval,
304+
_PyEval_AddPendingCall(tstate,
305+
&runtime->ceval,
306+
&interp->ceval,
307+
runtime->main_thread,
303308
report_wakeup_send_error,
304309
(void *)(intptr_t) last_error);
305310
}
@@ -318,7 +323,10 @@ trip_signal(int sig_num)
318323
{
319324
/* Py_AddPendingCall() isn't signal-safe, but we
320325
still use it for this exceptional case. */
321-
_PyEval_AddPendingCall(tstate, &runtime->ceval,
326+
_PyEval_AddPendingCall(tstate,
327+
&runtime->ceval,
328+
&interp->ceval,
329+
runtime->main_thread,
322330
report_wakeup_write_error,
323331
(void *)(intptr_t)errno);
324332
}

0 commit comments

Comments
 (0)