| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -167,7 +167,7 @@ typedef struct { | |||
| 167 | 167 | PyObject *co_weakreflist; /* to support weakrefs to code objects */ \ | |
| 168 | 168 | _PyExecutorArray *co_executors; /* executors from optimizer */ \ | |
| 169 | 169 | _PyCoCached *_co_cached; /* cached co_* attributes */ \ | |
| 170 | - uint64_t _co_instrumentation_version; /* current instrumentation version */ \ | ||
| 170 | + uintptr_t _co_instrumentation_version; /* current instrumentation version */ \ | ||
| 171 | 171 | _PyCoMonitoringData *_co_monitoring; /* Monitoring data */ \ | |
| 172 | 172 | int _co_firsttraceable; /* index of first traceable instruction */ \ | |
| 173 | 173 | /* Scratch space for extra data relating to the code object. \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -193,6 +193,39 @@ int _PyEval_UnpackIterable(PyThreadState *tstate, PyObject *v, int argcnt, int a | |||
| 193 | 193 | void _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame); | |
| 194 | 194 | ||
| 195 | 195 | ||
| 196 | + #define _PY_GIL_DROP_REQUEST_BIT 0 | ||
| 197 | + #define _PY_SIGNALS_PENDING_BIT 1 | ||
| 198 | + #define _PY_CALLS_TO_DO_BIT 2 | ||
| 199 | + #define _PY_ASYNC_EXCEPTION_BIT 3 | ||
| 200 | + #define _PY_GC_SCHEDULED_BIT 4 | ||
| 201 | + | ||
| 202 | + /* Reserve a few bits for future use */ | ||
| 203 | + #define _PY_EVAL_EVENTS_BITS 8 | ||
| 204 | + #define _PY_EVAL_EVENTS_MASK ((1 << _PY_EVAL_EVENTS_BITS)-1) | ||
| 205 | + | ||
| 206 | + static inline void | ||
| 207 | + _Py_set_eval_breaker_bit(PyInterpreterState *interp, uint32_t bit, uint32_t set) | ||
| 208 | + { | ||
| 209 | + assert(set == 0 || set == 1); | ||
| 210 | + uintptr_t to_set = set << bit; | ||
| 211 | + uintptr_t mask = ((uintptr_t)1) << bit; | ||
| 212 | + uintptr_t old = _Py_atomic_load_uintptr(&interp->ceval.eval_breaker); | ||
| 213 | + if ((old & mask) == to_set) { | ||
| 214 | + return; | ||
| 215 | + } | ||
| 216 | + uintptr_t new; | ||
| 217 | + do { | ||
| 218 | + new = (old & ~mask) | to_set; | ||
| 219 | + } while (!_Py_atomic_compare_exchange_uintptr(&interp->ceval.eval_breaker, &old, new)); | ||
| 220 | + } | ||
| 221 | + | ||
| 222 | + static inline bool | ||
| 223 | + _Py_eval_breaker_bit_is_set(PyInterpreterState *interp, int32_t bit) | ||
| 224 | + { | ||
| 225 | + return _Py_atomic_load_uintptr_relaxed(&interp->ceval.eval_breaker) & (((uintptr_t)1) << bit); | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + | ||
| 196 | 229 | #ifdef __cplusplus | |
| 197 | 230 | } | |
| 198 | 231 | #endif | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,11 +17,7 @@ struct _pending_calls { | |||
| 17 | 17 | int busy; | |
| 18 | 18 | PyThread_type_lock lock; | |
| 19 | 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; | ||
| 20 | + int32_t calls_to_do; | ||
| 25 | 21 | #define NPENDINGCALLS 32 | |
| 26 | 22 | struct _pending_call { | |
| 27 | 23 | _Py_pending_call_func func; | |
@@ -62,11 +58,6 @@ struct _ceval_runtime_state { | |||
| 62 | 58 | int _not_used; | |
| 63 | 59 | #endif | |
| 64 | 60 | } perf; | |
| 65 | - /* Request for checking signals. It is shared by all interpreters (see | ||
| 66 | - bpo-40513). Any thread of any interpreter can receive a signal, but only | ||
| 67 | - the main thread of the main interpreter can handle signals: see | ||
| 68 | - _Py_ThreadCanHandleSignals(). */ | ||
| 69 | - _Py_atomic_int signals_pending; | ||
| 70 | 61 | /* Pending calls to be made only on the main thread. */ | |
| 71 | 62 | struct _pending_calls pending_mainthread; | |
| 72 | 63 | }; | |
@@ -87,14 +78,12 @@ struct _ceval_state { | |||
| 87 | 78 | * the fast path in the eval loop. | |
| 88 | 79 | * It is by far the hottest field in this struct and | |
| 89 | 80 | * should be placed at the beginning. */ | |
| 90 | - _Py_atomic_int eval_breaker; | ||
| 91 | - /* Request for dropping the GIL */ | ||
| 92 | - _Py_atomic_int gil_drop_request; | ||
| 81 | + uintptr_t eval_breaker; | ||
| 82 | + /* Avoid false sharing */ | ||
| 83 | + int64_t padding[7]; | ||
| 93 | 84 | int recursion_limit; | |
| 94 | 85 | struct _gil_runtime_state *gil; | |
| 95 | 86 | int own_gil; | |
| 96 | - /* The GC is ready to be executed */ | ||
| 97 | - _Py_atomic_int gc_scheduled; | ||
| 98 | 87 | struct _pending_calls pending; | |
| 99 | 88 | }; | |
| 100 | 89 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -67,8 +67,7 @@ struct _is { | |||
| 67 | 67 | int _initialized; | |
| 68 | 68 | int finalizing; | |
| 69 | 69 | ||
| 70 | - uint64_t monitoring_version; | ||
| 71 | - uint64_t last_restart_version; | ||
| 70 | + uintptr_t last_restart_version; | ||
| 72 | 71 | struct pythreads { | |
| 73 | 72 | uint64_t next_unique_id; | |
| 74 | 73 | /* The linked list of threads, newest first. */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + The internal eval_breaker and supporting flags, plus the monitoring version | ||
| 2 | + have been merged into a single atomic integer to speed up checks. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -24,6 +24,7 @@ | |||
| 24 | 24 | */ | |
| 25 | 25 | ||
| 26 | 26 | #include "Python.h" | |
| 27 | + #include "pycore_ceval.h" // _Py_set_eval_breaker_bit() | ||
| 27 | 28 | #include "pycore_context.h" | |
| 28 | 29 | #include "pycore_dict.h" // _PyDict_MaybeUntrack() | |
| 29 | 30 | #include "pycore_initconfig.h" | |
@@ -2274,11 +2275,7 @@ _Py_ScheduleGC(PyInterpreterState *interp) | |||
| 2274 | 2275 | if (gcstate->collecting == 1) { | |
| 2275 | 2276 | return; | |
| 2276 | 2277 | } | |
| 2277 | - struct _ceval_state *ceval = &interp->ceval; | ||
| 2278 | - if (!_Py_atomic_load_relaxed(&ceval->gc_scheduled)) { | ||
| 2279 | - _Py_atomic_store_relaxed(&ceval->gc_scheduled, 1); | ||
| 2280 | - _Py_atomic_store_relaxed(&ceval->eval_breaker, 1); | ||
| 2281 | - } | ||
| 2278 | + _Py_set_eval_breaker_bit(interp, _PY_GC_SCHEDULED_BIT, 1); | ||
| 2282 | 2279 | } | |
| 2283 | 2280 | ||
| 2284 | 2281 | void | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1767,9 +1767,8 @@ PyErr_CheckSignals(void) | |||
| 1767 | 1767 | Python code to ensure signals are handled. Checking for the GC here | |
| 1768 | 1768 | allows long running native code to clean cycles created using the C-API | |
| 1769 | 1769 | even if it doesn't run the evaluation loop */ | |
| 1770 | - struct _ceval_state *interp_ceval_state = &tstate->interp->ceval; | ||
| 1771 | - if (_Py_atomic_load_relaxed(&interp_ceval_state->gc_scheduled)) { | ||
| 1772 | - _Py_atomic_store_relaxed(&interp_ceval_state->gc_scheduled, 0); | ||
| 1770 | + if (_Py_eval_breaker_bit_is_set(tstate->interp, _PY_GC_SCHEDULED_BIT)) { | ||
| 1771 | + _Py_set_eval_breaker_bit(tstate->interp, _PY_GC_SCHEDULED_BIT, 0); | ||
| 1773 | 1772 | _Py_RunGC(tstate); | |
| 1774 | 1773 | } | |
| 1775 | 1774 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -136,7 +136,12 @@ dummy_func( | |||
| 136 | 136 | inst(RESUME, (--)) { | |
| 137 | 137 | TIER_ONE_ONLY | |
| 138 | 138 | assert(frame == tstate->current_frame); | |
| 139 | - if (_PyFrame_GetCode(frame)->_co_instrumentation_version != tstate->interp->monitoring_version) { | ||
| 139 | + uintptr_t global_version = | ||
| 140 | + _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & | ||
| 141 | + ~_PY_EVAL_EVENTS_MASK; | ||
| 142 | + uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version; | ||
| 143 | + assert((code_version & 255) == 0); | ||
| 144 | + if (code_version != global_version) { | ||
| 140 | 145 | int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); | |
| 141 | 146 | ERROR_IF(err, error); | |
| 142 | 147 | next_instr--; | |
@@ -154,17 +159,16 @@ dummy_func( | |||
| 154 | 159 | DEOPT_IF(_Py_emscripten_signal_clock == 0); | |
| 155 | 160 | _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; | |
| 156 | 161 | #endif | |
| 157 | - /* Possibly combine these two checks */ | ||
| 158 | - DEOPT_IF(_PyFrame_GetCode(frame)->_co_instrumentation_version != tstate->interp->monitoring_version); | ||
| 159 | - DEOPT_IF(_Py_atomic_load_relaxed_int32(&tstate->interp->ceval.eval_breaker)); | ||
| 162 | + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker); | ||
| 163 | + uintptr_t version = _PyFrame_GetCode(frame)->_co_instrumentation_version; | ||
| 164 | + assert((version & _PY_EVAL_EVENTS_MASK) == 0); | ||
| 165 | + DEOPT_IF(eval_breaker != version); | ||
| 160 | 166 | } | |
| 161 | 167 | ||
| 162 | 168 | inst(INSTRUMENTED_RESUME, (--)) { | |
| 163 | - /* Possible performance enhancement: | ||
| 164 | - * We need to check the eval breaker anyway, can we | ||
| 165 | - * combine the instrument verison check and the eval breaker test? | ||
| 166 | - */ | ||
| 167 | - if (_PyFrame_GetCode(frame)->_co_instrumentation_version != tstate->interp->monitoring_version) { | ||
| 169 | + uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & ~_PY_EVAL_EVENTS_MASK; | ||
| 170 | + uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version; | ||
| 171 | + if (code_version != global_version) { | ||
| 168 | 172 | if (_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp)) { | |
| 169 | 173 | goto error; | |
| 170 | 174 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments