diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index a33da367e6071f..a5125dcbc3c2ee 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -3024,6 +3024,22 @@ Buffer Object Structures (5) Return ``0``. + **Thread safety:** + + In :term:`free-threaded ` Python, implementations must ensure: + + * The export counter increment in step (3) must be atomic (e.g., using + :c:func:`_Py_atomic_add_ssize` or equivalent). + + * The underlying buffer data must remain valid and at a stable memory + location until all exports are released. + + * For objects that support resizing or reallocation (like :class:`bytearray`), + the export counter must be checked atomically before performing such + operations, and :exc:`BufferError` must be raised if exports exist. + + * The function must be safe to call concurrently from multiple threads. + If *exporter* is part of a chain or tree of buffer providers, two main schemes can be used: @@ -3069,6 +3085,15 @@ Buffer Object Structures (2) If the counter is ``0``, free all memory associated with *view*. + **Thread safety:** + + In :term:`free-threaded ` Python: + + * The export counter decrement in step (1) must be atomic. + + * Any resource cleanup performed when the counter reaches zero must be + thread-safe, as multiple threads may release buffers concurrently. + 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 0e5f5dc39e7277..342873d057197a 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5091,6 +5091,58 @@ copying. .. versionadded:: 3.3 +.. _thread-safety-memoryview: + +.. rubric:: Thread safety for memoryview objects + +:class:`memoryview` objects provide access to the internal data of the +underlying object without copying. Thread safety depends on both the +memoryview implementation and the underlying object. + +The memoryview itself uses atomic operations to track buffer exports +(each active memoryview counts as one export) in :term:`free-threaded ` +Python. Creating, reading attributes of, and releasing a memoryview are +thread-safe operations. + +However, the actual data accessed through the memoryview is stored in +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 buffer views 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 + +Modifications to the underlying object (such as resizing a +:class:`bytearray`) while a memoryview exists may lead to undefined +behavior, regardless of threading. + .. _types-set: diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 27aedfa878af9a..36677a147f91f4 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -3620,12 +3620,24 @@ 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 ` Python, + implementations must ensure thread-safe management of 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. + .. 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 ` Python, + any export counter decrement must use atomic operations. Resource cleanup + must be thread-safe if multiple threads may release buffers concurrently. + Buffer objects that do not need to perform any cleanup are not required to implement this method.