Skip to content

Commit 7d90c37

Browse files
committed
Merge remote-tracking branch 'upstream/main' into gh-138122-2
2 parents 54be163 + 92972ae commit 7d90c37

23 files changed

+441
-268
lines changed

Doc/c-api/type.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ The following functions and structs are used to create
638638
under the :ref:`limited API <limited-c-api>`.
639639
640640
.. versionchanged:: 3.14
641-
The field :c:member:`~PyTypeObject.tp_vectorcall` can now set
641+
The field :c:member:`~PyTypeObject.tp_vectorcall` can now be set
642642
using :c:data:`Py_tp_vectorcall`. See the field's documentation
643643
for details.
644644

Include/internal/pycore_backoff.h

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,48 @@ extern "C" {
2222
Another use is for the Tier 2 optimizer to decide when to create
2323
a new Tier 2 trace (executor). Again, exponential backoff is used.
2424
25-
The 16-bit counter is structured as a 12-bit unsigned 'value'
26-
and a 4-bit 'backoff' field. When resetting the counter, the
25+
The 16-bit counter is structured as a 13-bit unsigned 'value'
26+
and a 3-bit 'backoff' field. When resetting the counter, the
2727
backoff field is incremented (until it reaches a limit) and the
28-
value is set to a bit mask representing the value 2**backoff - 1.
29-
The maximum backoff is 12 (the number of bits in the value).
28+
value is set to a bit mask representing some prime value - 1.
29+
New values and backoffs for each backoff are calculated once
30+
at compile time and saved to value_and_backoff_next table.
31+
The maximum backoff is 6, since 7 is an UNREACHABLE_BACKOFF.
3032
3133
There is an exceptional value which must not be updated, 0xFFFF.
3234
*/
3335

34-
#define BACKOFF_BITS 4
35-
#define MAX_BACKOFF 12
36-
#define UNREACHABLE_BACKOFF 15
37-
38-
static inline bool
39-
is_unreachable_backoff_counter(_Py_BackoffCounter counter)
40-
{
41-
return counter.value_and_backoff == UNREACHABLE_BACKOFF;
42-
}
36+
#define BACKOFF_BITS 3
37+
#define BACKOFF_MASK 7
38+
#define MAX_BACKOFF 6
39+
#define UNREACHABLE_BACKOFF 7
40+
#define MAX_VALUE 0x1FFF
41+
42+
#define MAKE_VALUE_AND_BACKOFF(value, backoff) \
43+
((value << BACKOFF_BITS) | backoff)
44+
45+
// For previous backoff b we use value x such that
46+
// x + 1 is near to 2**(2*b+1) and x + 1 is prime.
47+
static const uint16_t value_and_backoff_next[] = {
48+
MAKE_VALUE_AND_BACKOFF(1, 1),
49+
MAKE_VALUE_AND_BACKOFF(6, 2),
50+
MAKE_VALUE_AND_BACKOFF(30, 3),
51+
MAKE_VALUE_AND_BACKOFF(126, 4),
52+
MAKE_VALUE_AND_BACKOFF(508, 5),
53+
MAKE_VALUE_AND_BACKOFF(2052, 6),
54+
// We use the same backoff counter for all backoffs >= MAX_BACKOFF.
55+
MAKE_VALUE_AND_BACKOFF(8190, 6),
56+
MAKE_VALUE_AND_BACKOFF(8190, 6),
57+
};
4358

4459
static inline _Py_BackoffCounter
4560
make_backoff_counter(uint16_t value, uint16_t backoff)
4661
{
47-
assert(backoff <= 15);
48-
assert(value <= 0xFFF);
49-
_Py_BackoffCounter result;
50-
result.value_and_backoff = (value << BACKOFF_BITS) | backoff;
51-
return result;
62+
assert(backoff <= UNREACHABLE_BACKOFF);
63+
assert(value <= MAX_VALUE);
64+
return ((_Py_BackoffCounter){
65+
.value_and_backoff = MAKE_VALUE_AND_BACKOFF(value, backoff)
66+
});
5267
}
5368

5469
static inline _Py_BackoffCounter
@@ -62,14 +77,11 @@ forge_backoff_counter(uint16_t counter)
6277
static inline _Py_BackoffCounter
6378
restart_backoff_counter(_Py_BackoffCounter counter)
6479
{
65-
assert(!is_unreachable_backoff_counter(counter));
66-
int backoff = counter.value_and_backoff & 15;
67-
if (backoff < MAX_BACKOFF) {
68-
return make_backoff_counter((1 << (backoff + 1)) - 1, backoff + 1);
69-
}
70-
else {
71-
return make_backoff_counter((1 << MAX_BACKOFF) - 1, MAX_BACKOFF);
72-
}
80+
uint16_t backoff = counter.value_and_backoff & BACKOFF_MASK;
81+
assert(backoff <= MAX_BACKOFF);
82+
return ((_Py_BackoffCounter){
83+
.value_and_backoff = value_and_backoff_next[backoff]
84+
});
7385
}
7486

7587
static inline _Py_BackoffCounter
@@ -113,7 +125,7 @@ trigger_backoff_counter(void)
113125
// as we always end up tracing the loop iteration's
114126
// exhaustion iteration. Which aborts our current tracer.
115127
#define JUMP_BACKWARD_INITIAL_VALUE 4000
116-
#define JUMP_BACKWARD_INITIAL_BACKOFF 12
128+
#define JUMP_BACKWARD_INITIAL_BACKOFF 6
117129
static inline _Py_BackoffCounter
118130
initial_jump_backoff_counter(void)
119131
{
@@ -126,7 +138,7 @@ initial_jump_backoff_counter(void)
126138
* otherwise when a side exit warms up we may construct
127139
* a new trace before the Tier 1 code has properly re-specialized. */
128140
#define SIDE_EXIT_INITIAL_VALUE 4000
129-
#define SIDE_EXIT_INITIAL_BACKOFF 12
141+
#define SIDE_EXIT_INITIAL_BACKOFF 6
130142

131143
static inline _Py_BackoffCounter
132144
initial_temperature_backoff_counter(void)

Include/internal/pycore_critical_section.h

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ extern "C" {
3232
const bool _should_lock_cs = PyList_CheckExact(_orig_seq); \
3333
PyCriticalSection _cs; \
3434
if (_should_lock_cs) { \
35-
_PyCriticalSection_Begin(&_cs, _orig_seq); \
35+
PyCriticalSection_Begin(&_cs, _orig_seq); \
3636
}
3737

3838
# define Py_END_CRITICAL_SECTION_SEQUENCE_FAST() \
@@ -77,10 +77,10 @@ _PyCriticalSection_Resume(PyThreadState *tstate);
7777

7878
// (private) slow path for locking the mutex
7979
PyAPI_FUNC(void)
80-
_PyCriticalSection_BeginSlow(PyCriticalSection *c, PyMutex *m);
80+
_PyCriticalSection_BeginSlow(PyThreadState *tstate, PyCriticalSection *c, PyMutex *m);
8181

8282
PyAPI_FUNC(void)
83-
_PyCriticalSection2_BeginSlow(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2,
83+
_PyCriticalSection2_BeginSlow(PyThreadState *tstate, PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2,
8484
int is_m1_locked);
8585

8686
PyAPI_FUNC(void)
@@ -95,34 +95,30 @@ _PyCriticalSection_IsActive(uintptr_t tag)
9595
}
9696

9797
static inline void
98-
_PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m)
98+
_PyCriticalSection_BeginMutex(PyThreadState *tstate, PyCriticalSection *c, PyMutex *m)
9999
{
100100
if (PyMutex_LockFast(m)) {
101-
PyThreadState *tstate = _PyThreadState_GET();
102101
c->_cs_mutex = m;
103102
c->_cs_prev = tstate->critical_section;
104103
tstate->critical_section = (uintptr_t)c;
105104
}
106105
else {
107-
_PyCriticalSection_BeginSlow(c, m);
106+
_PyCriticalSection_BeginSlow(tstate, c, m);
108107
}
109108
}
110-
#define PyCriticalSection_BeginMutex _PyCriticalSection_BeginMutex
111109

112110
static inline void
113-
_PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op)
111+
_PyCriticalSection_Begin(PyThreadState *tstate, PyCriticalSection *c, PyObject *op)
114112
{
115-
_PyCriticalSection_BeginMutex(c, &op->ob_mutex);
113+
_PyCriticalSection_BeginMutex(tstate, c, &op->ob_mutex);
116114
}
117-
#define PyCriticalSection_Begin _PyCriticalSection_Begin
118115

119116
// Removes the top-most critical section from the thread's stack of critical
120117
// sections. If the new top-most critical section is inactive, then it is
121118
// resumed.
122119
static inline void
123-
_PyCriticalSection_Pop(PyCriticalSection *c)
120+
_PyCriticalSection_Pop(PyThreadState *tstate, PyCriticalSection *c)
124121
{
125-
PyThreadState *tstate = _PyThreadState_GET();
126122
uintptr_t prev = c->_cs_prev;
127123
tstate->critical_section = prev;
128124

@@ -132,7 +128,7 @@ _PyCriticalSection_Pop(PyCriticalSection *c)
132128
}
133129

134130
static inline void
135-
_PyCriticalSection_End(PyCriticalSection *c)
131+
_PyCriticalSection_End(PyThreadState *tstate, PyCriticalSection *c)
136132
{
137133
// If the mutex is NULL, we used the fast path in
138134
// _PyCriticalSection_BeginSlow for locks already held in the top-most
@@ -141,18 +137,17 @@ _PyCriticalSection_End(PyCriticalSection *c)
141137
return;
142138
}
143139
PyMutex_Unlock(c->_cs_mutex);
144-
_PyCriticalSection_Pop(c);
140+
_PyCriticalSection_Pop(tstate, c);
145141
}
146-
#define PyCriticalSection_End _PyCriticalSection_End
147142

148143
static inline void
149-
_PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
144+
_PyCriticalSection2_BeginMutex(PyThreadState *tstate, PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
150145
{
151146
if (m1 == m2) {
152147
// If the two mutex arguments are the same, treat this as a critical
153148
// section with a single mutex.
154149
c->_cs_mutex2 = NULL;
155-
_PyCriticalSection_BeginMutex(&c->_cs_base, m1);
150+
_PyCriticalSection_BeginMutex(tstate, &c->_cs_base, m1);
156151
return;
157152
}
158153

@@ -167,7 +162,6 @@ _PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
167162

168163
if (PyMutex_LockFast(m1)) {
169164
if (PyMutex_LockFast(m2)) {
170-
PyThreadState *tstate = _PyThreadState_GET();
171165
c->_cs_base._cs_mutex = m1;
172166
c->_cs_mutex2 = m2;
173167
c->_cs_base._cs_prev = tstate->critical_section;
@@ -176,24 +170,22 @@ _PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
176170
tstate->critical_section = p;
177171
}
178172
else {
179-
_PyCriticalSection2_BeginSlow(c, m1, m2, 1);
173+
_PyCriticalSection2_BeginSlow(tstate, c, m1, m2, 1);
180174
}
181175
}
182176
else {
183-
_PyCriticalSection2_BeginSlow(c, m1, m2, 0);
177+
_PyCriticalSection2_BeginSlow(tstate, c, m1, m2, 0);
184178
}
185179
}
186-
#define PyCriticalSection2_BeginMutex _PyCriticalSection2_BeginMutex
187180

188181
static inline void
189-
_PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b)
182+
_PyCriticalSection2_Begin(PyThreadState *tstate, PyCriticalSection2 *c, PyObject *a, PyObject *b)
190183
{
191-
_PyCriticalSection2_BeginMutex(c, &a->ob_mutex, &b->ob_mutex);
184+
_PyCriticalSection2_BeginMutex(tstate, c, &a->ob_mutex, &b->ob_mutex);
192185
}
193-
#define PyCriticalSection2_Begin _PyCriticalSection2_Begin
194186

195187
static inline void
196-
_PyCriticalSection2_End(PyCriticalSection2 *c)
188+
_PyCriticalSection2_End(PyThreadState *tstate, PyCriticalSection2 *c)
197189
{
198190
// if mutex1 is NULL, we used the fast path in
199191
// _PyCriticalSection_BeginSlow for mutexes that are already held,
@@ -207,9 +199,8 @@ _PyCriticalSection2_End(PyCriticalSection2 *c)
207199
PyMutex_Unlock(c->_cs_mutex2);
208200
}
209201
PyMutex_Unlock(c->_cs_base._cs_mutex);
210-
_PyCriticalSection_Pop(&c->_cs_base);
202+
_PyCriticalSection_Pop(tstate, &c->_cs_base);
211203
}
212-
#define PyCriticalSection2_End _PyCriticalSection2_End
213204

214205
static inline void
215206
_PyCriticalSection_AssertHeld(PyMutex *mutex)
@@ -251,6 +242,45 @@ _PyCriticalSection_AssertHeldObj(PyObject *op)
251242

252243
#endif
253244
}
245+
246+
#undef Py_BEGIN_CRITICAL_SECTION
247+
# define Py_BEGIN_CRITICAL_SECTION(op) \
248+
{ \
249+
PyCriticalSection _py_cs; \
250+
PyThreadState *_cs_tstate = _PyThreadState_GET(); \
251+
_PyCriticalSection_Begin(_cs_tstate, &_py_cs, _PyObject_CAST(op))
252+
253+
#undef Py_BEGIN_CRITICAL_SECTION_MUTEX
254+
# define Py_BEGIN_CRITICAL_SECTION_MUTEX(mutex) \
255+
{ \
256+
PyCriticalSection _py_cs; \
257+
PyThreadState *_cs_tstate = _PyThreadState_GET(); \
258+
_PyCriticalSection_BeginMutex(_cs_tstate, &_py_cs, mutex)
259+
260+
#undef Py_END_CRITICAL_SECTION
261+
# define Py_END_CRITICAL_SECTION() \
262+
_PyCriticalSection_End(_cs_tstate, &_py_cs); \
263+
}
264+
265+
#undef Py_BEGIN_CRITICAL_SECTION2
266+
# define Py_BEGIN_CRITICAL_SECTION2(a, b) \
267+
{ \
268+
PyCriticalSection2 _py_cs2; \
269+
PyThreadState *_cs_tstate = _PyThreadState_GET(); \
270+
_PyCriticalSection2_Begin(_cs_tstate, &_py_cs2, _PyObject_CAST(a), _PyObject_CAST(b))
271+
272+
#undef Py_BEGIN_CRITICAL_SECTION2_MUTEX
273+
# define Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2) \
274+
{ \
275+
PyCriticalSection2 _py_cs2; \
276+
PyThreadState *_cs_tstate = _PyThreadState_GET(); \
277+
_PyCriticalSection2_BeginMutex(_cs_tstate, &_py_cs2, m1, m2)
278+
279+
#undef Py_END_CRITICAL_SECTION2
280+
# define Py_END_CRITICAL_SECTION2() \
281+
_PyCriticalSection2_End(_cs_tstate, &_py_cs2); \
282+
}
283+
254284
#endif /* Py_GIL_DISABLED */
255285

256286
#ifdef __cplusplus

0 commit comments

Comments
 (0)