@@ -1701,34 +1701,56 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text)
17011701 bytes_len = PyBytes_GET_SIZE (b );
17021702 }
17031703
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+ }
17131719 }
1714- self -> pending_bytes = b ;
17151720 }
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 ;
17251725 }
17261726 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+ }
17301747 }
1748+
1749+ int ret = PyList_Append (self -> pending_bytes , b );
17311750 Py_DECREF (b );
1751+ if (ret < 0 ) {
1752+ return NULL ;
1753+ }
17321754 }
17331755
17341756 self -> pending_bytes_count += bytes_len ;
0 commit comments