Skip to content
Closed
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
25 changes: 25 additions & 0 deletions Doc/c-api/typeobj.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3024,6 +3024,22 @@

(5) Return ``0``.

**Thread safety:**

In :term:`free-threaded <free threading>` Python, implementations must ensure:

* The export counter increment in step (3) must be atomic (e.g., using

Check warning on line 3031 in Doc/c-api/typeobj.rst

View workflow job for this annotation

GitHub Actions / Docs / Docs

c:func reference target not found: _Py_atomic_add_ssize [ref.func]
: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:

Expand Down Expand Up @@ -3069,6 +3085,15 @@

(2) If the counter is ``0``, free all memory associated with *view*.

**Thread safety:**

In :term:`free-threaded <free threading>` 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
Expand Down
52 changes: 52 additions & 0 deletions Doc/library/stdtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <free threading>`
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:

Expand Down
12 changes: 12 additions & 0 deletions Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <free threading>` 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 <free threading>` 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.

Expand Down
Loading