Skip to content

Commit 56a2076

Browse files
The same pattern of refcounting for stackrefs as in production build
1 parent e0ea1b1 commit 56a2076

File tree

3 files changed

+115
-50
lines changed

3 files changed

+115
-50
lines changed

Include/internal/pycore_stackref.h

Lines changed: 104 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -53,36 +53,53 @@ extern "C" {
5353

5454
#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
5555

56-
#define Py_TAG_BITS 0
56+
#define Py_INT_TAG 3
57+
#define Py_TAG_INVALID 2
58+
#define Py_TAG_REFCNT 1
59+
#define Py_TAG_BITS 3
5760

5861
PyAPI_FUNC(PyObject *) _Py_stackref_get_object(_PyStackRef ref);
5962
PyAPI_FUNC(PyObject *) _Py_stackref_close(_PyStackRef ref, const char *filename, int linenumber);
60-
PyAPI_FUNC(_PyStackRef) _Py_stackref_create(PyObject *obj, const char *filename, int linenumber);
63+
PyAPI_FUNC(_PyStackRef) _Py_stackref_create(PyObject *obj, uint16_t flags, const char *filename, int linenumber);
6164
PyAPI_FUNC(void) _Py_stackref_record_borrow(_PyStackRef ref, const char *filename, int linenumber);
6265
extern void _Py_stackref_associate(PyInterpreterState *interp, PyObject *obj, _PyStackRef ref);
6366

6467
static const _PyStackRef PyStackRef_NULL = { .index = 0 };
65-
static const _PyStackRef PyStackRef_ERROR = { .index = 2 };
68+
static const _PyStackRef PyStackRef_ERROR = { .index = 4 };
6669

67-
// Use the first 3 even numbers for None, True and False.
68-
// Odd numbers are reserved for (tagged) integers
69-
#define PyStackRef_None ((_PyStackRef){ .index = 4 } )
70-
#define PyStackRef_False ((_PyStackRef){ .index = 6 })
71-
#define PyStackRef_True ((_PyStackRef){ .index = 8 })
70+
#define PyStackRef_None ((_PyStackRef){ .index = 8 } )
71+
#define PyStackRef_False ((_PyStackRef){ .index = 12 })
72+
#define PyStackRef_True ((_PyStackRef){ .index = 16 })
7273

73-
#define INITIAL_STACKREF_INDEX 10
74+
#define INITIAL_STACKREF_INDEX 20
7475

7576
static inline _PyStackRef
7677
PyStackRef_Wrap(void *ptr)
7778
{
7879
assert(ptr != NULL);
80+
#ifdef Py_DEBUG
81+
assert(((uint64_t)ptr & Py_TAG_BITS) == 0);
82+
return (_PyStackRef){ .index = ((uint64_t)ptr) | Py_TAG_INVALID };
83+
#else
7984
return (_PyStackRef){ .index = (uint64_t)ptr };
85+
#endif
8086
}
8187

8288
static inline void *
8389
PyStackRef_Unwrap(_PyStackRef ref)
8490
{
91+
#ifdef Py_DEBUG
92+
assert ((ref.index & Py_TAG_BITS) == Py_TAG_INVALID);
93+
return (void *)(ref.index & ~Py_TAG_BITS);
94+
#else
8595
return (void *)(ref.index);
96+
#endif
97+
}
98+
99+
static inline int
100+
PyStackRef_RefcountOnObject(_PyStackRef ref)
101+
{
102+
return (ref.index & Py_TAG_REFCNT) == 0;
86103
}
87104

88105
static inline int
@@ -94,7 +111,7 @@ PyStackRef_IsNull(_PyStackRef ref)
94111
static inline bool
95112
PyStackRef_IsError(_PyStackRef ref)
96113
{
97-
return ref.index == 2;
114+
return ref.index == 4;
98115
}
99116

100117
static inline bool
@@ -125,7 +142,7 @@ PyStackRef_IsNone(_PyStackRef ref)
125142
static inline bool
126143
PyStackRef_IsTaggedInt(_PyStackRef ref)
127144
{
128-
return (ref.index & 1) == 1;
145+
return (ref.index & Py_TAG_BITS) == Py_INT_TAG;
129146
}
130147

131148
static inline PyObject *
@@ -136,51 +153,68 @@ _PyStackRef_AsPyObjectBorrow(_PyStackRef ref, const char *filename, int linenumb
136153
_Py_stackref_record_borrow(ref, filename, linenumber);
137154
return _Py_stackref_get_object(ref);
138155
}
139-
140156
#define PyStackRef_AsPyObjectBorrow(REF) _PyStackRef_AsPyObjectBorrow((REF), __FILE__, __LINE__)
141157

142158
static inline PyObject *
143159
_PyStackRef_AsPyObjectSteal(_PyStackRef ref, const char *filename, int linenumber)
144160
{
145-
return _Py_stackref_close(ref, filename, linenumber);
161+
PyObject *obj = _Py_stackref_close(ref, filename, linenumber);
162+
if (PyStackRef_RefcountOnObject(ref)) {
163+
return obj;
164+
}
165+
return Py_NewRef(obj);
146166
}
147167
#define PyStackRef_AsPyObjectSteal(REF) _PyStackRef_AsPyObjectSteal((REF), __FILE__, __LINE__)
148168

149169
static inline _PyStackRef
150170
_PyStackRef_FromPyObjectNew(PyObject *obj, const char *filename, int linenumber)
151171
{
152-
Py_INCREF(obj);
153-
return _Py_stackref_create(obj, filename, linenumber);
172+
assert(obj != NULL);
173+
uint16_t flags = 0;
174+
if (!_Py_IsImmortal(obj)) {
175+
_Py_INCREF_MORTAL(obj);
176+
} else {
177+
flags = Py_TAG_REFCNT;
178+
}
179+
return _Py_stackref_create(obj, flags, filename, linenumber);
154180
}
155181
#define PyStackRef_FromPyObjectNew(obj) _PyStackRef_FromPyObjectNew(_PyObject_CAST(obj), __FILE__, __LINE__)
156182

157183
static inline _PyStackRef
158184
_PyStackRef_FromPyObjectSteal(PyObject *obj, const char *filename, int linenumber)
159185
{
160-
return _Py_stackref_create(obj, filename, linenumber);
186+
assert(obj != NULL);
187+
uint16_t flags = 0;
188+
if (_Py_IsImmortal(obj)) {
189+
flags = Py_TAG_REFCNT;
190+
}
191+
return _Py_stackref_create(obj, flags, filename, linenumber);
161192
}
162193
#define PyStackRef_FromPyObjectSteal(obj) _PyStackRef_FromPyObjectSteal(_PyObject_CAST(obj), __FILE__, __LINE__)
163194

164195
static inline _PyStackRef
165196
_PyStackRef_FromPyObjectBorrow(PyObject *obj, const char *filename, int linenumber)
166197
{
167-
Py_INCREF(obj);
168-
return _Py_stackref_create(obj, filename, linenumber);
198+
return _Py_stackref_create(obj, Py_TAG_REFCNT, filename, linenumber);
169199
}
170200
#define PyStackRef_FromPyObjectBorrow(obj) _PyStackRef_FromPyObjectBorrow(_PyObject_CAST(obj), __FILE__, __LINE__)
171201

172202
static inline void
173203
_PyStackRef_CLOSE(_PyStackRef ref, const char *filename, int linenumber)
174204
{
205+
assert(!PyStackRef_IsError(ref));
206+
assert(!PyStackRef_IsNull(ref));
175207
if (PyStackRef_IsTaggedInt(ref)) {
176208
return;
177209
}
178210
PyObject *obj = _Py_stackref_close(ref, filename, linenumber);
179-
Py_DECREF(obj);
211+
assert(Py_REFCNT(obj) > 0);
212+
if (PyStackRef_RefcountOnObject(ref)) {
213+
Py_DECREF(obj);
214+
}
180215
}
181216
#define PyStackRef_CLOSE(REF) _PyStackRef_CLOSE((REF), __FILE__, __LINE__)
182217

183-
184218
static inline void
185219
_PyStackRef_XCLOSE(_PyStackRef ref, const char *filename, int linenumber)
186220
{
@@ -196,31 +230,46 @@ static inline _PyStackRef
196230
_PyStackRef_DUP(_PyStackRef ref, const char *filename, int linenumber)
197231
{
198232
assert(!PyStackRef_IsError(ref));
233+
assert(!PyStackRef_IsNull(ref));
199234
if (PyStackRef_IsTaggedInt(ref)) {
200235
return ref;
201236
}
202-
else {
203-
PyObject *obj = _Py_stackref_get_object(ref);
237+
PyObject *obj = _Py_stackref_get_object(ref);
238+
uint16_t flags = 0;
239+
if (PyStackRef_RefcountOnObject(ref)) {
204240
Py_INCREF(obj);
205-
return _Py_stackref_create(obj, filename, linenumber);
241+
} else {
242+
flags = Py_TAG_REFCNT;
206243
}
244+
return _Py_stackref_create(obj, flags, filename, linenumber);
207245
}
208246
#define PyStackRef_DUP(REF) _PyStackRef_DUP(REF, __FILE__, __LINE__)
209247

210-
extern void _PyStackRef_CLOSE_SPECIALIZED(_PyStackRef ref, destructor destruct, const char *filename, int linenumber);
211-
#define PyStackRef_CLOSE_SPECIALIZED(REF, DESTRUCT) _PyStackRef_CLOSE_SPECIALIZED(REF, DESTRUCT, __FILE__, __LINE__)
212-
213-
static inline _PyStackRef
214-
PyStackRef_MakeHeapSafe(_PyStackRef ref)
248+
static inline void
249+
_PyStackRef_CLOSE_SPECIALIZED(_PyStackRef ref, destructor destruct, const char *filename, int linenumber)
215250
{
216-
return ref;
251+
assert(!PyStackRef_IsError(ref));
252+
assert(!PyStackRef_IsNull(ref));
253+
assert(!PyStackRef_IsTaggedInt(ref));
254+
PyObject *obj = _Py_stackref_close(ref, filename, linenumber);
255+
if (PyStackRef_RefcountOnObject(ref)) {
256+
_Py_DECREF_SPECIALIZED(obj, destruct);
257+
}
217258
}
259+
#define PyStackRef_CLOSE_SPECIALIZED(REF, DESTRUCT) _PyStackRef_CLOSE_SPECIALIZED(REF, DESTRUCT, __FILE__, __LINE__)
218260

219261
static inline _PyStackRef
220-
PyStackRef_Borrow(_PyStackRef ref)
262+
_PyStackRef_Borrow(_PyStackRef ref, const char *filename, int linenumber)
221263
{
222-
return PyStackRef_DUP(ref);
264+
assert(!PyStackRef_IsError(ref));
265+
assert(!PyStackRef_IsNull(ref));
266+
if (PyStackRef_IsTaggedInt(ref)) {
267+
return ref;
268+
}
269+
PyObject *obj = _Py_stackref_get_object(ref);
270+
return _Py_stackref_create(obj, Py_TAG_REFCNT, filename, linenumber);
223271
}
272+
#define PyStackRef_Borrow(REF) _PyStackRef_Borrow((REF), __FILE__, __LINE__)
224273

225274
#define PyStackRef_CLEAR(REF) \
226275
do { \
@@ -234,27 +283,44 @@ static inline _PyStackRef
234283
_PyStackRef_FromPyObjectStealMortal(PyObject *obj, const char *filename, int linenumber)
235284
{
236285
assert(!_Py_IsImmortal(obj));
237-
return _Py_stackref_create(obj, filename, linenumber);
286+
return _Py_stackref_create(obj, 0, filename, linenumber);
238287
}
239288
#define PyStackRef_FromPyObjectStealMortal(obj) _PyStackRef_FromPyObjectStealMortal(_PyObject_CAST(obj), __FILE__, __LINE__)
240289

241290
static inline bool
242291
PyStackRef_IsHeapSafe(_PyStackRef ref)
243292
{
244-
return true;
293+
if ((ref.index & Py_TAG_BITS) != Py_TAG_REFCNT || PyStackRef_IsNull(ref)) {
294+
// Tagged ints and ERROR are included.
295+
return true;
296+
}
297+
298+
PyObject *obj = _Py_stackref_get_object(ref);
299+
return _Py_IsImmortal(obj);
245300
}
246301

302+
static inline _PyStackRef
303+
_PyStackRef_MakeHeapSafe(_PyStackRef ref, const char *filename, int linenumber)
304+
{
305+
if (PyStackRef_IsHeapSafe(ref)) {
306+
return ref;
307+
}
308+
309+
PyObject *obj = _Py_stackref_close(ref, filename, linenumber);
310+
Py_INCREF(obj);
311+
return _Py_stackref_create(obj, 0, filename, linenumber);
312+
}
313+
#define PyStackRef_MakeHeapSafe(REF) _PyStackRef_MakeHeapSafe(REF, __FILE__, __LINE__)
314+
247315
static inline _PyStackRef
248316
_PyStackRef_FromPyObjectNewMortal(PyObject *obj, const char *filename, int linenumber)
249317
{
250318
assert(!_Py_IsStaticImmortal(obj));
251-
Py_INCREF(obj);
252-
return _Py_stackref_create(obj, filename, linenumber);
319+
Py_XINCREF(obj);
320+
return _Py_stackref_create(obj, 0, filename, linenumber);
253321
}
254322
#define PyStackRef_FromPyObjectNewMortal(obj) _PyStackRef_FromPyObjectNewMortal(_PyObject_CAST(obj), __FILE__, __LINE__)
255323

256-
#define PyStackRef_RefcountOnObject(REF) 1
257-
258324
extern int PyStackRef_Is(_PyStackRef a, _PyStackRef b);
259325

260326
extern bool PyStackRef_IsTaggedInt(_PyStackRef ref);
@@ -287,6 +353,7 @@ PyStackRef_Wrap(void *ptr)
287353
{
288354
assert(ptr != NULL);
289355
#ifdef Py_DEBUG
356+
assert(((uintptr_t)ptr & Py_TAG_BITS) == 0);
290357
return (_PyStackRef){ .bits = ((uintptr_t)ptr) | Py_TAG_INVALID };
291358
#else
292359
return (_PyStackRef){ .bits = (uintptr_t)ptr };
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Changes in stackref debugging mode when ``Py_STACKREF_DEBUG`` is set. We use
2+
the same pattern of refcounting for stackrefs as in production build.

Python/stackrefs.c

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,19 @@ _Py_stackref_close(_PyStackRef ref, const char *filename, int linenumber)
109109
}
110110

111111
_PyStackRef
112-
_Py_stackref_create(PyObject *obj, const char *filename, int linenumber)
112+
_Py_stackref_create(PyObject *obj, uint16_t flags, const char *filename, int linenumber)
113113
{
114114
if (obj == NULL) {
115115
Py_FatalError("Cannot create a stackref for NULL");
116116
}
117117
PyInterpreterState *interp = PyInterpreterState_Get();
118118
uint64_t new_id = interp->next_stackref;
119-
interp->next_stackref = new_id + 2;
119+
interp->next_stackref = new_id + 4;
120120
TableEntry *entry = make_table_entry(obj, filename, linenumber);
121121
if (entry == NULL) {
122122
Py_FatalError("No memory left for stackref debug table");
123123
}
124+
new_id |= flags;
124125
if (_Py_hashtable_set(interp->open_stackrefs_table, (void *)new_id, entry) < 0) {
125126
Py_FatalError("No memory left for stackref debug table");
126127
}
@@ -194,24 +195,18 @@ _Py_stackref_report_leaks(PyInterpreterState *interp)
194195
}
195196
}
196197

197-
void
198-
_PyStackRef_CLOSE_SPECIALIZED(_PyStackRef ref, destructor destruct, const char *filename, int linenumber)
199-
{
200-
PyObject *obj = _Py_stackref_close(ref, filename, linenumber);
201-
_Py_DECREF_SPECIALIZED(obj, destruct);
202-
}
203-
204198
_PyStackRef PyStackRef_TagInt(intptr_t i)
205199
{
206-
return (_PyStackRef){ .index = (i << 1) + 1 };
200+
assert(Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, (i << 2), 2) == i);
201+
return (_PyStackRef){ .index = (i << 2) | Py_INT_TAG };
207202
}
208203

209204
intptr_t
210205
PyStackRef_UntagInt(_PyStackRef i)
211206
{
212207
assert(PyStackRef_IsTaggedInt(i));
213208
intptr_t val = (intptr_t)i.index;
214-
return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, val, 1);
209+
return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, val, 2);
215210
}
216211

217212
bool
@@ -223,8 +218,9 @@ PyStackRef_IsNullOrInt(_PyStackRef ref)
223218
_PyStackRef
224219
PyStackRef_IncrementTaggedIntNoOverflow(_PyStackRef ref)
225220
{
226-
assert(ref.index <= INT_MAX - 2); // No overflow
227-
return (_PyStackRef){ .index = ref.index + 2 };
221+
assert(PyStackRef_IsTaggedInt(ref));
222+
assert((ref.index & (~Py_TAG_BITS)) != (INT_MAX & (~Py_TAG_BITS))); // Isn't about to overflow
223+
return (_PyStackRef){ .index = ref.index + 4 };
228224
}
229225

230226

0 commit comments

Comments
 (0)