Skip to content

Commit bb874c3

Browse files
committed
Add flags to object header to assess cost of maintain might-be-in-cycle flag
1 parent f878d46 commit bb874c3

File tree

5 files changed

+44
-11
lines changed

5 files changed

+44
-11
lines changed

Include/internal/pycore_object.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
187187
#endif
188188
if (--op->ob_refcnt != 0) {
189189
assert(op->ob_refcnt > 0);
190+
op->ob_flags |= _Py_MAYBE_IN_CYCLE;
190191
}
191192
else {
192193
#ifdef Py_TRACE_REFS
@@ -212,6 +213,7 @@ _Py_DECREF_NO_DEALLOC(PyObject *op)
212213
_Py_FatalRefcountError("Expected a positive remaining refcount");
213214
}
214215
#endif
216+
op->ob_flags |= _Py_MAYBE_IN_CYCLE;
215217
}
216218

217219
#else

Include/object.h

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,19 +79,22 @@ whose size is determined when the object is allocated.
7979
(type), \
8080
},
8181
#else
82-
#define PyObject_HEAD_INIT(type) \
83-
{ \
84-
{ _Py_IMMORTAL_REFCNT }, \
85-
(type) \
82+
83+
#define PyObject_HEAD_INIT(type) \
84+
{ \
85+
.ob_refcnt = _Py_IMMORTAL_REFCNT, \
86+
.ob_flags = 0, \
87+
.ob_type = (type) \
8688
},
87-
#endif
8889

8990
#define PyVarObject_HEAD_INIT(type, size) \
9091
{ \
9192
PyObject_HEAD_INIT(type) \
9293
(size) \
9394
},
9495

96+
#endif
97+
9598
/* PyObject_VAR_HEAD defines the initial segment of all variable-size
9699
* container objects. These end with a declaration of an array with 1
97100
* element, but enough space is malloc'ed so that the array actually
@@ -119,17 +122,31 @@ struct _object {
119122
__pragma(warning(push))
120123
__pragma(warning(disable: 4201))
121124
#endif
122-
union {
123-
Py_ssize_t ob_refcnt;
124125
#if SIZEOF_VOID_P > 4
125-
PY_UINT32_T ob_refcnt_split[2];
126+
127+
#if PY_BIG_ENDIAN
128+
PY_UINT32_T ob_flags;
129+
PY_UINT32_T ob_refcnt;
130+
#else
131+
PY_UINT32_T ob_refcnt;
132+
PY_UINT32_T ob_flags;
126133
#endif
127-
};
134+
135+
#else
136+
PY_UINT32_T ob_refcnt;
137+
#endif
138+
128139
#ifdef _MSC_VER
129140
__pragma(warning(pop))
130141
#endif
131142

132143
PyTypeObject *ob_type;
144+
#if SIZEOF_VOID_P <= 4
145+
PY_UINT32_T ob_flags;
146+
#endif
147+
148+
#define _Py_MAYBE_IN_CYCLE (1 << 31)
149+
133150
};
134151
#else
135152
// Objects that are not owned by any thread use a thread id (tid) of zero.

Include/refcount.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,14 +238,14 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op)
238238
}
239239
#elif SIZEOF_VOID_P > 4
240240
// Portable saturated add, branching on the carry flag and set low bits
241-
PY_UINT32_T cur_refcnt = op->ob_refcnt_split[PY_BIG_ENDIAN];
241+
PY_UINT32_T cur_refcnt = op->ob_refcnt;
242242
PY_UINT32_T new_refcnt = cur_refcnt + 1;
243243
if (new_refcnt == 0) {
244244
// cur_refcnt is equal to _Py_IMMORTAL_REFCNT: the object is immortal,
245245
// do nothing
246246
return;
247247
}
248-
op->ob_refcnt_split[PY_BIG_ENDIAN] = new_refcnt;
248+
op->ob_refcnt = new_refcnt;
249249
#else
250250
// Explicitly check immortality against the immortal value
251251
if (_Py_IsImmortal(op)) {
@@ -350,6 +350,9 @@ static inline void Py_DECREF(const char *filename, int lineno, PyObject *op)
350350
if (--op->ob_refcnt == 0) {
351351
_Py_Dealloc(op);
352352
}
353+
else {
354+
op->ob_flags |= _Py_MAYBE_IN_CYCLE;
355+
}
353356
}
354357
#define Py_DECREF(op) Py_DECREF(__FILE__, __LINE__, _PyObject_CAST(op))
355358

@@ -365,6 +368,9 @@ static inline Py_ALWAYS_INLINE void Py_DECREF(PyObject *op)
365368
if (--op->ob_refcnt == 0) {
366369
_Py_Dealloc(op);
367370
}
371+
else {
372+
op->ob_flags |= _Py_MAYBE_IN_CYCLE;
373+
}
368374
}
369375
#define Py_DECREF(op) Py_DECREF(_PyObject_CAST(op))
370376
#endif

Objects/object.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2369,6 +2369,7 @@ new_reference(PyObject *op)
23692369
// Skip the immortal object check in Py_SET_REFCNT; always set refcnt to 1
23702370
#if !defined(Py_GIL_DISABLED)
23712371
op->ob_refcnt = 1;
2372+
op->ob_flags = 0;
23722373
#else
23732374
op->ob_tid = _Py_ThreadId();
23742375
op->_padding = 0;
@@ -2411,6 +2412,7 @@ _Py_SetImmortalUntracked(PyObject *op)
24112412
op->ob_ref_shared = 0;
24122413
#else
24132414
op->ob_refcnt = _Py_IMMORTAL_REFCNT;
2415+
op->ob_flags = 0;
24142416
#endif
24152417
}
24162418

Python/ceval.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@
7171
destructor dealloc = Py_TYPE(op)->tp_dealloc; \
7272
(*dealloc)(op); \
7373
} \
74+
else { \
75+
op->ob_flags |= _Py_MAYBE_IN_CYCLE; \
76+
} \
7477
} while (0)
7578

7679
#undef Py_XDECREF
@@ -98,6 +101,9 @@
98101
destructor d = (destructor)(dealloc); \
99102
d(op); \
100103
} \
104+
else { \
105+
op->ob_flags |= _Py_MAYBE_IN_CYCLE; \
106+
} \
101107
} while (0)
102108
#endif
103109

0 commit comments

Comments
 (0)