https://github.com/python/cpython/commit/847f83ef1c1693d75cc024b31c3dcb9bcaca826f
commit: 847f83ef1c1693d75cc024b31c3dcb9bcaca826f
branch: main
author: Lysandros Nikolaou <[email protected]>
committer: lysnikolaou <[email protected]>
date: 2026-03-18T14:18:28+01:00
summary:

gh-142518: Add thread safety notes for the buffer protocol (#145911)

files:
M Doc/c-api/typeobj.rst
M Doc/library/stdtypes.rst
M Doc/library/threadsafety.rst
M Doc/reference/datamodel.rst

diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst
index 87b488912653b9..cd13c0f4d61a42 100644
--- a/Doc/c-api/typeobj.rst
+++ b/Doc/c-api/typeobj.rst
@@ -3057,6 +3057,24 @@ Buffer Object Structures
 
    (5) Return ``0``.
 
+   **Thread safety:**
+
+   In the :term:`free-threaded build`, implementations must ensure:
+
+   * The export counter increment in step (3) is atomic.
+
+   * The underlying buffer data remains valid and at a stable memory
+     location for the lifetime of all exports.
+
+   * For objects that support resizing or reallocation (such as
+     :class:`bytearray`), the export counter is checked atomically before
+     such operations, and :exc:`BufferError` is raised if exports exist.
+
+   * The function is safe to call concurrently from multiple threads.
+
+   See also :ref:`thread-safety-memoryview` for the Python-level
+   thread safety guarantees of :class:`memoryview` objects.
+
    If *exporter* is part of a chain or tree of buffer providers, two main
    schemes can be used:
 
@@ -3102,6 +3120,16 @@ Buffer Object Structures
 
    (2) If the counter is ``0``, free all memory associated with *view*.
 
+   **Thread safety:**
+
+   In the :term:`free-threaded build`:
+
+   * The export counter decrement in step (1) must be atomic.
+
+   * Resource cleanup when the counter reaches zero must be done atomically,
+     as the final release may race with concurrent releases from other
+     threads and dellocation must only happen once.
+
    The exporter MUST use the :c:member:`~Py_buffer.internal` field to keep
    track of buffer-specific resources. This field is guaranteed to remain
    constant, while a consumer MAY pass a copy of the original buffer as the
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index 24f53a3a272d73..48291622d7be89 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -5045,6 +5045,9 @@ copying.
 
       .. versionadded:: 3.3
 
+For information on the thread safety of :class:`memoryview` objects in
+the :term:`free-threaded build`, see :ref:`thread-safety-memoryview`.
+
 
 .. _types-set:
 
diff --git a/Doc/library/threadsafety.rst b/Doc/library/threadsafety.rst
index 8063c2ea5011e7..a529f7803affbc 100644
--- a/Doc/library/threadsafety.rst
+++ b/Doc/library/threadsafety.rst
@@ -548,3 +548,59 @@ Thread safety for bytearray objects
 
    Consider external synchronization when sharing :class:`bytearray` instances
    across threads.  See :ref:`freethreading-python-howto` for more information.
+
+
+.. _thread-safety-memoryview:
+
+Thread safety for memoryview objects
+====================================
+
+:class:`memoryview` objects provide access to the internal data of an
+underlying object without copying. Thread safety depends on both the
+memoryview itself and the underlying buffer exporter.
+
+The memoryview implementation uses atomic operations to track its own
+exports in the :term:`free-threaded build`. Creating and
+releasing a memoryview are thread-safe. Attribute access (e.g.,
+:attr:`~memoryview.shape`, :attr:`~memoryview.format`) reads fields that
+are immutable for the lifetime of the memoryview, so concurrent reads
+are safe as long as the memoryview has not been released.
+
+However, the actual data accessed through the memoryview is owned by the
+underlying object. Concurrent access to this data is only safe if the
+underlying object supports it:
+
+* For immutable objects like :class:`bytes`, concurrent reads through
+  multiple memoryviews are safe.
+
+* For mutable objects like :class:`bytearray`, reading and writing the
+  same memory region from multiple threads without external
+  synchronization is not safe and may result in data corruption.
+  Note that even read-only memoryviews of mutable objects do not
+  prevent data races if the underlying object is modified from
+  another thread.
+
+.. code-block::
+   :class: bad
+
+   # NOT safe: concurrent writes to the same buffer
+   data = bytearray(1000)
+   view = memoryview(data)
+   # Thread 1: view[0:500] = b'x' * 500
+   # Thread 2: view[0:500] = b'y' * 500
+
+.. code-block::
+   :class: good
+
+   # Safe: use a lock for concurrent access
+   import threading
+   lock = threading.Lock()
+   data = bytearray(1000)
+   view = memoryview(data)
+
+   with lock:
+       view[0:500] = b'x' * 500
+
+Resizing or reallocating the underlying object (such as calling
+:meth:`bytearray.resize`) while a memoryview is exported raises
+:exc:`BufferError`. This is enforced regardless of threading.
diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst
index 90b8821daaf3fb..1e53c0e0e6f971 100644
--- a/Doc/reference/datamodel.rst
+++ b/Doc/reference/datamodel.rst
@@ -3637,12 +3637,25 @@ implement the protocol in Python.
    provides a convenient way to interpret the flags. The method must return
    a :class:`memoryview` object.
 
+   **Thread safety:** In :term:`free-threaded <free threading>` Python,
+   implementations must manage any internal export counter using atomic
+   operations. The method must be safe to call concurrently from multiple
+   threads, and the returned buffer's underlying data must remain valid
+   until the corresponding :meth:`~object.__release_buffer__` call
+   completes. See :ref:`thread-safety-memoryview` for details.
+
 .. method:: object.__release_buffer__(self, buffer)
 
    Called when a buffer is no longer needed. The *buffer* argument is a
    :class:`memoryview` object that was previously returned by
    :meth:`~object.__buffer__`. The method must release any resources associated
    with the buffer. This method should return ``None``.
+
+   **Thread safety:** In :term:`free-threaded <free threading>` Python,
+   any export counter decrement must use atomic operations. Resource
+   cleanup must be thread-safe, as the final release may race with
+   concurrent releases from other threads.
+
    Buffer objects that do not need to perform any cleanup are not required
    to implement this method.
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to