@@ -447,3 +447,104 @@ atomic:
447447
448448 Consider external synchronization when sharing :class: `set ` instances
449449across threads. See :ref: `freethreading-python-howto ` for more information.
450+
451+
452+ .. _thread-safety-bytearray :
453+
454+ Thread safety for bytearray objects
455+ ===================================
456+
457+ The :func: `len ` function is lock-free and :term: `atomic <atomic operation> `.
458+
459+ Concatenation and comparisons use the buffer protocol, which prevents
460+ resizing but does not hold the per-object lock. These operations may
461+ observe intermediate states from concurrent modifications:
462+
463+ .. code-block ::
464+ :class: maybe
465+
466+ ba + other # may observe concurrent writes
467+ ba == other # may observe concurrent writes
468+ ba < other # may observe concurrent writes
469+
470+ All other operations from here on hold the per-object lock.
471+
472+ Reading a single element or slice is safe to call from multiple threads:
473+
474+ .. code-block ::
475+ :class: good
476+
477+ ba[i] # bytearray.__getitem__
478+ ba[i:j] # slice
479+
480+ The following operations are safe to call from multiple threads and will
481+ not corrupt the bytearray:
482+
483+ .. code-block ::
484+ :class: good
485+
486+ ba[i] = x # write single byte
487+ ba[i:j] = values # write slice
488+ ba.append(x) # append single byte
489+ ba.extend(other) # extend with iterable
490+ ba.insert(i, x) # insert single byte
491+ ba.pop() # remove and return last byte
492+ ba.pop(i) # remove and return byte at index
493+ ba.remove(x) # remove first occurrence
494+ ba.reverse() # reverse in place
495+ ba.clear() # remove all bytes
496+
497+ Slice assignment locks both objects when *values * is a :class: `bytearray `:
498+
499+ .. code-block ::
500+ :class: good
501+
502+ ba[i:j] = other_bytearray # both locked
503+
504+ The following operations return new objects and hold the per-object lock
505+ for the duration:
506+
507+ .. code-block ::
508+ :class: good
509+
510+ ba.copy() # returns a shallow copy
511+ ba * n # repeat into new bytearray
512+
513+ The membership test holds the lock for its duration:
514+
515+ .. code-block ::
516+ :class: good
517+
518+ x in ba # bytearray.__contains__
519+
520+ All other bytearray methods (such as :meth: `~bytearray.find `,
521+ :meth: `~bytearray.replace `, :meth: `~bytearray.split `,
522+ :meth: `~bytearray.decode `, etc.) hold the per-object lock for their
523+ duration.
524+
525+ Operations that involve multiple accesses, as well as iteration, are never
526+ atomic:
527+
528+ .. code-block ::
529+ :class: bad
530+
531+ # NOT atomic: check-then-act
532+ if x in ba:
533+ ba.remove(x)
534+
535+ # NOT thread-safe: iteration while modifying
536+ for byte in ba:
537+ process(byte) # another thread may modify ba
538+
539+ To safely iterate over a bytearray that may be modified by another
540+ thread, iterate over a copy:
541+
542+ .. code-block ::
543+ :class: good
544+
545+ # Make a copy to iterate safely
546+ for byte in ba.copy():
547+ process(byte)
548+
549+ Consider external synchronization when sharing :class: `bytearray ` instances
550+ across threads. See :ref: `freethreading-python-howto ` for more information.
0 commit comments