summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_memoryio.py21
-rw-r--r--Misc/NEWS.d/next/Library/2023-10-23-18-42-26.gh-issue-111049.Ys7-o_.rst2
-rw-r--r--Modules/_io/bytesio.c14
3 files changed, 27 insertions, 10 deletions
diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py
index 7312992..8192502 100644
--- a/Lib/test/test_memoryio.py
+++ b/Lib/test/test_memoryio.py
@@ -6,10 +6,12 @@ BytesIO -- for bytes
import unittest
from test import support
+import gc
import io
import _pyio as pyio
import pickle
import sys
+import weakref
class IntLike:
def __init__(self, num):
@@ -477,6 +479,25 @@ class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase):
buf2.release()
memio.write(b'x')
+ def test_getbuffer_gc_collect(self):
+ memio = self.ioclass(b"1234567890")
+ buf = memio.getbuffer()
+ memiowr = weakref.ref(memio)
+ bufwr = weakref.ref(buf)
+ # Create a reference loop.
+ a = [buf]
+ a.append(a)
+ # The Python implementation emits an unraisable exception.
+ with support.catch_unraisable_exception():
+ del memio
+ del buf
+ del a
+ # The C implementation emits an unraisable exception.
+ with support.catch_unraisable_exception():
+ gc.collect()
+ self.assertIsNone(memiowr())
+ self.assertIsNone(bufwr())
+
def test_read1(self):
buf = self.buftype("1234567890")
self.assertEqual(self.ioclass(buf).read1(), buf)
diff --git a/Misc/NEWS.d/next/Library/2023-10-23-18-42-26.gh-issue-111049.Ys7-o_.rst b/Misc/NEWS.d/next/Library/2023-10-23-18-42-26.gh-issue-111049.Ys7-o_.rst
new file mode 100644
index 0000000..b1de348
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-10-23-18-42-26.gh-issue-111049.Ys7-o_.rst
@@ -0,0 +1,2 @@
+Fix crash during garbage collection of the :class:`io.BytesIO` buffer
+object.
diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c
index 16b8ac6..4a15c8e 100644
--- a/Modules/_io/bytesio.c
+++ b/Modules/_io/bytesio.c
@@ -990,7 +990,9 @@ static int
bytesio_clear(bytesio *self)
{
Py_CLEAR(self->dict);
- Py_CLEAR(self->buf);
+ if (self->exports == 0) {
+ Py_CLEAR(self->buf);
+ }
return 0;
}
@@ -1096,13 +1098,6 @@ bytesiobuf_releasebuffer(bytesiobuf *obj, Py_buffer *view)
}
static int
-bytesiobuf_clear(bytesiobuf *self)
-{
- Py_CLEAR(self->source);
- return 0;
-}
-
-static int
bytesiobuf_traverse(bytesiobuf *self, visitproc visit, void *arg)
{
Py_VISIT(Py_TYPE(self));
@@ -1116,7 +1111,7 @@ bytesiobuf_dealloc(bytesiobuf *self)
PyTypeObject *tp = Py_TYPE(self);
/* bpo-31095: UnTrack is needed before calling any callbacks */
PyObject_GC_UnTrack(self);
- (void)bytesiobuf_clear(self);
+ Py_CLEAR(self->source);
tp->tp_free(self);
Py_DECREF(tp);
}
@@ -1124,7 +1119,6 @@ bytesiobuf_dealloc(bytesiobuf *self)
static PyType_Slot bytesiobuf_slots[] = {
{Py_tp_dealloc, bytesiobuf_dealloc},
{Py_tp_traverse, bytesiobuf_traverse},
- {Py_tp_clear, bytesiobuf_clear},
// Buffer protocol
{Py_bf_getbuffer, bytesiobuf_getbuffer},