Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/library/stdtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3472,6 +3472,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:

Expand Down
101 changes: 101 additions & 0 deletions Doc/library/threadsafety.rst
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,104 @@ thread, iterate over a copy:

Consider external synchronization when sharing :class:`dict` instances
across threads.


.. _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.
Loading