Skip to content

Commit aa0de5d

Browse files
[mypyc] Fix debug warnings/assertions from vecs (#21373)
Two issues found by running the `vecs` tests with a debug build of python: Fix assertion `PyObject_GC_Track() object is not valid` caused by calling the track function on vec buffers before their items are initialized. We have an optimization in place that doesn't immediately zero-initialize the buffers because they are set to their actual values right after calling the allocator. But that makes the GC track garbage memory so move the track function calls to after the items are initialized. Fix resource warning about calling `PyObject_GC_Del` on `vec_generic_alias` objects. We were missing a `PyObject_GC_UnTrack` call in the deallocator. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent f60d40b commit aa0de5d

3 files changed

Lines changed: 32 additions & 6 deletions

File tree

mypyc/lib-rt/vecs/librt_vecs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ VecGenericAlias_traverse(VecGenericAlias *self, visitproc visit, void *arg)
139139
static void
140140
VecGenericAlias_dealloc(VecGenericAlias *self)
141141
{
142+
PyObject_GC_UnTrack(self);
142143
if (self->item_type && !Vec_IsMagicItemType(self->item_type)) {
143144
Py_DECREF((PyObject *)(self->item_type & ~1));
144145
self->item_type = 0;

mypyc/lib-rt/vecs/vec_nested.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@ static inline VecNested vec_error() {
1616
return v;
1717
}
1818

19+
static inline void vec_track_buffer(VecNested *vec) {
20+
PyObject_GC_Track(vec->buf);
21+
}
22+
1923
static inline PyObject *box_vec_item_by_index(VecNested v, Py_ssize_t index) {
2024
return VecNested_BoxItem(v, v.buf->items[index]);
2125
}
2226

23-
// Alloc a partially initialized vec. Caller *must* initialize len and buf->items of the
24-
// return value.
27+
// Alloc a partially initialized vec. If size > 0, caller *must* immediately initialize len,
28+
// and buf->items. Caller *must* also call vec_track_buffer on the returned vec but only
29+
// after initializing the items.
2530
static VecNested vec_alloc(Py_ssize_t size, size_t item_type, size_t depth) {
2631
VecNestedBufObject *buf = PyObject_GC_NewVar(VecNestedBufObject, &VecNestedBufType, size);
2732
if (buf == NULL)
@@ -31,7 +36,6 @@ static VecNested vec_alloc(Py_ssize_t size, size_t item_type, size_t depth) {
3136
if (!Vec_IsMagicItemType(item_type))
3237
Py_INCREF(VEC_BUF_ITEM_TYPE(buf));
3338
VecNested res = { .buf = buf };
34-
PyObject_GC_Track(buf);
3539
return res;
3640
}
3741

@@ -79,6 +83,7 @@ VecNested VecNested_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type, size_
7983
vec.buf->items[i].buf = NULL;
8084
}
8185
vec.len = size;
86+
vec_track_buffer(&vec);
8287
return vec;
8388
}
8489

@@ -122,6 +127,7 @@ VecNested VecNested_Slice(VecNested vec, int64_t start, int64_t end) {
122127
Py_XINCREF(item.buf);
123128
res.buf->items[i] = item;
124129
}
130+
vec_track_buffer(&res);
125131
return res;
126132
}
127133

@@ -155,6 +161,7 @@ static PyObject *vec_subscript(PyObject *self, PyObject *item) {
155161
res.buf->items[i] = item;
156162
j += step;
157163
}
164+
vec_track_buffer(&res);
158165
return VecNested_Box(res);
159166
} else {
160167
PyErr_Format(PyExc_TypeError, "vec indices must be integers or slices, not %.100s",
@@ -280,6 +287,7 @@ VecNested VecNested_Append(VecNested vec, VecNestedBufItem x) {
280287
}
281288
new.buf->items[vec.len] = x;
282289
new.len = vec.len + 1;
290+
vec_track_buffer(&new);
283291
VEC_DECREF(vec);
284292
return new;
285293
}
@@ -381,6 +389,7 @@ VecNested VecNested_ExtendVec(VecNested dst, VecNested src) {
381389
}
382390
memset(new.buf->items + new_len, 0, sizeof(VecNestedBufItem) * (new_cap - new_len));
383391
new.len = new_len;
392+
vec_track_buffer(&new);
384393
VEC_DECREF(dst);
385394
return new;
386395
}
@@ -679,6 +688,7 @@ PyObject *VecNested_FromIterable(size_t item_type, size_t depth, PyObject *itera
679688
}
680689
}
681690
v.len = 0;
691+
vec_track_buffer(&v);
682692

683693
PyObject *iter = PyObject_GetIter(iterable);
684694
if (iter == NULL) {

mypyc/lib-rt/vecs/vec_t.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,18 @@ static inline VecTBufObject *alloc_buf(Py_ssize_t size, size_t item_type) {
2424
return NULL;
2525
buf->item_type = item_type;
2626
Py_INCREF(VEC_BUF_ITEM_TYPE(buf));
27-
PyObject_GC_Track(buf);
2827
return buf;
2928
}
3029

31-
// Alloc a partially initialized vec. Caller *must* immediately initialize len, and buf->items
32-
// if size > 0.
30+
static inline void vec_track_buffer(VecT *vec) {
31+
if (vec->buf != NULL) {
32+
PyObject_GC_Track(vec->buf);
33+
}
34+
}
35+
36+
// Alloc a partially initialized vec. If size > 0, caller *must* immediately initialize len,
37+
// and buf->items. Caller *must* also call vec_track_buffer on the returned vec but only
38+
// after initializing the items.
3339
static VecT vec_alloc(Py_ssize_t size, size_t item_type) {
3440
VecTBufObject *buf;
3541

@@ -51,6 +57,7 @@ PyObject *VecT_Box(VecT vec, size_t item_type) {
5157
vec.buf = alloc_buf(0, item_type);
5258
if (vec.buf == NULL)
5359
return NULL;
60+
vec_track_buffer(&vec);
5461
}
5562
VecTObject *obj = PyObject_GC_New(VecTObject, &VecTType);
5663
if (obj == NULL) {
@@ -93,6 +100,7 @@ VecT VecT_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type) {
93100
for (Py_ssize_t i = 0; i < cap; i++) {
94101
vec.buf->items[i] = NULL;
95102
}
103+
vec_track_buffer(&vec);
96104
vec.len = size;
97105
return vec;
98106
}
@@ -143,6 +151,7 @@ VecT VecT_Slice(VecT vec, int64_t start, int64_t end) {
143151
Py_INCREF(item);
144152
res.buf->items[i] = item;
145153
}
154+
vec_track_buffer(&res);
146155
return res;
147156
}
148157

@@ -180,6 +189,7 @@ static PyObject *vec_subscript(PyObject *self, PyObject *item) {
180189
res.buf->items[i] = item;
181190
j += step;
182191
}
192+
vec_track_buffer(&res);
183193
PyObject *result = VecT_Box(res, vec.buf->item_type);
184194
if (result == NULL) {
185195
VEC_DECREF(res);
@@ -260,6 +270,7 @@ VecT VecT_Append(VecT vec, PyObject *x, size_t item_type) {
260270
Py_INCREF(x);
261271
new.len = 1;
262272
new.buf->items[0] = x;
273+
vec_track_buffer(&new);
263274
return new;
264275
}
265276
Py_ssize_t cap = VEC_CAP(vec);
@@ -294,6 +305,7 @@ VecT VecT_Append(VecT vec, PyObject *x, size_t item_type) {
294305
}
295306
new.buf->items[vec.len] = x;
296307
new.len = vec.len + 1;
308+
vec_track_buffer(&new);
297309
VEC_DECREF(vec);
298310
return new;
299311
}
@@ -361,6 +373,7 @@ VecT VecT_ExtendVec(VecT dst, VecT src, size_t item_type) {
361373
}
362374
memset(new.buf->items + src.len, 0, sizeof(PyObject *) * (new_len - src.len));
363375
new.len = new_len;
376+
vec_track_buffer(&new);
364377
return new;
365378
}
366379
Py_ssize_t cap = VEC_CAP(dst);
@@ -405,6 +418,7 @@ VecT VecT_ExtendVec(VecT dst, VecT src, size_t item_type) {
405418
}
406419
memset(new.buf->items + new_len, 0, sizeof(PyObject *) * (new_cap - new_len));
407420
new.len = new_len;
421+
vec_track_buffer(&new);
408422
VEC_DECREF(dst);
409423
return new;
410424
}
@@ -680,6 +694,7 @@ PyObject *VecT_FromIterable(size_t item_type, PyObject *iterable, int64_t cap) {
680694
v.buf->items[i] = NULL;
681695
}
682696
v.len = 0;
697+
vec_track_buffer(&v);
683698

684699
PyObject *iter = PyObject_GetIter(iterable);
685700
if (iter == NULL) {

0 commit comments

Comments
 (0)