Skip to content

Commit eaacaff

Browse files
committed
pythongh-112175: Add eval_breaker to PyThreadState
1 parent ef3ceab commit eaacaff

18 files changed

Lines changed: 257 additions & 163 deletions

‎Include/cpython/pystate.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ struct _ts {
6868
PyThreadState *next;
6969
PyInterpreterState *interp;
7070

71+
uintptr_t eval_breaker;
72+
7173
struct {
7274
/* Has been initialized to a safe state.
7375

‎Include/internal/pycore_ceval.h‎

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ PyAPI_FUNC(int) _PyEval_MakePendingCalls(PyThreadState *);
4242

4343
extern void _Py_FinishPendingCalls(PyThreadState *tstate);
4444
extern void _PyEval_InitState(PyInterpreterState *);
45-
extern void _PyEval_SignalReceived(PyInterpreterState *interp);
45+
extern void _PyEval_SignalReceived(void);
4646

4747
// bitwise flags:
4848
#define _Py_PENDING_MAINTHREADONLY 1
@@ -55,7 +55,6 @@ PyAPI_FUNC(int) _PyEval_AddPendingCall(
5555
void *arg,
5656
int flags);
5757

58-
extern void _PyEval_SignalAsyncExc(PyInterpreterState *interp);
5958
#ifdef HAVE_FORK
6059
extern PyStatus _PyEval_ReInitThreads(PyThreadState *tstate);
6160
#endif
@@ -200,40 +199,6 @@ int _PyEval_UnpackIterable(PyThreadState *tstate, PyObject *v, int argcnt, int a
200199
void _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame);
201200

202201

203-
#define _PY_GIL_DROP_REQUEST_BIT 0
204-
#define _PY_SIGNALS_PENDING_BIT 1
205-
#define _PY_CALLS_TO_DO_BIT 2
206-
#define _PY_ASYNC_EXCEPTION_BIT 3
207-
#define _PY_GC_SCHEDULED_BIT 4
208-
#define _PY_EVAL_PLEASE_STOP_BIT 5
209-
210-
/* Reserve a few bits for future use */
211-
#define _PY_EVAL_EVENTS_BITS 8
212-
#define _PY_EVAL_EVENTS_MASK ((1 << _PY_EVAL_EVENTS_BITS)-1)
213-
214-
static inline void
215-
_Py_set_eval_breaker_bit(PyInterpreterState *interp, uint32_t bit, uint32_t set)
216-
{
217-
assert(set == 0 || set == 1);
218-
uintptr_t to_set = set << bit;
219-
uintptr_t mask = ((uintptr_t)1) << bit;
220-
uintptr_t old = _Py_atomic_load_uintptr(&interp->ceval.eval_breaker);
221-
if ((old & mask) == to_set) {
222-
return;
223-
}
224-
uintptr_t new;
225-
do {
226-
new = (old & ~mask) | to_set;
227-
} while (!_Py_atomic_compare_exchange_uintptr(&interp->ceval.eval_breaker, &old, new));
228-
}
229-
230-
static inline bool
231-
_Py_eval_breaker_bit_is_set(PyInterpreterState *interp, int32_t bit)
232-
{
233-
return _Py_atomic_load_uintptr_relaxed(&interp->ceval.eval_breaker) & (((uintptr_t)1) << bit);
234-
}
235-
236-
237202
#ifdef __cplusplus
238203
}
239204
#endif

‎Include/internal/pycore_ceval_state.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ struct _ceval_state {
8282
* the fast path in the eval loop.
8383
* It is by far the hottest field in this struct and
8484
* should be placed at the beginning. */
85-
uintptr_t eval_breaker;
85+
uintptr_t interp_eval_breaker;
8686
/* Avoid false sharing */
8787
int64_t padding[7];
8888
int recursion_limit;

‎Include/internal/pycore_gc.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ extern void _PySlice_ClearCache(_PyFreeListState *state);
287287
extern void _PyDict_ClearFreeList(_PyFreeListState *state, int is_finalization);
288288
extern void _PyAsyncGen_ClearFreeLists(_PyFreeListState *state, int is_finalization);
289289
extern void _PyContext_ClearFreeList(_PyFreeListState *state, int is_finalization);
290-
extern void _Py_ScheduleGC(PyInterpreterState *interp);
290+
extern void _Py_ScheduleGC(PyThreadState *interp);
291291
extern void _Py_RunGC(PyThreadState *tstate);
292292

293293
#ifdef __cplusplus

‎Include/internal/pycore_pystate.h‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,41 @@ static inline _PyFreeListState* _PyFreeListState_GET(void)
282282
#endif
283283
}
284284

285+
#define _PY_GIL_DROP_REQUEST_BIT (1U << 0)
286+
#define _PY_SIGNALS_PENDING_BIT (1U << 1)
287+
#define _PY_CALLS_TO_DO_BIT (1U << 2)
288+
#define _PY_ASYNC_EXCEPTION_BIT (1U << 3)
289+
#define _PY_GC_SCHEDULED_BIT (1U << 4)
290+
#define _PY_EVAL_PLEASE_STOP_BIT (1U << 5)
291+
292+
/* Reserve a few bits for future use */
293+
#define _PY_EVAL_EVENTS_BITS 8
294+
#define _PY_EVAL_EVENTS_MASK ((1U << _PY_EVAL_EVENTS_BITS)-1)
295+
296+
static inline void
297+
_PyThreadState_Signal(PyThreadState *tstate, uintptr_t bit)
298+
{
299+
_Py_atomic_or_uintptr(&tstate->eval_breaker, bit);
300+
}
301+
302+
static inline void
303+
_PyThreadState_Unsignal(PyThreadState *tstate, uintptr_t bit)
304+
{
305+
_Py_atomic_and_uintptr(&tstate->eval_breaker, ~bit);
306+
}
307+
308+
static inline int
309+
_PyThreadState_IsSignalled(PyThreadState *tstate, uintptr_t bit)
310+
{
311+
uintptr_t b = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker);
312+
return (b & bit) != 0;
313+
}
314+
315+
// Free-threaded builds use these functions to set or unset a bit on all
316+
// threads in the given interpreter.
317+
void _PyInterpreterState_SignalAll(PyInterpreterState *interp, uintptr_t bit);
318+
void _PyInterpreterState_UnsignalAll(PyInterpreterState *interp, uintptr_t bit);
319+
285320
#ifdef __cplusplus
286321
}
287322
#endif

‎Include/internal/pycore_runtime.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,9 @@ typedef struct pyruntimestate {
191191
int64_t next_id;
192192
} interpreters;
193193

194+
/* Platform-specific identifier and PyThreadStates for the main thread. */
194195
unsigned long main_thread;
196+
PyThreadState *main_tstate;
195197

196198
/* ---------- IMPORTANT ---------------------------
197199
The fields above this line are declared as early as

‎Modules/signalmodule.c‎

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -276,11 +276,7 @@ trip_signal(int sig_num)
276276
cleared in PyErr_CheckSignals() before .tripped. */
277277
_Py_atomic_store_int(&is_tripped, 1);
278278

279-
/* Signals are always handled by the main interpreter */
280-
PyInterpreterState *interp = _PyInterpreterState_Main();
281-
282-
/* Notify ceval.c */
283-
_PyEval_SignalReceived(interp);
279+
_PyEval_SignalReceived();
284280

285281
/* And then write to the wakeup fd *after* setting all the globals and
286282
doing the _PyEval_SignalReceived. We used to write to the wakeup fd
@@ -303,6 +299,7 @@ trip_signal(int sig_num)
303299

304300
int fd = wakeup.fd;
305301
if (fd != INVALID_FD) {
302+
PyInterpreterState *interp = _PyInterpreterState_Main();
306303
unsigned char byte = (unsigned char)sig_num;
307304
#ifdef MS_WINDOWS
308305
if (wakeup.use_send) {
@@ -1770,8 +1767,8 @@ PyErr_CheckSignals(void)
17701767
Python code to ensure signals are handled. Checking for the GC here
17711768
allows long running native code to clean cycles created using the C-API
17721769
even if it doesn't run the evaluation loop */
1773-
if (_Py_eval_breaker_bit_is_set(tstate->interp, _PY_GC_SCHEDULED_BIT)) {
1774-
_Py_set_eval_breaker_bit(tstate->interp, _PY_GC_SCHEDULED_BIT, 0);
1770+
if (_PyThreadState_IsSignalled(tstate, _PY_GC_SCHEDULED_BIT)) {
1771+
_PyThreadState_Unsignal(tstate, _PY_GC_SCHEDULED_BIT);
17751772
_Py_RunGC(tstate);
17761773
}
17771774

‎Python/bytecodes.c‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
#include "Python.h"
1010
#include "pycore_abstract.h" // _PyIndex_Check()
11-
#include "pycore_ceval.h" // _PyEval_SignalAsyncExc()
1211
#include "pycore_code.h"
1312
#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS
1413
#include "pycore_function.h"
@@ -144,7 +143,7 @@ dummy_func(
144143
TIER_ONE_ONLY
145144
assert(frame == tstate->current_frame);
146145
uintptr_t global_version =
147-
_Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) &
146+
_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) &
148147
~_PY_EVAL_EVENTS_MASK;
149148
uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version;
150149
assert((code_version & 255) == 0);
@@ -166,14 +165,14 @@ dummy_func(
166165
DEOPT_IF(_Py_emscripten_signal_clock == 0);
167166
_Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING;
168167
#endif
169-
uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker);
168+
uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker);
170169
uintptr_t version = _PyFrame_GetCode(frame)->_co_instrumentation_version;
171170
assert((version & _PY_EVAL_EVENTS_MASK) == 0);
172171
DEOPT_IF(eval_breaker != version);
173172
}
174173

175174
inst(INSTRUMENTED_RESUME, (--)) {
176-
uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & ~_PY_EVAL_EVENTS_MASK;
175+
uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & ~_PY_EVAL_EVENTS_MASK;
177176
uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version;
178177
if (code_version != global_version) {
179178
if (_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp)) {

‎Python/ceval.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include "Python.h"
66
#include "pycore_abstract.h" // _PyIndex_Check()
77
#include "pycore_call.h" // _PyObject_CallNoArgs()
8-
#include "pycore_ceval.h" // _PyEval_SignalAsyncExc()
8+
#include "pycore_ceval.h"
99
#include "pycore_code.h"
1010
#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS
1111
#include "pycore_function.h"

0 commit comments

Comments
 (0)