Skip to content

Commit 818628c

Browse files
authored
bpo-44531: Add _PyType_AllocNoTrack() function (GH-26947)
Add an internal _PyType_AllocNoTrack() function to allocate an object without tracking it in the GC. Modify dict_new() to use _PyType_AllocNoTrack(): dict subclasses are now only tracked once all PyDictObject members are initialized. Calling _PyObject_GC_UNTRACK() is no longer needed for the dict type. Similar change in tuple_subtype_new() for tuple subclasses. Replace tuple_gc_track() with _PyObject_GC_TRACK().
1 parent 1b28187 commit 818628c

File tree

4 files changed

+51
-28
lines changed

4 files changed

+51
-28
lines changed

Include/internal/pycore_object.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ extern int _Py_CheckSlotResult(
178178
// See also the Py_TPFLAGS_READY flag.
179179
#define _PyType_IsReady(type) ((type)->tp_dict != NULL)
180180

181+
extern PyObject* _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems);
182+
181183
#ifdef __cplusplus
182184
}
183185
#endif

Objects/dictobject.c

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3324,26 +3324,34 @@ static PyNumberMethods dict_as_number = {
33243324
static PyObject *
33253325
dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
33263326
{
3327-
PyObject *self;
3328-
PyDictObject *d;
3327+
assert(type != NULL);
3328+
assert(type->tp_alloc != NULL);
3329+
// dict subclasses must implement the GC protocol
3330+
assert(_PyType_IS_GC(type));
33293331

3330-
assert(type != NULL && type->tp_alloc != NULL);
3331-
self = type->tp_alloc(type, 0);
3332-
if (self == NULL)
3332+
PyObject *self = type->tp_alloc(type, 0);
3333+
if (self == NULL) {
33333334
return NULL;
3334-
d = (PyDictObject *)self;
3335-
3336-
/* The object has been implicitly tracked by tp_alloc */
3337-
if (type == &PyDict_Type) {
3338-
_PyObject_GC_UNTRACK(d);
33393335
}
3336+
PyDictObject *d = (PyDictObject *)self;
33403337

33413338
d->ma_used = 0;
33423339
d->ma_version_tag = DICT_NEXT_VERSION();
33433340
dictkeys_incref(Py_EMPTY_KEYS);
33443341
d->ma_keys = Py_EMPTY_KEYS;
33453342
d->ma_values = empty_values;
33463343
ASSERT_CONSISTENT(d);
3344+
3345+
if (type != &PyDict_Type) {
3346+
// Don't track if a subclass tp_alloc is PyType_GenericAlloc()
3347+
if (!_PyObject_GC_IS_TRACKED(d)) {
3348+
_PyObject_GC_TRACK(d);
3349+
}
3350+
}
3351+
else {
3352+
// _PyType_AllocNoTrack() does not track the created object
3353+
assert(!_PyObject_GC_IS_TRACKED(d));
3354+
}
33473355
return self;
33483356
}
33493357

@@ -3441,7 +3449,7 @@ PyTypeObject PyDict_Type = {
34413449
0, /* tp_descr_set */
34423450
0, /* tp_dictoffset */
34433451
dict_init, /* tp_init */
3444-
PyType_GenericAlloc, /* tp_alloc */
3452+
_PyType_AllocNoTrack, /* tp_alloc */
34453453
dict_new, /* tp_new */
34463454
PyObject_GC_Del, /* tp_free */
34473455
.tp_vectorcall = dict_vectorcall,

Objects/tupleobject.c

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,6 @@ get_tuple_state(void)
2525
#endif
2626

2727

28-
static inline void
29-
tuple_gc_track(PyTupleObject *op)
30-
{
31-
_PyObject_GC_TRACK(op);
32-
}
33-
34-
3528
/* Print summary info about the state of the optimized allocator */
3629
void
3730
_PyTuple_DebugMallocStats(FILE *out)
@@ -48,10 +41,12 @@ _PyTuple_DebugMallocStats(FILE *out)
4841
#endif
4942
}
5043

51-
/* Allocate an uninitialized tuple object. Before making it public following
44+
/* Allocate an uninitialized tuple object. Before making it public, following
5245
steps must be done:
53-
- initialize its items
54-
- call tuple_gc_track() on it
46+
47+
- Initialize its items.
48+
- Call _PyObject_GC_TRACK() on it.
49+
5550
Because the empty tuple is always reused and it's already tracked by GC,
5651
this function must not be called with size == 0 (unless from PyTuple_New()
5752
which wraps this function).
@@ -161,7 +156,7 @@ PyTuple_New(Py_ssize_t size)
161156
for (Py_ssize_t i = 0; i < size; i++) {
162157
op->ob_item[i] = NULL;
163158
}
164-
tuple_gc_track(op);
159+
_PyObject_GC_TRACK(op);
165160
return (PyObject *) op;
166161
}
167162

@@ -257,7 +252,7 @@ PyTuple_Pack(Py_ssize_t n, ...)
257252
items[i] = o;
258253
}
259254
va_end(vargs);
260-
tuple_gc_track(result);
255+
_PyObject_GC_TRACK(result);
261256
return (PyObject *)result;
262257
}
263258

@@ -473,7 +468,7 @@ _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
473468
Py_INCREF(item);
474469
dst[i] = item;
475470
}
476-
tuple_gc_track(tuple);
471+
_PyObject_GC_TRACK(tuple);
477472
return (PyObject *)tuple;
478473
}
479474

@@ -551,7 +546,7 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
551546
Py_INCREF(v);
552547
dest[i] = v;
553548
}
554-
tuple_gc_track(np);
549+
_PyObject_GC_TRACK(np);
555550
return (PyObject *)np;
556551
}
557552

@@ -588,7 +583,7 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
588583
p++;
589584
}
590585
}
591-
tuple_gc_track(np);
586+
_PyObject_GC_TRACK(np);
592587
return (PyObject *) np;
593588
}
594589

@@ -783,6 +778,9 @@ tuple_subtype_new(PyTypeObject *type, PyObject *iterable)
783778
Py_ssize_t i, n;
784779

785780
assert(PyType_IsSubtype(type, &PyTuple_Type));
781+
// tuple subclasses must implement the GC protocol
782+
assert(_PyType_IS_GC(type));
783+
786784
tmp = tuple_new_impl(&PyTuple_Type, iterable);
787785
if (tmp == NULL)
788786
return NULL;
@@ -798,6 +796,11 @@ tuple_subtype_new(PyTypeObject *type, PyObject *iterable)
798796
PyTuple_SET_ITEM(newobj, i, item);
799797
}
800798
Py_DECREF(tmp);
799+
800+
// Don't track if a subclass tp_alloc is PyType_GenericAlloc()
801+
if (!_PyObject_GC_IS_TRACKED(newobj)) {
802+
_PyObject_GC_TRACK(newobj);
803+
}
801804
return newobj;
802805
}
803806

@@ -857,7 +860,7 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
857860
dest[i] = it;
858861
}
859862

860-
tuple_gc_track(result);
863+
_PyObject_GC_TRACK(result);
861864
return (PyObject *)result;
862865
}
863866
}

Objects/typeobject.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1164,7 +1164,7 @@ type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
11641164
}
11651165

11661166
PyObject *
1167-
PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
1167+
_PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems)
11681168
{
11691169
PyObject *obj;
11701170
const size_t size = _PyObject_VAR_SIZE(type, nitems+1);
@@ -1189,6 +1189,16 @@ PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
11891189
else {
11901190
_PyObject_InitVar((PyVarObject *)obj, type, nitems);
11911191
}
1192+
return obj;
1193+
}
1194+
1195+
PyObject *
1196+
PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
1197+
{
1198+
PyObject *obj = _PyType_AllocNoTrack(type, nitems);
1199+
if (obj == NULL) {
1200+
return NULL;
1201+
}
11921202

11931203
if (_PyType_IS_GC(type)) {
11941204
_PyObject_GC_TRACK(obj);

0 commit comments

Comments
 (0)