Skip to content
Merged
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
44 changes: 44 additions & 0 deletions Doc/c-api/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,25 @@ List Objects
Like :c:func:`PyList_GetItemRef`, but returns a
:term:`borrowed reference` instead of a :term:`strong reference`.

.. note::

In the :term:`free-threaded build`, the returned
:term:`borrowed reference` may become invalid if another thread modifies
the list concurrently. Prefer :c:func:`PyList_GetItemRef`, which returns
a :term:`strong reference`.


.. c:function:: PyObject* PyList_GET_ITEM(PyObject *list, Py_ssize_t i)

Similar to :c:func:`PyList_GetItem`, but without error checking.

.. note::

In the :term:`free-threaded build`, the returned
:term:`borrowed reference` may become invalid if another thread modifies
the list concurrently. Prefer :c:func:`PyList_GetItemRef`, which returns
a :term:`strong reference`.


.. c:function:: int PyList_SetItem(PyObject *list, Py_ssize_t index, PyObject *item)

Expand Down Expand Up @@ -108,6 +122,14 @@ List Objects
is being replaced; any reference in *list* at position *i* will be
leaked.

.. note::

In the :term:`free-threaded build`, this macro has no internal
synchronization. It is normally only used to fill in new lists where no
other thread has a reference to the list. If the list may be shared,
use :c:func:`PyList_SetItem` instead, which uses a :term:`per-object
lock`.


.. c:function:: int PyList_Insert(PyObject *list, Py_ssize_t index, PyObject *item)

Expand Down Expand Up @@ -138,6 +160,12 @@ List Objects
Return ``0`` on success, ``-1`` on failure. Indexing from the end of the
list is not supported.

.. note::

In the :term:`free-threaded build`, when *itemlist* is a :class:`list`,
both *list* and *itemlist* are locked for the duration of the operation.
For other iterables (or ``NULL``), only *list* is locked.


.. c:function:: int PyList_Extend(PyObject *list, PyObject *iterable)

Expand All @@ -150,6 +178,14 @@ List Objects

.. versionadded:: 3.13

.. note::

In the :term:`free-threaded build`, when *iterable* is a :class:`list`,
:class:`set`, :class:`dict`, or dict view, both *list* and *iterable*
(or its underlying dict) are locked for the duration of the operation.
For other iterables, only *list* is locked; *iterable* may be
concurrently modified by another thread.


.. c:function:: int PyList_Clear(PyObject *list)

Expand All @@ -168,6 +204,14 @@ List Objects
Sort the items of *list* in place. Return ``0`` on success, ``-1`` on
failure. This is equivalent to ``list.sort()``.

.. note::

In the :term:`free-threaded build`, element comparison via
:meth:`~object.__lt__` can execute arbitrary Python code, during which
the :term:`per-object lock` may be temporarily released. For built-in
types (:class:`str`, :class:`int`, :class:`float`), the lock is not
released during comparison.


.. c:function:: int PyList_Reverse(PyObject *list)

Expand Down
56 changes: 56 additions & 0 deletions Doc/data/threadsafety.dat
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,59 @@
PyMutex_Lock:shared:
PyMutex_Unlock:shared:
PyMutex_IsLocked:atomic:

# List objects (Doc/c-api/list.rst)

# Type checks - read ob_type pointer, always safe
PyList_Check:atomic:
PyList_CheckExact:atomic:

# Creation - pure allocation, no shared state
PyList_New:atomic:

# Size - uses atomic load on free-threaded builds
PyList_Size:atomic:
PyList_GET_SIZE:atomic:

# Strong-reference lookup - lock-free with atomic ops
PyList_GetItemRef:atomic:

# Borrowed-reference lookups - no locking; returned borrowed
# reference is unsafe in free-threaded builds without
# external synchronization
PyList_GetItem:compatible:
PyList_GET_ITEM:compatible:

# Single-item mutations - hold per-object lock for duration;
# appear atomic to lock-free readers
PyList_SetItem:atomic:
PyList_Append:atomic:

# Insert - protected by per-object critical section; shifts
# elements so lock-free readers may observe intermediate states
PyList_Insert:shared:

# Initialization macro - no synchronization; normally only used
# to fill in new lists where there is no previous content
PyList_SET_ITEM:compatible:

# Bulk operations - hold per-object lock for duration
PyList_GetSlice:atomic:
PyList_AsTuple:atomic:
PyList_Clear:atomic:

# Reverse - protected by per-object critical section; swaps
# elements so lock-free readers may observe intermediate states
PyList_Reverse:shared:

# Slice assignment - lock target list; also lock source when it
# is a list
PyList_SetSlice:shared:

# Sort - per-object lock held; comparison callbacks may execute
# arbitrary Python code
PyList_Sort:shared:

# Extend - lock target list; also lock source when it is a
# list, set, or dict
PyList_Extend:shared:
Loading