Skip to content

Commit f13c5c8

Browse files
bpo-33608: Factor out a private, per-interpreter _Py_AddPendingCall(). (gh-12360)
This is effectively an un-revert of #11617 and #12024 (reverted in #12159). Portions of those were merged in other PRs (with lower risk) and this represents the remainder. Note that I found 3 different bugs in the original PRs and have fixed them here.
1 parent 4423504 commit f13c5c8

File tree

11 files changed

+151
-108
lines changed

11 files changed

+151
-108
lines changed

Include/ceval.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ PyAPI_FUNC(Py_ssize_t) _PyEval_RequestCodeExtraIndex(freefunc);
221221
#ifndef Py_LIMITED_API
222222
PyAPI_FUNC(int) _PyEval_SliceIndex(PyObject *, Py_ssize_t *);
223223
PyAPI_FUNC(int) _PyEval_SliceIndexNotNone(PyObject *, Py_ssize_t *);
224-
PyAPI_FUNC(void) _PyEval_SignalAsyncExc(void);
224+
PyAPI_FUNC(void) _PyEval_SignalAsyncExc(PyInterpreterState *);
225225
#endif
226226

227227
/* Masks and values used by FORMAT_VALUE opcode. */

Include/internal/pycore_ceval.h

+13-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ extern "C" {
1111
#include "pycore_atomic.h"
1212
#include "pythread.h"
1313

14-
PyAPI_FUNC(void) _Py_FinishPendingCalls(void);
14+
struct _is; // See PyInterpreterState in cpython/pystate.h.
15+
16+
PyAPI_FUNC(int) _Py_AddPendingCall(struct _is*, unsigned long, int (*)(void *), void *);
17+
PyAPI_FUNC(int) _Py_MakePendingCalls(struct _is*);
18+
PyAPI_FUNC(void) _Py_FinishPendingCalls(struct _is*);
1519

1620
struct _pending_calls {
1721
int finishing;
@@ -24,13 +28,21 @@ struct _pending_calls {
2428
int async_exc;
2529
#define NPENDINGCALLS 32
2630
struct {
31+
unsigned long thread_id;
2732
int (*func)(void *);
2833
void *arg;
2934
} calls[NPENDINGCALLS];
3035
int first;
3136
int last;
3237
};
3338

39+
struct _ceval_interpreter_state {
40+
/* This single variable consolidates all requests to break out of
41+
the fast path in the eval loop. */
42+
_Py_atomic_int eval_breaker;
43+
struct _pending_calls pending;
44+
};
45+
3446
#include "pycore_gil.h"
3547

3648
struct _ceval_runtime_state {
@@ -41,12 +53,8 @@ struct _ceval_runtime_state {
4153
c_tracefunc. This speeds up the if statement in
4254
PyEval_EvalFrameEx() after fast_next_opcode. */
4355
int tracing_possible;
44-
/* This single variable consolidates all requests to break out of
45-
the fast path in the eval loop. */
46-
_Py_atomic_int eval_breaker;
4756
/* Request for dropping the GIL */
4857
_Py_atomic_int gil_drop_request;
49-
struct _pending_calls pending;
5058
/* Request for checking signals. */
5159
_Py_atomic_int signals_pending;
5260
struct _gil_runtime_state gil;

Include/internal/pycore_pystate.h

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ extern "C" {
1212
#include "pystate.h"
1313
#include "pythread.h"
1414

15+
#include "pycore_atomic.h"
1516
#include "pycore_ceval.h"
1617
#include "pycore_pathconfig.h"
1718
#include "pycore_pymem.h"
@@ -83,6 +84,8 @@ struct _is {
8384
PyObject *pyexitmodule;
8485

8586
uint64_t tstate_next_unique_id;
87+
88+
struct _ceval_interpreter_state ceval;
8689
};
8790

8891
PyAPI_FUNC(struct _is*) _PyInterpreterState_LookUpID(PY_INT64_T);

Lib/test/test_capi.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ def pendingcalls_wait(self, l, n, context = None):
373373
def test_pendingcalls_threaded(self):
374374

375375
#do every callback on a separate thread
376-
n = 32 #total callbacks
376+
n = 32 #total callbacks (see NPENDINGCALLS in pycore_ceval.h)
377377
threads = []
378378
class foo(object):pass
379379
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
@@ -2445,6 +2445,7 @@ pending_threadfunc(PyObject *self, PyObject *arg)
24452445
Py_INCREF(callable);
24462446

24472447
Py_BEGIN_ALLOW_THREADS
2448+
/* XXX Use the internal _Py_AddPendingCall(). */
24482449
r = Py_AddPendingCall(&_pending_callback, callable);
24492450
Py_END_ALLOW_THREADS
24502451

Modules/signalmodule.c

+9-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <process.h>
2020
#endif
2121
#endif
22+
#include "internal/pycore_pystate.h"
2223

2324
#ifdef HAVE_SIGNAL_H
2425
#include <signal.h>
@@ -295,8 +296,10 @@ trip_signal(int sig_num)
295296
{
296297
/* Py_AddPendingCall() isn't signal-safe, but we
297298
still use it for this exceptional case. */
298-
Py_AddPendingCall(report_wakeup_send_error,
299-
(void *)(intptr_t) last_error);
299+
_Py_AddPendingCall(_PyRuntime.interpreters.main,
300+
main_thread,
301+
report_wakeup_send_error,
302+
(void *)(intptr_t) last_error);
300303
}
301304
}
302305
}
@@ -313,8 +316,10 @@ trip_signal(int sig_num)
313316
{
314317
/* Py_AddPendingCall() isn't signal-safe, but we
315318
still use it for this exceptional case. */
316-
Py_AddPendingCall(report_wakeup_write_error,
317-
(void *)(intptr_t)errno);
319+
_Py_AddPendingCall(_PyRuntime.interpreters.main,
320+
main_thread,
321+
report_wakeup_write_error,
322+
(void *)(intptr_t)errno);
318323
}
319324
}
320325
}

0 commit comments

Comments
 (0)