@@ -1701,34 +1701,56 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text)
1701
1701
bytes_len = PyBytes_GET_SIZE (b );
1702
1702
}
1703
1703
1704
- if (self -> pending_bytes == NULL ) {
1705
- self -> pending_bytes_count = 0 ;
1706
- self -> pending_bytes = b ;
1707
- }
1708
- else if (self -> pending_bytes_count + bytes_len > self -> chunk_size ) {
1709
- // Prevent to concatenate more than chunk_size data.
1710
- if (_textiowrapper_writeflush (self ) < 0 ) {
1711
- Py_DECREF (b );
1712
- return NULL ;
1704
+ // We should avoid concatinating huge data.
1705
+ // Flush the buffer before adding b to the buffer if b is not small.
1706
+ // https://github.com/python/cpython/issues/87426
1707
+ if (bytes_len >= self -> chunk_size ) {
1708
+ // _textiowrapper_writeflush() calls buffer.write().
1709
+ // self->pending_bytes can be appended during buffer->write()
1710
+ // or other thread.
1711
+ // We need to loop until buffer becomes empty.
1712
+ // https://github.com/python/cpython/issues/118138
1713
+ // https://github.com/python/cpython/issues/119506
1714
+ while (self -> pending_bytes != NULL ) {
1715
+ if (_textiowrapper_writeflush (self ) < 0 ) {
1716
+ Py_DECREF (b );
1717
+ return NULL ;
1718
+ }
1713
1719
}
1714
- self -> pending_bytes = b ;
1715
1720
}
1716
- else if (!PyList_CheckExact (self -> pending_bytes )) {
1717
- PyObject * list = PyList_New (2 );
1718
- if (list == NULL ) {
1719
- Py_DECREF (b );
1720
- return NULL ;
1721
- }
1722
- PyList_SET_ITEM (list , 0 , self -> pending_bytes );
1723
- PyList_SET_ITEM (list , 1 , b );
1724
- self -> pending_bytes = list ;
1721
+
1722
+ if (self -> pending_bytes == NULL ) {
1723
+ assert (self -> pending_bytes_count == 0 );
1724
+ self -> pending_bytes = b ;
1725
1725
}
1726
1726
else {
1727
- if (PyList_Append (self -> pending_bytes , b ) < 0 ) {
1728
- Py_DECREF (b );
1729
- return NULL ;
1727
+ if (!PyList_CheckExact (self -> pending_bytes )) {
1728
+ PyObject * list = PyList_New (0 );
1729
+ if (list == NULL ) {
1730
+ Py_DECREF (b );
1731
+ return NULL ;
1732
+ }
1733
+ // PyList_New() may trigger GC and other thread may call write().
1734
+ // So, we need to check the self->pending_bytes is a list again.
1735
+ if (PyList_CheckExact (self -> pending_bytes )) {
1736
+ // Releasing empty list won't trigger GC and/or __del__.
1737
+ Py_DECREF (list );
1738
+ }
1739
+ else {
1740
+ if (PyList_Append (list , self -> pending_bytes ) < 0 ) {
1741
+ Py_DECREF (list );
1742
+ Py_DECREF (b );
1743
+ return NULL ;
1744
+ }
1745
+ Py_SETREF (self -> pending_bytes , list );
1746
+ }
1730
1747
}
1748
+
1749
+ int ret = PyList_Append (self -> pending_bytes , b );
1731
1750
Py_DECREF (b );
1751
+ if (ret < 0 ) {
1752
+ return NULL ;
1753
+ }
1732
1754
}
1733
1755
1734
1756
self -> pending_bytes_count += bytes_len ;
0 commit comments