[3.14] gh-142518: Document thread-safety guarantees of bytearray objects (GH-145226) (#145982)

(cherry picked from commit 2f4e4ec2e7)

Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com>
This commit is contained in:
Miss Islington (bot) 2026-03-15 16:03:32 +01:00 committed by GitHub
parent 5fa025aebe
commit 64e2acbc8e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 106 additions and 0 deletions

View file

@ -3478,6 +3478,11 @@ The representation of bytearray objects uses the bytes literal format
``bytearray([46, 46, 46])``. You can always convert a bytearray object into
a list of integers using ``list(b)``.
.. seealso::
For detailed information on thread-safety guarantees for :class:`bytearray`
objects, see :ref:`thread-safety-bytearray`.
.. _bytes-methods:

View file

@ -447,3 +447,104 @@ atomic:
Consider external synchronization when sharing :class:`set` instances
across threads. See :ref:`freethreading-python-howto` for more information.
.. _thread-safety-bytearray:
Thread safety for bytearray objects
===================================
The :func:`len` function is lock-free and :term:`atomic <atomic operation>`.
Concatenation and comparisons use the buffer protocol, which prevents
resizing but does not hold the per-object lock. These operations may
observe intermediate states from concurrent modifications:
.. code-block::
:class: maybe
ba + other # may observe concurrent writes
ba == other # may observe concurrent writes
ba < other # may observe concurrent writes
All other operations from here on hold the per-object lock.
Reading a single element or slice is safe to call from multiple threads:
.. code-block::
:class: good
ba[i] # bytearray.__getitem__
ba[i:j] # slice
The following operations are safe to call from multiple threads and will
not corrupt the bytearray:
.. code-block::
:class: good
ba[i] = x # write single byte
ba[i:j] = values # write slice
ba.append(x) # append single byte
ba.extend(other) # extend with iterable
ba.insert(i, x) # insert single byte
ba.pop() # remove and return last byte
ba.pop(i) # remove and return byte at index
ba.remove(x) # remove first occurrence
ba.reverse() # reverse in place
ba.clear() # remove all bytes
Slice assignment locks both objects when *values* is a :class:`bytearray`:
.. code-block::
:class: good
ba[i:j] = other_bytearray # both locked
The following operations return new objects and hold the per-object lock
for the duration:
.. code-block::
:class: good
ba.copy() # returns a shallow copy
ba * n # repeat into new bytearray
The membership test holds the lock for its duration:
.. code-block::
:class: good
x in ba # bytearray.__contains__
All other bytearray methods (such as :meth:`~bytearray.find`,
:meth:`~bytearray.replace`, :meth:`~bytearray.split`,
:meth:`~bytearray.decode`, etc.) hold the per-object lock for their
duration.
Operations that involve multiple accesses, as well as iteration, are never
atomic:
.. code-block::
:class: bad
# NOT atomic: check-then-act
if x in ba:
ba.remove(x)
# NOT thread-safe: iteration while modifying
for byte in ba:
process(byte) # another thread may modify ba
To safely iterate over a bytearray that may be modified by another
thread, iterate over a copy:
.. code-block::
:class: good
# Make a copy to iterate safely
for byte in ba.copy():
process(byte)
Consider external synchronization when sharing :class:`bytearray` instances
across threads. See :ref:`freethreading-python-howto` for more information.