Skip to content

Commit b5baeab

Browse files
StanFromIrelandmiss-islington
authored andcommitted
pythongh-151814: Fix unbounded memory growth from repeated empty writes to io.TextIOWrapper (pythonGH-151817)
(cherry picked from commit c613072) Co-authored-by: Stan Ulbrych <stan@python.org>
1 parent 117b76a commit b5baeab

2 files changed

Lines changed: 27 additions & 19 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix unbounded memory growth in :class:`io.TextIOWrapper` when repeatedly
2+
writing an empty string.

Modules/_io/textio.c

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,32 +1741,38 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text)
17411741
}
17421742
}
17431743

1744-
if (self->pending_bytes == NULL) {
1745-
assert(self->pending_bytes_count == 0);
1746-
self->pending_bytes = b;
1747-
}
1748-
else if (!PyList_CheckExact(self->pending_bytes)) {
1749-
PyObject *list = PyList_New(2);
1750-
if (list == NULL) {
1744+
if (bytes_len > 0) {
1745+
if (self->pending_bytes == NULL) {
1746+
assert(self->pending_bytes_count == 0);
1747+
self->pending_bytes = b;
1748+
}
1749+
else if (!PyList_CheckExact(self->pending_bytes)) {
1750+
PyObject *list = PyList_New(2);
1751+
if (list == NULL) {
1752+
Py_DECREF(b);
1753+
return NULL;
1754+
}
1755+
// Since Python 3.12, allocating GC object won't trigger GC and release
1756+
// GIL. See https://github.com/python/cpython/issues/97922
1757+
assert(!PyList_CheckExact(self->pending_bytes));
1758+
PyList_SET_ITEM(list, 0, self->pending_bytes);
1759+
PyList_SET_ITEM(list, 1, b);
1760+
self->pending_bytes = list;
1761+
}
1762+
else {
1763+
if (PyList_Append(self->pending_bytes, b) < 0) {
1764+
Py_DECREF(b);
1765+
return NULL;
1766+
}
17511767
Py_DECREF(b);
1752-
return NULL;
17531768
}
1754-
// Since Python 3.12, allocating GC object won't trigger GC and release
1755-
// GIL. See https://github.com/python/cpython/issues/97922
1756-
assert(!PyList_CheckExact(self->pending_bytes));
1757-
PyList_SET_ITEM(list, 0, self->pending_bytes);
1758-
PyList_SET_ITEM(list, 1, b);
1759-
self->pending_bytes = list;
1769+
1770+
self->pending_bytes_count += bytes_len;
17601771
}
17611772
else {
1762-
if (PyList_Append(self->pending_bytes, b) < 0) {
1763-
Py_DECREF(b);
1764-
return NULL;
1765-
}
17661773
Py_DECREF(b);
17671774
}
17681775

1769-
self->pending_bytes_count += bytes_len;
17701776
if (self->pending_bytes_count >= self->chunk_size || needflush ||
17711777
text_needflush) {
17721778
if (_textiowrapper_writeflush(self) < 0)

0 commit comments

Comments
 (0)