aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Modules/_io
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/_io')
-rw-r--r--Modules/_io/bufferedio.c16
-rw-r--r--Modules/_io/bytesio.c284
-rw-r--r--Modules/_io/clinic/bytesio.c.h66
-rw-r--r--Modules/_io/clinic/stringio.c.h12
-rw-r--r--Modules/_io/fileio.c8
-rw-r--r--Modules/_io/iobase.c4
-rw-r--r--Modules/_io/stringio.c29
-rw-r--r--Modules/_io/textio.c27
-rw-r--r--Modules/_io/winconsoleio.c4
9 files changed, 304 insertions, 146 deletions
diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c
index 2189b1f3800..25c8bf8b3d5 100644
--- a/Modules/_io/bufferedio.c
+++ b/Modules/_io/bufferedio.c
@@ -13,6 +13,7 @@
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
#include "pycore_pyerrors.h" // _Py_FatalErrorFormat()
#include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing()
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
#include "_iomodule.h"
@@ -421,8 +422,7 @@ buffered_dealloc(PyObject *op)
return;
_PyObject_GC_UNTRACK(self);
self->ok = 0;
- if (self->weakreflist != NULL)
- PyObject_ClearWeakRefs(op);
+ FT_CLEAR_WEAKREFS(op, self->weakreflist);
if (self->buffer) {
PyMem_Free(self->buffer);
self->buffer = NULL;
@@ -2312,8 +2312,7 @@ bufferedrwpair_dealloc(PyObject *op)
rwpair *self = rwpair_CAST(op);
PyTypeObject *tp = Py_TYPE(self);
_PyObject_GC_UNTRACK(self);
- if (self->weakreflist != NULL)
- PyObject_ClearWeakRefs(op);
+ FT_CLEAR_WEAKREFS(op, self->weakreflist);
(void)bufferedrwpair_clear(op);
tp->tp_free(self);
Py_DECREF(tp);
@@ -2555,8 +2554,7 @@ static PyMethodDef bufferedreader_methods[] = {
_IO__BUFFERED_TRUNCATE_METHODDEF
_IO__BUFFERED___SIZEOF___METHODDEF
- {"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS},
- {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O},
+ {"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS},
{NULL, NULL}
};
@@ -2615,8 +2613,7 @@ static PyMethodDef bufferedwriter_methods[] = {
_IO__BUFFERED_TELL_METHODDEF
_IO__BUFFERED___SIZEOF___METHODDEF
- {"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS},
- {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O},
+ {"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS},
{NULL, NULL}
};
@@ -2733,8 +2730,7 @@ static PyMethodDef bufferedrandom_methods[] = {
_IO_BUFFEREDWRITER_WRITE_METHODDEF
_IO__BUFFERED___SIZEOF___METHODDEF
- {"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS},
- {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O},
+ {"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS},
{NULL, NULL}
};
diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c
index e45a2d1a16d..1c71bce4fbb 100644
--- a/Modules/_io/bytesio.c
+++ b/Modules/_io/bytesio.c
@@ -1,8 +1,11 @@
#include "Python.h"
+#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION()
#include "pycore_object.h"
-#include "pycore_sysmodule.h" // _PySys_GetSizeOf()
+#include "pycore_pyatomic_ft_wrappers.h"
+#include "pycore_sysmodule.h" // _PySys_GetSizeOf()
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
-#include <stddef.h> // offsetof()
+#include <stddef.h> // offsetof()
#include "_iomodule.h"
/*[clinic input]
@@ -50,7 +53,7 @@ check_closed(bytesio *self)
static int
check_exports(bytesio *self)
{
- if (self->exports > 0) {
+ if (FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) > 0) {
PyErr_SetString(PyExc_BufferError,
"Existing exports of data: object cannot be re-sized");
return 1;
@@ -68,15 +71,17 @@ check_exports(bytesio *self)
return NULL; \
}
-#define SHARED_BUF(self) (Py_REFCNT((self)->buf) > 1)
+#define SHARED_BUF(self) (!_PyObject_IsUniquelyReferenced((self)->buf))
/* Internal routine to get a line from the buffer of a BytesIO
object. Returns the length between the current position to the
next newline character. */
static Py_ssize_t
-scan_eol(bytesio *self, Py_ssize_t len)
+scan_eol_lock_held(bytesio *self, Py_ssize_t len)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
+
const char *start, *n;
Py_ssize_t maxlen;
@@ -109,11 +114,13 @@ scan_eol(bytesio *self, Py_ssize_t len)
The caller should ensure that the 'size' argument is non-negative and
not lesser than self->string_size. Returns 0 on success, -1 otherwise. */
static int
-unshare_buffer(bytesio *self, size_t size)
+unshare_buffer_lock_held(bytesio *self, size_t size)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
+
PyObject *new_buf;
assert(SHARED_BUF(self));
- assert(self->exports == 0);
+ assert(FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) == 0);
assert(size >= (size_t)self->string_size);
new_buf = PyBytes_FromStringAndSize(NULL, size);
if (new_buf == NULL)
@@ -128,10 +135,12 @@ unshare_buffer(bytesio *self, size_t size)
The caller should ensure that the 'size' argument is non-negative. Returns
0 on success, -1 otherwise. */
static int
-resize_buffer(bytesio *self, size_t size)
+resize_buffer_lock_held(bytesio *self, size_t size)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
+
assert(self->buf != NULL);
- assert(self->exports == 0);
+ assert(FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) == 0);
/* Here, unsigned types are used to avoid dealing with signed integer
overflow, which is undefined in C. */
@@ -160,7 +169,7 @@ resize_buffer(bytesio *self, size_t size)
}
if (SHARED_BUF(self)) {
- if (unshare_buffer(self, alloc) < 0)
+ if (unshare_buffer_lock_held(self, alloc) < 0)
return -1;
}
else {
@@ -181,8 +190,10 @@ resize_buffer(bytesio *self, size_t size)
Inlining is disabled because it's significantly decreases performance
of writelines() in PGO build. */
Py_NO_INLINE static Py_ssize_t
-write_bytes(bytesio *self, PyObject *b)
+write_bytes_lock_held(bytesio *self, PyObject *b)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
+
if (check_closed(self)) {
return -1;
}
@@ -202,13 +213,13 @@ write_bytes(bytesio *self, PyObject *b)
assert(self->pos >= 0);
size_t endpos = (size_t)self->pos + len;
if (endpos > (size_t)PyBytes_GET_SIZE(self->buf)) {
- if (resize_buffer(self, endpos) < 0) {
+ if (resize_buffer_lock_held(self, endpos) < 0) {
len = -1;
goto done;
}
}
else if (SHARED_BUF(self)) {
- if (unshare_buffer(self, Py_MAX(endpos, (size_t)self->string_size)) < 0) {
+ if (unshare_buffer_lock_held(self, Py_MAX(endpos, (size_t)self->string_size)) < 0) {
len = -1;
goto done;
}
@@ -245,13 +256,17 @@ write_bytes(bytesio *self, PyObject *b)
static PyObject *
bytesio_get_closed(PyObject *op, void *Py_UNUSED(closure))
{
+ PyObject *ret;
bytesio *self = bytesio_CAST(op);
+ Py_BEGIN_CRITICAL_SECTION(self);
if (self->buf == NULL) {
- Py_RETURN_TRUE;
+ ret = Py_True;
}
else {
- Py_RETURN_FALSE;
+ ret = Py_False;
}
+ Py_END_CRITICAL_SECTION();
+ return ret;
}
/*[clinic input]
@@ -311,6 +326,7 @@ _io_BytesIO_flush_impl(bytesio *self)
}
/*[clinic input]
+@critical_section
_io.BytesIO.getbuffer
cls: defining_class
@@ -321,7 +337,7 @@ Get a read-write view over the contents of the BytesIO object.
static PyObject *
_io_BytesIO_getbuffer_impl(bytesio *self, PyTypeObject *cls)
-/*[clinic end generated code: output=045091d7ce87fe4e input=0668fbb48f95dffa]*/
+/*[clinic end generated code: output=045091d7ce87fe4e input=8295764061be77fd]*/
{
_PyIO_State *state = get_io_state_by_cls(cls);
PyTypeObject *type = state->PyBytesIOBuffer_Type;
@@ -340,6 +356,7 @@ _io_BytesIO_getbuffer_impl(bytesio *self, PyTypeObject *cls)
}
/*[clinic input]
+@critical_section
_io.BytesIO.getvalue
Retrieve the entire contents of the BytesIO object.
@@ -347,16 +364,16 @@ Retrieve the entire contents of the BytesIO object.
static PyObject *
_io_BytesIO_getvalue_impl(bytesio *self)
-/*[clinic end generated code: output=b3f6a3233c8fd628 input=4b403ac0af3973ed]*/
+/*[clinic end generated code: output=b3f6a3233c8fd628 input=c91bff398df0c352]*/
{
CHECK_CLOSED(self);
- if (self->string_size <= 1 || self->exports > 0)
+ if (self->string_size <= 1 || FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) > 0)
return PyBytes_FromStringAndSize(PyBytes_AS_STRING(self->buf),
self->string_size);
if (self->string_size != PyBytes_GET_SIZE(self->buf)) {
if (SHARED_BUF(self)) {
- if (unshare_buffer(self, self->string_size) < 0)
+ if (unshare_buffer_lock_held(self, self->string_size) < 0)
return NULL;
}
else {
@@ -384,6 +401,7 @@ _io_BytesIO_isatty_impl(bytesio *self)
}
/*[clinic input]
+@critical_section
_io.BytesIO.tell
Current file position, an integer.
@@ -391,22 +409,24 @@ Current file position, an integer.
static PyObject *
_io_BytesIO_tell_impl(bytesio *self)
-/*[clinic end generated code: output=b54b0f93cd0e5e1d input=b106adf099cb3657]*/
+/*[clinic end generated code: output=b54b0f93cd0e5e1d input=2c7b0e8f82e05c4d]*/
{
CHECK_CLOSED(self);
return PyLong_FromSsize_t(self->pos);
}
static PyObject *
-read_bytes(bytesio *self, Py_ssize_t size)
+read_bytes_lock_held(bytesio *self, Py_ssize_t size)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
+
const char *output;
assert(self->buf != NULL);
assert(size <= self->string_size);
if (size > 1 &&
self->pos == 0 && size == PyBytes_GET_SIZE(self->buf) &&
- self->exports == 0) {
+ FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) == 0) {
self->pos += size;
return Py_NewRef(self->buf);
}
@@ -417,6 +437,7 @@ read_bytes(bytesio *self, Py_ssize_t size)
}
/*[clinic input]
+@critical_section
_io.BytesIO.read
size: Py_ssize_t(accept={int, NoneType}) = -1
/
@@ -429,7 +450,7 @@ Return an empty bytes object at EOF.
static PyObject *
_io_BytesIO_read_impl(bytesio *self, Py_ssize_t size)
-/*[clinic end generated code: output=9cc025f21c75bdd2 input=74344a39f431c3d7]*/
+/*[clinic end generated code: output=9cc025f21c75bdd2 input=9e2f7ff3075fdd39]*/
{
Py_ssize_t n;
@@ -443,11 +464,12 @@ _io_BytesIO_read_impl(bytesio *self, Py_ssize_t size)
size = 0;
}
- return read_bytes(self, size);
+ return read_bytes_lock_held(self, size);
}
/*[clinic input]
+@critical_section
_io.BytesIO.read1
size: Py_ssize_t(accept={int, NoneType}) = -1
/
@@ -460,12 +482,13 @@ Return an empty bytes object at EOF.
static PyObject *
_io_BytesIO_read1_impl(bytesio *self, Py_ssize_t size)
-/*[clinic end generated code: output=d0f843285aa95f1c input=440a395bf9129ef5]*/
+/*[clinic end generated code: output=d0f843285aa95f1c input=a08fc9e507ab380c]*/
{
return _io_BytesIO_read_impl(self, size);
}
/*[clinic input]
+@critical_section
_io.BytesIO.readline
size: Py_ssize_t(accept={int, NoneType}) = -1
/
@@ -479,18 +502,19 @@ Return an empty bytes object at EOF.
static PyObject *
_io_BytesIO_readline_impl(bytesio *self, Py_ssize_t size)
-/*[clinic end generated code: output=4bff3c251df8ffcd input=e7c3fbd1744e2783]*/
+/*[clinic end generated code: output=4bff3c251df8ffcd input=db09d47e23cf2c9e]*/
{
Py_ssize_t n;
CHECK_CLOSED(self);
- n = scan_eol(self, size);
+ n = scan_eol_lock_held(self, size);
- return read_bytes(self, n);
+ return read_bytes_lock_held(self, n);
}
/*[clinic input]
+@critical_section
_io.BytesIO.readlines
size as arg: object = None
/
@@ -504,7 +528,7 @@ total number of bytes in the lines returned.
static PyObject *
_io_BytesIO_readlines_impl(bytesio *self, PyObject *arg)
-/*[clinic end generated code: output=09b8e34c880808ff input=691aa1314f2c2a87]*/
+/*[clinic end generated code: output=09b8e34c880808ff input=5c57d7d78e409985]*/
{
Py_ssize_t maxsize, size, n;
PyObject *result, *line;
@@ -533,7 +557,7 @@ _io_BytesIO_readlines_impl(bytesio *self, PyObject *arg)
return NULL;
output = PyBytes_AS_STRING(self->buf) + self->pos;
- while ((n = scan_eol(self, -1)) != 0) {
+ while ((n = scan_eol_lock_held(self, -1)) != 0) {
self->pos += n;
line = PyBytes_FromStringAndSize(output, n);
if (!line)
@@ -556,6 +580,7 @@ _io_BytesIO_readlines_impl(bytesio *self, PyObject *arg)
}
/*[clinic input]
+@critical_section
_io.BytesIO.readinto
buffer: Py_buffer(accept={rwbuffer})
/
@@ -568,7 +593,7 @@ is set not to block and has no data to read.
static PyObject *
_io_BytesIO_readinto_impl(bytesio *self, Py_buffer *buffer)
-/*[clinic end generated code: output=a5d407217dcf0639 input=1424d0fdce857919]*/
+/*[clinic end generated code: output=a5d407217dcf0639 input=093a8d330de3fcd1]*/
{
Py_ssize_t len, n;
@@ -583,17 +608,18 @@ _io_BytesIO_readinto_impl(bytesio *self, Py_buffer *buffer)
len = 0;
}
- memcpy(buffer->buf, PyBytes_AS_STRING(self->buf) + self->pos, len);
assert(self->pos + len < PY_SSIZE_T_MAX);
assert(len >= 0);
+ memcpy(buffer->buf, PyBytes_AS_STRING(self->buf) + self->pos, len);
self->pos += len;
return PyLong_FromSsize_t(len);
}
/*[clinic input]
+@critical_section
_io.BytesIO.truncate
- size: Py_ssize_t(accept={int, NoneType}, c_default="((bytesio *)self)->pos") = None
+ size: object = None
/
Truncate the file to at most size bytes.
@@ -603,44 +629,68 @@ The current file position is unchanged. Returns the new size.
[clinic start generated code]*/
static PyObject *
-_io_BytesIO_truncate_impl(bytesio *self, Py_ssize_t size)
-/*[clinic end generated code: output=9ad17650c15fa09b input=dae4295e11c1bbb4]*/
+_io_BytesIO_truncate_impl(bytesio *self, PyObject *size)
+/*[clinic end generated code: output=ab42491b4824f384 input=b4acb5f80481c053]*/
{
CHECK_CLOSED(self);
CHECK_EXPORTS(self);
- if (size < 0) {
- PyErr_Format(PyExc_ValueError,
- "negative size value %zd", size);
- return NULL;
+ Py_ssize_t new_size;
+
+ if (size == Py_None) {
+ new_size = self->pos;
+ }
+ else {
+ new_size = PyLong_AsLong(size);
+ if (new_size == -1 && PyErr_Occurred()) {
+ return NULL;
+ }
+ if (new_size < 0) {
+ PyErr_Format(PyExc_ValueError,
+ "negative size value %zd", new_size);
+ return NULL;
+ }
}
- if (size < self->string_size) {
- self->string_size = size;
- if (resize_buffer(self, size) < 0)
+ if (new_size < self->string_size) {
+ self->string_size = new_size;
+ if (resize_buffer_lock_held(self, new_size) < 0)
return NULL;
}
- return PyLong_FromSsize_t(size);
+ return PyLong_FromSsize_t(new_size);
}
static PyObject *
-bytesio_iternext(PyObject *op)
+bytesio_iternext_lock_held(PyObject *op)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
+
Py_ssize_t n;
bytesio *self = bytesio_CAST(op);
CHECK_CLOSED(self);
- n = scan_eol(self, -1);
+ n = scan_eol_lock_held(self, -1);
if (n == 0)
return NULL;
- return read_bytes(self, n);
+ return read_bytes_lock_held(self, n);
+}
+
+static PyObject *
+bytesio_iternext(PyObject *op)
+{
+ PyObject *ret;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ ret = bytesio_iternext_lock_held(op);
+ Py_END_CRITICAL_SECTION();
+ return ret;
}
/*[clinic input]
+@critical_section
_io.BytesIO.seek
pos: Py_ssize_t
whence: int = 0
@@ -657,7 +707,7 @@ Returns the new absolute position.
static PyObject *
_io_BytesIO_seek_impl(bytesio *self, Py_ssize_t pos, int whence)
-/*[clinic end generated code: output=c26204a68e9190e4 input=1e875e6ebc652948]*/
+/*[clinic end generated code: output=c26204a68e9190e4 input=20f05ddf659255df]*/
{
CHECK_CLOSED(self);
@@ -700,6 +750,7 @@ _io_BytesIO_seek_impl(bytesio *self, Py_ssize_t pos, int whence)
}
/*[clinic input]
+@critical_section
_io.BytesIO.write
b: object
/
@@ -711,13 +762,14 @@ Return the number of bytes written.
static PyObject *
_io_BytesIO_write_impl(bytesio *self, PyObject *b)
-/*[clinic end generated code: output=d3e46bcec8d9e21c input=f5ec7c8c64ed720a]*/
+/*[clinic end generated code: output=d3e46bcec8d9e21c input=46c0c17eac7474a4]*/
{
- Py_ssize_t n = write_bytes(self, b);
+ Py_ssize_t n = write_bytes_lock_held(self, b);
return n >= 0 ? PyLong_FromSsize_t(n) : NULL;
}
/*[clinic input]
+@critical_section
_io.BytesIO.writelines
lines: object
/
@@ -731,7 +783,7 @@ each element.
static PyObject *
_io_BytesIO_writelines_impl(bytesio *self, PyObject *lines)
-/*[clinic end generated code: output=03a43a75773bc397 input=e972539176fc8fc1]*/
+/*[clinic end generated code: output=03a43a75773bc397 input=5d6a616ae39dc9ca]*/
{
PyObject *it, *item;
@@ -742,7 +794,7 @@ _io_BytesIO_writelines_impl(bytesio *self, PyObject *lines)
return NULL;
while ((item = PyIter_Next(it)) != NULL) {
- Py_ssize_t ret = write_bytes(self, item);
+ Py_ssize_t ret = write_bytes_lock_held(self, item);
Py_DECREF(item);
if (ret < 0) {
Py_DECREF(it);
@@ -759,6 +811,7 @@ _io_BytesIO_writelines_impl(bytesio *self, PyObject *lines)
}
/*[clinic input]
+@critical_section
_io.BytesIO.close
Disable all I/O operations.
@@ -766,7 +819,7 @@ Disable all I/O operations.
static PyObject *
_io_BytesIO_close_impl(bytesio *self)
-/*[clinic end generated code: output=1471bb9411af84a0 input=37e1f55556e61f60]*/
+/*[clinic end generated code: output=1471bb9411af84a0 input=34ce76d8bd17a23b]*/
{
CHECK_EXPORTS(self);
Py_CLEAR(self->buf);
@@ -788,35 +841,49 @@ _io_BytesIO_close_impl(bytesio *self)
function to use the efficient instance representation of PEP 307.
*/
+ static PyObject *
+ bytesio_getstate_lock_held(PyObject *op)
+ {
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
+
+ bytesio *self = bytesio_CAST(op);
+ PyObject *initvalue = _io_BytesIO_getvalue_impl(self);
+ PyObject *dict;
+ PyObject *state;
+
+ if (initvalue == NULL)
+ return NULL;
+ if (self->dict == NULL) {
+ dict = Py_NewRef(Py_None);
+ }
+ else {
+ dict = PyDict_Copy(self->dict);
+ if (dict == NULL) {
+ Py_DECREF(initvalue);
+ return NULL;
+ }
+ }
+
+ state = Py_BuildValue("(OnN)", initvalue, self->pos, dict);
+ Py_DECREF(initvalue);
+ return state;
+}
+
static PyObject *
bytesio_getstate(PyObject *op, PyObject *Py_UNUSED(dummy))
{
- bytesio *self = bytesio_CAST(op);
- PyObject *initvalue = _io_BytesIO_getvalue_impl(self);
- PyObject *dict;
- PyObject *state;
-
- if (initvalue == NULL)
- return NULL;
- if (self->dict == NULL) {
- dict = Py_NewRef(Py_None);
- }
- else {
- dict = PyDict_Copy(self->dict);
- if (dict == NULL) {
- Py_DECREF(initvalue);
- return NULL;
- }
- }
-
- state = Py_BuildValue("(OnN)", initvalue, self->pos, dict);
- Py_DECREF(initvalue);
- return state;
+ PyObject *ret;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ ret = bytesio_getstate_lock_held(op);
+ Py_END_CRITICAL_SECTION();
+ return ret;
}
static PyObject *
-bytesio_setstate(PyObject *op, PyObject *state)
+bytesio_setstate_lock_held(PyObject *op, PyObject *state)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
+
PyObject *result;
PyObject *position_obj;
PyObject *dict;
@@ -890,21 +957,30 @@ bytesio_setstate(PyObject *op, PyObject *state)
Py_RETURN_NONE;
}
+static PyObject *
+bytesio_setstate(PyObject *op, PyObject *state)
+{
+ PyObject *ret;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ ret = bytesio_setstate_lock_held(op, state);
+ Py_END_CRITICAL_SECTION();
+ return ret;
+}
+
static void
bytesio_dealloc(PyObject *op)
{
bytesio *self = bytesio_CAST(op);
PyTypeObject *tp = Py_TYPE(self);
_PyObject_GC_UNTRACK(self);
- if (self->exports > 0) {
+ if (FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) > 0) {
PyErr_SetString(PyExc_SystemError,
"deallocated BytesIO object has exported buffers");
PyErr_Print();
}
Py_CLEAR(self->buf);
Py_CLEAR(self->dict);
- if (self->weakreflist != NULL)
- PyObject_ClearWeakRefs(op);
+ FT_CLEAR_WEAKREFS(op, self->weakreflist);
tp->tp_free(self);
Py_DECREF(tp);
}
@@ -932,6 +1008,7 @@ bytesio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
}
/*[clinic input]
+@critical_section
_io.BytesIO.__init__
initial_bytes as initvalue: object(c_default="NULL") = b''
@@ -940,13 +1017,13 @@ Buffered I/O implementation using an in-memory bytes buffer.
static int
_io_BytesIO___init___impl(bytesio *self, PyObject *initvalue)
-/*[clinic end generated code: output=65c0c51e24c5b621 input=aac7f31b67bf0fb6]*/
+/*[clinic end generated code: output=65c0c51e24c5b621 input=3da5a74ee4c4f1ac]*/
{
/* In case, __init__ is called multiple times. */
self->string_size = 0;
self->pos = 0;
- if (self->exports > 0) {
+ if (FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) > 0) {
PyErr_SetString(PyExc_BufferError,
"Existing exports of data: object cannot be re-sized");
return -1;
@@ -970,8 +1047,10 @@ _io_BytesIO___init___impl(bytesio *self, PyObject *initvalue)
}
static PyObject *
-bytesio_sizeof(PyObject *op, PyObject *Py_UNUSED(dummy))
+bytesio_sizeof_lock_held(PyObject *op)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
+
bytesio *self = bytesio_CAST(op);
size_t res = _PyObject_SIZE(Py_TYPE(self));
if (self->buf && !SHARED_BUF(self)) {
@@ -984,6 +1063,16 @@ bytesio_sizeof(PyObject *op, PyObject *Py_UNUSED(dummy))
return PyLong_FromSize_t(res);
}
+static PyObject *
+bytesio_sizeof(PyObject *op, PyObject *Py_UNUSED(dummy))
+{
+ PyObject *ret;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ ret = bytesio_sizeof_lock_held(op);
+ Py_END_CRITICAL_SECTION();
+ return ret;
+}
+
static int
bytesio_traverse(PyObject *op, visitproc visit, void *arg)
{
@@ -999,7 +1088,7 @@ bytesio_clear(PyObject *op)
{
bytesio *self = bytesio_CAST(op);
Py_CLEAR(self->dict);
- if (self->exports == 0) {
+ if (FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) == 0) {
Py_CLEAR(self->buf);
}
return 0;
@@ -1077,18 +1166,15 @@ PyType_Spec bytesio_spec = {
*/
static int
-bytesiobuf_getbuffer(PyObject *op, Py_buffer *view, int flags)
+bytesiobuf_getbuffer_lock_held(PyObject *op, Py_buffer *view, int flags)
{
bytesiobuf *obj = bytesiobuf_CAST(op);
bytesio *b = bytesio_CAST(obj->source);
- if (view == NULL) {
- PyErr_SetString(PyExc_BufferError,
- "bytesiobuf_getbuffer: view==NULL argument is obsolete");
- return -1;
- }
- if (b->exports == 0 && SHARED_BUF(b)) {
- if (unshare_buffer(b, b->string_size) < 0)
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(b);
+
+ if (FT_ATOMIC_LOAD_SSIZE_RELAXED(b->exports) == 0 && SHARED_BUF(b)) {
+ if (unshare_buffer_lock_held(b, b->string_size) < 0)
return -1;
}
@@ -1096,16 +1182,32 @@ bytesiobuf_getbuffer(PyObject *op, Py_buffer *view, int flags)
(void)PyBuffer_FillInfo(view, op,
PyBytes_AS_STRING(b->buf), b->string_size,
0, flags);
- b->exports++;
+ FT_ATOMIC_ADD_SSIZE(b->exports, 1);
return 0;
}
+static int
+bytesiobuf_getbuffer(PyObject *op, Py_buffer *view, int flags)
+{
+ if (view == NULL) {
+ PyErr_SetString(PyExc_BufferError,
+ "bytesiobuf_getbuffer: view==NULL argument is obsolete");
+ return -1;
+ }
+
+ int ret;
+ Py_BEGIN_CRITICAL_SECTION(bytesiobuf_CAST(op)->source);
+ ret = bytesiobuf_getbuffer_lock_held(op, view, flags);
+ Py_END_CRITICAL_SECTION();
+ return ret;
+}
+
static void
bytesiobuf_releasebuffer(PyObject *op, Py_buffer *Py_UNUSED(view))
{
bytesiobuf *obj = bytesiobuf_CAST(op);
bytesio *b = bytesio_CAST(obj->source);
- b->exports--;
+ FT_ATOMIC_ADD_SSIZE(b->exports, -1);
}
static int
diff --git a/Modules/_io/clinic/bytesio.c.h b/Modules/_io/clinic/bytesio.c.h
index aaf4884d173..8553ed05f70 100644
--- a/Modules/_io/clinic/bytesio.c.h
+++ b/Modules/_io/clinic/bytesio.c.h
@@ -7,6 +7,7 @@ preserve
# include "pycore_runtime.h" // _Py_ID()
#endif
#include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t()
+#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(_io_BytesIO_readable__doc__,
@@ -96,11 +97,18 @@ _io_BytesIO_getbuffer_impl(bytesio *self, PyTypeObject *cls);
static PyObject *
_io_BytesIO_getbuffer(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
+ PyObject *return_value = NULL;
+
if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
PyErr_SetString(PyExc_TypeError, "getbuffer() takes no arguments");
- return NULL;
+ goto exit;
}
- return _io_BytesIO_getbuffer_impl((bytesio *)self, cls);
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _io_BytesIO_getbuffer_impl((bytesio *)self, cls);
+ Py_END_CRITICAL_SECTION();
+
+exit:
+ return return_value;
}
PyDoc_STRVAR(_io_BytesIO_getvalue__doc__,
@@ -118,7 +126,13 @@ _io_BytesIO_getvalue_impl(bytesio *self);
static PyObject *
_io_BytesIO_getvalue(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- return _io_BytesIO_getvalue_impl((bytesio *)self);
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _io_BytesIO_getvalue_impl((bytesio *)self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
}
PyDoc_STRVAR(_io_BytesIO_isatty__doc__,
@@ -156,7 +170,13 @@ _io_BytesIO_tell_impl(bytesio *self);
static PyObject *
_io_BytesIO_tell(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- return _io_BytesIO_tell_impl((bytesio *)self);
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _io_BytesIO_tell_impl((bytesio *)self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
}
PyDoc_STRVAR(_io_BytesIO_read__doc__,
@@ -190,7 +210,9 @@ _io_BytesIO_read(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
goto exit;
}
skip_optional:
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _io_BytesIO_read_impl((bytesio *)self, size);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -227,7 +249,9 @@ _io_BytesIO_read1(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
goto exit;
}
skip_optional:
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _io_BytesIO_read1_impl((bytesio *)self, size);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -265,7 +289,9 @@ _io_BytesIO_readline(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
goto exit;
}
skip_optional:
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _io_BytesIO_readline_impl((bytesio *)self, size);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -301,7 +327,9 @@ _io_BytesIO_readlines(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
}
arg = args[0];
skip_optional:
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _io_BytesIO_readlines_impl((bytesio *)self, arg);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -332,7 +360,9 @@ _io_BytesIO_readinto(PyObject *self, PyObject *arg)
_PyArg_BadArgument("readinto", "argument", "read-write bytes-like object", arg);
goto exit;
}
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _io_BytesIO_readinto_impl((bytesio *)self, &buffer);
+ Py_END_CRITICAL_SECTION();
exit:
/* Cleanup for buffer */
@@ -356,13 +386,13 @@ PyDoc_STRVAR(_io_BytesIO_truncate__doc__,
{"truncate", _PyCFunction_CAST(_io_BytesIO_truncate), METH_FASTCALL, _io_BytesIO_truncate__doc__},
static PyObject *
-_io_BytesIO_truncate_impl(bytesio *self, Py_ssize_t size);
+_io_BytesIO_truncate_impl(bytesio *self, PyObject *size);
static PyObject *
_io_BytesIO_truncate(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
- Py_ssize_t size = ((bytesio *)self)->pos;
+ PyObject *size = Py_None;
if (!_PyArg_CheckPositional("truncate", nargs, 0, 1)) {
goto exit;
@@ -370,11 +400,11 @@ _io_BytesIO_truncate(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
if (nargs < 1) {
goto skip_optional;
}
- if (!_Py_convert_optional_to_ssize_t(args[0], &size)) {
- goto exit;
- }
+ size = args[0];
skip_optional:
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _io_BytesIO_truncate_impl((bytesio *)self, size);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -428,7 +458,9 @@ _io_BytesIO_seek(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
goto exit;
}
skip_optional:
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _io_BytesIO_seek_impl((bytesio *)self, pos, whence);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -453,7 +485,9 @@ _io_BytesIO_write(PyObject *self, PyObject *b)
{
PyObject *return_value = NULL;
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _io_BytesIO_write_impl((bytesio *)self, b);
+ Py_END_CRITICAL_SECTION();
return return_value;
}
@@ -479,7 +513,9 @@ _io_BytesIO_writelines(PyObject *self, PyObject *lines)
{
PyObject *return_value = NULL;
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _io_BytesIO_writelines_impl((bytesio *)self, lines);
+ Py_END_CRITICAL_SECTION();
return return_value;
}
@@ -499,7 +535,13 @@ _io_BytesIO_close_impl(bytesio *self);
static PyObject *
_io_BytesIO_close(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- return _io_BytesIO_close_impl((bytesio *)self);
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _io_BytesIO_close_impl((bytesio *)self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
}
PyDoc_STRVAR(_io_BytesIO___init____doc__,
@@ -558,9 +600,11 @@ _io_BytesIO___init__(PyObject *self, PyObject *args, PyObject *kwargs)
}
initvalue = fastargs[0];
skip_optional_pos:
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _io_BytesIO___init___impl((bytesio *)self, initvalue);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
}
-/*[clinic end generated code: output=6dbfd82f4e9d4ef3 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=580205daa01def2e input=a9049054013a1b77]*/
diff --git a/Modules/_io/clinic/stringio.c.h b/Modules/_io/clinic/stringio.c.h
index 8e8cd8df9ab..83165e5f7ad 100644
--- a/Modules/_io/clinic/stringio.c.h
+++ b/Modules/_io/clinic/stringio.c.h
@@ -149,13 +149,13 @@ PyDoc_STRVAR(_io_StringIO_truncate__doc__,
{"truncate", _PyCFunction_CAST(_io_StringIO_truncate), METH_FASTCALL, _io_StringIO_truncate__doc__},
static PyObject *
-_io_StringIO_truncate_impl(stringio *self, Py_ssize_t size);
+_io_StringIO_truncate_impl(stringio *self, PyObject *pos);
static PyObject *
_io_StringIO_truncate(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
- Py_ssize_t size = ((stringio *)self)->pos;
+ PyObject *pos = Py_None;
if (!_PyArg_CheckPositional("truncate", nargs, 0, 1)) {
goto exit;
@@ -163,12 +163,10 @@ _io_StringIO_truncate(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
if (nargs < 1) {
goto skip_optional;
}
- if (!_Py_convert_optional_to_ssize_t(args[0], &size)) {
- goto exit;
- }
+ pos = args[0];
skip_optional:
Py_BEGIN_CRITICAL_SECTION(self);
- return_value = _io_StringIO_truncate_impl((stringio *)self, size);
+ return_value = _io_StringIO_truncate_impl((stringio *)self, pos);
Py_END_CRITICAL_SECTION();
exit:
@@ -552,4 +550,4 @@ _io_StringIO_newlines_get(PyObject *self, void *Py_UNUSED(context))
return return_value;
}
-/*[clinic end generated code: output=5bfaaab7f41ee6b5 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=bccc25ef8e6ce9ef input=a9049054013a1b77]*/
diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c
index 0c5424954be..26537fc6395 100644
--- a/Modules/_io/fileio.c
+++ b/Modules/_io/fileio.c
@@ -4,6 +4,7 @@
#include "pycore_fileutils.h" // _Py_BEGIN_SUPPRESS_IPH
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1()
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
#include <stdbool.h> // bool
#ifdef HAVE_UNISTD_H
@@ -570,9 +571,7 @@ fileio_dealloc(PyObject *op)
PyMem_Free(self->stat_atopen);
self->stat_atopen = NULL;
}
- if (self->weakreflist != NULL) {
- PyObject_ClearWeakRefs(op);
- }
+ FT_CLEAR_WEAKREFS(op, self->weakreflist);
(void)fileio_clear(op);
PyTypeObject *tp = Py_TYPE(op);
@@ -1262,8 +1261,7 @@ static PyMethodDef fileio_methods[] = {
_IO_FILEIO_ISATTY_METHODDEF
{"_isatty_open_only", _io_FileIO_isatty_open_only, METH_NOARGS},
{"_dealloc_warn", fileio_dealloc_warn, METH_O, NULL},
- {"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS},
- {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O},
+ {"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS},
{NULL, NULL} /* sentinel */
};
diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c
index cd4c7e7cead..044f6b7803c 100644
--- a/Modules/_io/iobase.c
+++ b/Modules/_io/iobase.c
@@ -14,6 +14,7 @@
#include "pycore_long.h" // _PyLong_GetOne()
#include "pycore_object.h" // _PyType_HasFeature()
#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1()
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
#include <stddef.h> // offsetof()
#include "_iomodule.h"
@@ -383,8 +384,7 @@ iobase_dealloc(PyObject *op)
}
PyTypeObject *tp = Py_TYPE(self);
_PyObject_GC_UNTRACK(self);
- if (self->weakreflist != NULL)
- PyObject_ClearWeakRefs(op);
+ FT_CLEAR_WEAKREFS(op, self->weakreflist);
Py_CLEAR(self->dict);
tp->tp_free(self);
Py_DECREF(tp);
diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c
index 9d1bfa3ea05..20b7cfc0088 100644
--- a/Modules/_io/stringio.c
+++ b/Modules/_io/stringio.c
@@ -1,6 +1,7 @@
#include "Python.h"
#include <stddef.h> // offsetof()
#include "pycore_object.h"
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
#include "_iomodule.h"
/* Implementation note: the buffer is always at least one character longer
@@ -444,7 +445,7 @@ stringio_iternext(PyObject *op)
/*[clinic input]
@critical_section
_io.StringIO.truncate
- pos as size: Py_ssize_t(accept={int, NoneType}, c_default="((stringio *)self)->pos") = None
+ pos: object = None
/
Truncate size to pos.
@@ -455,16 +456,26 @@ Returns the new absolute position.
[clinic start generated code]*/
static PyObject *
-_io_StringIO_truncate_impl(stringio *self, Py_ssize_t size)
-/*[clinic end generated code: output=eb3aef8e06701365 input=fa8a6c98bb2ba780]*/
+_io_StringIO_truncate_impl(stringio *self, PyObject *pos)
+/*[clinic end generated code: output=c76c43b5ecfaf4e2 input=d59fd2ee49757ae6]*/
{
CHECK_INITIALIZED(self);
CHECK_CLOSED(self);
- if (size < 0) {
- PyErr_Format(PyExc_ValueError,
- "Negative size value %zd", size);
- return NULL;
+ Py_ssize_t size;
+ if (pos == Py_None) {
+ size = self->pos;
+ }
+ else {
+ size = PyLong_AsLong(pos);
+ if (size == -1 && PyErr_Occurred()) {
+ return NULL;
+ }
+ if (size < 0) {
+ PyErr_Format(PyExc_ValueError,
+ "negative pos value %zd", size);
+ return NULL;
+ }
}
if (size < self->string_size) {
@@ -628,9 +639,7 @@ stringio_dealloc(PyObject *op)
}
PyUnicodeWriter_Discard(self->writer);
(void)stringio_clear(op);
- if (self->weakreflist != NULL) {
- PyObject_ClearWeakRefs(op);
- }
+ FT_CLEAR_WEAKREFS(op, self->weakreflist);
tp->tp_free(self);
Py_DECREF(tp);
}
diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c
index e77d8448310..5354cf63442 100644
--- a/Modules/_io/textio.c
+++ b/Modules/_io/textio.c
@@ -16,6 +16,7 @@
#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_unicodeobject.h" // _PyUnicode_AsASCIIString()
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
#include "_iomodule.h"
@@ -1185,7 +1186,7 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer,
}
/* Check we have been asked for a real text encoding */
- codec_info = _PyCodec_LookupTextEncoding(encoding, "codecs.open()");
+ codec_info = _PyCodec_LookupTextEncoding(encoding, NULL);
if (codec_info == NULL) {
Py_CLEAR(self->encoding);
goto error;
@@ -1324,8 +1325,7 @@ textiowrapper_change_encoding(textio *self, PyObject *encoding,
}
// Create new encoder & decoder
- PyObject *codec_info = _PyCodec_LookupTextEncoding(
- c_encoding, "codecs.open()");
+ PyObject *codec_info = _PyCodec_LookupTextEncoding(c_encoding, NULL);
if (codec_info == NULL) {
Py_DECREF(encoding);
Py_DECREF(errors);
@@ -1470,8 +1470,7 @@ textiowrapper_dealloc(PyObject *op)
return;
self->ok = 0;
_PyObject_GC_UNTRACK(self);
- if (self->weakreflist != NULL)
- PyObject_ClearWeakRefs(op);
+ FT_CLEAR_WEAKREFS(op, self->weakreflist);
(void)textiowrapper_clear(op);
tp->tp_free(self);
Py_DECREF(tp);
@@ -1579,6 +1578,8 @@ _io_TextIOWrapper_detach_impl(textio *self)
static int
_textiowrapper_writeflush(textio *self)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
+
if (self->pending_bytes == NULL)
return 0;
@@ -3174,8 +3175,9 @@ _io_TextIOWrapper_close_impl(textio *self)
}
static PyObject *
-textiowrapper_iternext(PyObject *op)
+textiowrapper_iternext_lock_held(PyObject *op)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
PyObject *line;
textio *self = textio_CAST(op);
@@ -3211,6 +3213,16 @@ textiowrapper_iternext(PyObject *op)
return line;
}
+static PyObject *
+textiowrapper_iternext(PyObject *op)
+{
+ PyObject *result;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ result = textiowrapper_iternext_lock_held(op);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
/*[clinic input]
@critical_section
@getter
@@ -3367,8 +3379,7 @@ static PyMethodDef textiowrapper_methods[] = {
_IO_TEXTIOWRAPPER_TELL_METHODDEF
_IO_TEXTIOWRAPPER_TRUNCATE_METHODDEF
- {"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS},
- {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O},
+ {"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS},
{NULL, NULL}
};
diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c
index 3e783b9da45..950b7fe241c 100644
--- a/Modules/_io/winconsoleio.c
+++ b/Modules/_io/winconsoleio.c
@@ -10,6 +10,7 @@
#include "pycore_fileutils.h" // _Py_BEGIN_SUPPRESS_IPH
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1()
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
#ifdef HAVE_WINDOWS_CONSOLE_IO
@@ -518,8 +519,7 @@ winconsoleio_dealloc(PyObject *op)
if (_PyIOBase_finalize(op) < 0)
return;
_PyObject_GC_UNTRACK(self);
- if (self->weakreflist != NULL)
- PyObject_ClearWeakRefs(op);
+ FT_CLEAR_WEAKREFS(op, self->weakreflist);
Py_CLEAR(self->dict);
tp->tp_free(self);
Py_DECREF(tp);