@@ -22,6 +22,9 @@ typedef struct {
2222 PyObject * dict ;
2323 PyObject * weakreflist ;
2424 Py_ssize_t exports ;
25+ #ifdef Py_GIL_DISABLED
26+ int buf_shared ;
27+ #endif
2528} bytesio ;
2629
2730#define bytesio_CAST (op ) ((bytesio *)(op))
@@ -71,7 +74,45 @@ check_exports(bytesio *self)
7174 return NULL; \
7275 }
7376
77+ #ifdef Py_GIL_DISABLED
78+ #define SHARED_BUF (self ) ((self)->buf_shared || !_PyObject_IsUniquelyReferenced((self)->buf))
79+ #else
7480#define SHARED_BUF (self ) (!_PyObject_IsUniquelyReferenced((self)->buf))
81+ #endif
82+
83+ static inline void
84+ set_shared_buf (bytesio * self )
85+ {
86+ #ifdef Py_GIL_DISABLED
87+ self -> buf_shared = 1 ;
88+ #endif
89+ }
90+
91+ static inline void
92+ clear_shared_buf (bytesio * self )
93+ {
94+ #ifdef Py_GIL_DISABLED
95+ self -> buf_shared = 0 ;
96+ #endif
97+ }
98+
99+ static int
100+ resize_unshared_buffer_lock_held (bytesio * self , Py_ssize_t size )
101+ {
102+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (self );
103+
104+ #ifdef Py_GIL_DISABLED
105+ /* If the internal bytes object escaped via a zero-copy getvalue(), read(),
106+ or peek(), resizing it would mutate an object visible to Python code.
107+ Callers must detach first. */
108+ assert (!self -> buf_shared );
109+ #endif
110+ int ret = _PyBytes_Resize (& self -> buf , size );
111+ if (ret == 0 ) {
112+ clear_shared_buf (self );
113+ }
114+ return ret ;
115+ }
75116
76117
77118/* Internal routine to get a line from the buffer of a BytesIO
@@ -128,6 +169,7 @@ unshare_buffer_lock_held(bytesio *self, size_t size)
128169 memcpy (PyBytes_AS_STRING (new_buf ), PyBytes_AS_STRING (self -> buf ),
129170 self -> string_size );
130171 Py_SETREF (self -> buf , new_buf );
172+ clear_shared_buf (self );
131173 return 0 ;
132174}
133175
@@ -173,7 +215,7 @@ resize_buffer_lock_held(bytesio *self, size_t size)
173215 return -1 ;
174216 }
175217 else {
176- if (_PyBytes_Resize ( & self -> buf , alloc ) < 0 )
218+ if (resize_unshared_buffer_lock_held ( self , alloc ) < 0 )
177219 return -1 ;
178220 }
179221
@@ -381,10 +423,11 @@ _io_BytesIO_getvalue_impl(bytesio *self)
381423 return NULL ;
382424 }
383425 else {
384- if (_PyBytes_Resize ( & self -> buf , self -> string_size ) < 0 )
426+ if (resize_unshared_buffer_lock_held ( self , self -> string_size ) < 0 )
385427 return NULL ;
386428 }
387429 }
430+ set_shared_buf (self );
388431 return Py_NewRef (self -> buf );
389432}
390433
@@ -433,6 +476,7 @@ peek_bytes_lock_held(bytesio *self, Py_ssize_t size)
433476 if (size > 1 &&
434477 self -> pos == 0 && size == PyBytes_GET_SIZE (self -> buf ) &&
435478 FT_ATOMIC_LOAD_SSIZE_RELAXED (self -> exports ) == 0 ) {
479+ set_shared_buf (self );
436480 return Py_NewRef (self -> buf );
437481 }
438482
@@ -1091,6 +1135,7 @@ _io_BytesIO___init___impl(bytesio *self, PyObject *initvalue)
10911135 if (initvalue && initvalue != Py_None ) {
10921136 if (PyBytes_CheckExact (initvalue )) {
10931137 Py_XSETREF (self -> buf , Py_NewRef (initvalue ));
1138+ clear_shared_buf (self );
10941139 self -> string_size = PyBytes_GET_SIZE (initvalue );
10951140 }
10961141 else {
0 commit comments