gh-151640: avoid sharing BytesIO buffer in free-threaded builds#151651
gh-151640: avoid sharing BytesIO buffer in free-threaded builds#151651pedramkarimii wants to merge 4 commits into
Conversation
|
Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool. If this change has little impact on Python users, wait for a maintainer to apply the |
|
Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool. If this change has little impact on Python users, wait for a maintainer to apply the |
|
Additional context: This PR is intended to fix the specific While investigating the code path, I also found that The chosen fix is intentionally conservative: in free-threaded builds, avoid returning the internal I also considered fixing this lower in the resize/shared-buffer ownership path, but that seemed more invasive because it would require changing the interaction between |
| #ifndef Py_GIL_DISABLED | ||
| if (size > 1 && | ||
| self->pos == 0 && size == PyBytes_GET_SIZE(self->buf) && | ||
| FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) == 0) { |
There was a problem hiding this comment.
FT_ATOMIC calls inside a block where Py_GIL_DISABLED is never defined are useless. Let's remove them.
There was a problem hiding this comment.
Done, thanks. I simplified the exports checks in the non-Py_GIL_DISABLED paths.
15cf615 to
0359497
Compare
82e72df to
b26fbb8
Compare
b26fbb8 to
33bc6c8
Compare
|
I think always copying |
Fixes gh-151640.
In free-threaded builds, whole-buffer
BytesIO.read()andBytesIO.peek()could returnself->bufdirectly viaPy_NewRef().BytesIO.getvalue()could also return the internal buffer directly. A concurrent writer could then resize that bytes object while another thread decrefs a returned reference, producing a TSAN-reported race between_PyBytes_Resize()and_Py_DecRefShared().This change avoids exposing the internal
BytesIObuffer inPy_GIL_DISABLEDbuilds. Whole-bufferread(),peek(), andgetvalue()now return a copy in free-threaded builds, while preserving the existing fast path for regular GIL builds.A focused free-threading regression test covers concurrent whole-buffer
read(),peek(), andgetvalue()calls with buffer-resizing writes.Tests run:
./python -m test -v test_free_threading.test_io -m test_concurrent_whole_buffer_read_and_resize./python -m test -v test_free_threading.test_io./python -m test -v test_io./python -m test -v test_free_threading./python -m test -v -R 3:3 test_free_threading.test_io -m test_concurrent_whole_buffer_read_and_resizemake patchcheck