@@ -447,3 +447,160 @@ 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.
551+
552+
553+ .. _thread-safety-memoryview :
554+
555+ Thread safety for memoryview objects
556+ ====================================
557+
558+ :class: `memoryview ` objects provide access to the internal data of an
559+ underlying object without copying. Thread safety depends on both the
560+ memoryview itself and the underlying buffer exporter.
561+
562+ The memoryview implementation uses atomic operations to track its own
563+ exports in the :term: `free-threaded build `. Creating and
564+ releasing a memoryview are thread-safe. Attribute access (e.g.,
565+ :attr: `~memoryview.shape `, :attr: `~memoryview.format `) reads fields that
566+ are immutable for the lifetime of the memoryview, so concurrent reads
567+ are safe as long as the memoryview has not been released.
568+
569+ However, the actual data accessed through the memoryview is owned by the
570+ underlying object. Concurrent access to this data is only safe if the
571+ underlying object supports it:
572+
573+ * For immutable objects like :class: `bytes `, concurrent reads through
574+ multiple memoryviews are safe.
575+
576+ * For mutable objects like :class: `bytearray `, reading and writing the
577+ same memory region from multiple threads without external
578+ synchronization is not safe and may result in data corruption.
579+ Note that even read-only memoryviews of mutable objects do not
580+ prevent data races if the underlying object is modified from
581+ another thread.
582+
583+ .. code-block ::
584+ :class: bad
585+
586+ # NOT safe: concurrent writes to the same buffer
587+ data = bytearray(1000)
588+ view = memoryview(data)
589+ # Thread 1: view[0:500] = b'x' * 500
590+ # Thread 2: view[0:500] = b'y' * 500
591+
592+ .. code-block ::
593+ :class: good
594+
595+ # Safe: use a lock for concurrent access
596+ import threading
597+ lock = threading.Lock()
598+ data = bytearray(1000)
599+ view = memoryview(data)
600+
601+ with lock:
602+ view[0:500] = b'x' * 500
603+
604+ Resizing or reallocating the underlying object (such as calling
605+ :meth: `bytearray.resize `) while a memoryview is exported raises
606+ :exc: `BufferError `. This is enforced regardless of threading.
0 commit comments