From 064bc5036505fb35e5bb3fc6e0086b3b45df4997 Mon Sep 17 00:00:00 2001 From: Lysandros Nikolaou Date: Fri, 6 Feb 2026 18:19:56 +0100 Subject: [PATCH 1/3] gh-142518: Define lock-free and per-object lock - Add definitions of lock-free and per-object lock to the glossary - Cross-reference these from list thread safety notes - Change admonition to rubric --- Doc/glossary.rst | 20 ++++++++++++++++++++ Doc/library/stdtypes.rst | 14 ++++++++------ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 24b95b88dfb651..1dccb77cc53228 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -951,6 +951,16 @@ Glossary to locks exist such as queues, producer/consumer patterns, and thread-local state. See also :term:`deadlock`, and :term:`reentrant`. + lock-free + An operation that does not acquire any :term:`lock` and uses atomic CPU + instructions to ensure correctness. Lock-free operations can execute + concurrently without blocking each other and cannot be blocked by + operations that hold locks. In :term:`free-threaded ` + Python, built-in types like :class:`dict` and :class:`list` provide + lock-free read operations, which means other threads may observe + intermediate states during multi-step modifications even when those + modifications hold the :term:`per-object lock`. + loader An object that loads a module. It must define the :meth:`!exec_module` and :meth:`!create_module` methods @@ -1217,6 +1227,16 @@ Glossary `, the :class:`inspect.Parameter` class, the :ref:`function` section, and :pep:`362`. + per-object lock + A :term:`lock` associated with an individual object instance rather than + a global lock shared across all objects. In :term:`free-threaded + ` Python, built-in types like :class:`dict` and + :class:`list` use per-object locks to allow concurrent operations on + different objects while serializing operations on the same object. + Operations that hold the per-object lock prevent other locking operations + on the same object from proceeding, but do not block :term:`lock-free` + operations. + path entry A single location on the :term:`import path` which the :term:`path based finder` consults to find modules for importing. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 0f20163e69509c..4ff3e7bc51f42d 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1441,7 +1441,9 @@ application). list appear empty for the duration, and raises :exc:`ValueError` if it can detect that the list has been mutated during a sort. -.. admonition:: Thread safety +.. _thread-safety-list: + +.. rubric:: Thread safety for list objects Reading a single element from a :class:`list` is :term:`atomic `: @@ -1462,11 +1464,11 @@ application). lst.index(item) lst.count(item) - All of the above methods/operations are also lock-free. They do not block - concurrent modifications. Other operations that hold a lock will not block - these from observing intermediate states. + All of the above methods/operations are also :term:`lock-free`. They do not + block concurrent modifications. Other operations that hold a lock will not + block these from observing intermediate states. - All other operations from here on block using the per-object lock. + All other operations from here on block using the :term:`per-object lock`. Writing a single item via ``lst[i] = x`` is safe to call from multiple threads and will not corrupt the list. @@ -1497,7 +1499,7 @@ application). Other threads cannot observe intermediate states during sorting, but the list appears empty for the duration of the sort. - The following operations may allow lock-free operations to observe + The following operations may allow :term:`lock-free` operations to observe intermediate states since they modify multiple elements in place: .. code-block:: From 4ada47a03e911ba57d44f188f676160f318fbf0e Mon Sep 17 00:00:00 2001 From: Lysandros Nikolaou Date: Fri, 6 Feb 2026 19:25:49 +0100 Subject: [PATCH 2/3] Address Sam's feedback - avoid using lock-free --- Doc/library/stdtypes.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 4ff3e7bc51f42d..d5a001effa5e7e 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1464,9 +1464,10 @@ application). lst.index(item) lst.count(item) - All of the above methods/operations are also :term:`lock-free`. They do not - block concurrent modifications. Other operations that hold a lock will not - block these from observing intermediate states. + All of the above operations avoid acquiring :term:`per-object locks + `. They do not block concurrent modifications. Other + operations that hold a lock will not block these from observing intermediate + states. All other operations from here on block using the :term:`per-object lock`. From a6348382d7590b40f34270f7bb68a11d7ea725b5 Mon Sep 17 00:00:00 2001 From: Lysandros Nikolaou Date: Fri, 6 Feb 2026 19:28:37 +0100 Subject: [PATCH 3/3] Dedent notes after change from admonition to rubric --- Doc/library/stdtypes.rst | 146 +++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index d5a001effa5e7e..f0acc1876d1dd8 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1445,107 +1445,107 @@ application). .. rubric:: Thread safety for list objects - Reading a single element from a :class:`list` is - :term:`atomic `: +Reading a single element from a :class:`list` is +:term:`atomic `: - .. code-block:: - :class: green +.. code-block:: + :class: green - lst[i] # list.__getitem__ + lst[i] # list.__getitem__ - The following methods traverse the list and use :term:`atomic ` - reads of each item to perform their function. That means that they may - return results affected by concurrent modifications: +The following methods traverse the list and use :term:`atomic ` +reads of each item to perform their function. That means that they may +return results affected by concurrent modifications: - .. code-block:: - :class: maybe +.. code-block:: + :class: maybe - item in lst - lst.index(item) - lst.count(item) + item in lst + lst.index(item) + lst.count(item) - All of the above operations avoid acquiring :term:`per-object locks - `. They do not block concurrent modifications. Other - operations that hold a lock will not block these from observing intermediate - states. +All of the above operations avoid acquiring :term:`per-object locks +`. They do not block concurrent modifications. Other +operations that hold a lock will not block these from observing intermediate +states. - All other operations from here on block using the :term:`per-object lock`. +All other operations from here on block using the :term:`per-object lock`. - Writing a single item via ``lst[i] = x`` is safe to call from multiple - threads and will not corrupt the list. +Writing a single item via ``lst[i] = x`` is safe to call from multiple +threads and will not corrupt the list. - The following operations return new objects and appear - :term:`atomic ` to other threads: +The following operations return new objects and appear +:term:`atomic ` to other threads: - .. code-block:: - :class: good +.. code-block:: + :class: good - lst1 + lst2 # concatenates two lists into a new list - x * lst # repeats lst x times into a new list - lst.copy() # returns a shallow copy of the list + lst1 + lst2 # concatenates two lists into a new list + x * lst # repeats lst x times into a new list + lst.copy() # returns a shallow copy of the list - Methods that only operate on a single elements with no shifting required are - :term:`atomic `: +Methods that only operate on a single elements with no shifting required are +:term:`atomic `: - .. code-block:: - :class: good +.. code-block:: + :class: good - lst.append(x) # append to the end of the list, no shifting required - lst.pop() # pop element from the end of the list, no shifting required + lst.append(x) # append to the end of the list, no shifting required + lst.pop() # pop element from the end of the list, no shifting required - The :meth:`~list.clear` method is also :term:`atomic `. - Other threads cannot observe elements being removed. +The :meth:`~list.clear` method is also :term:`atomic `. +Other threads cannot observe elements being removed. - The :meth:`~list.sort` method is not :term:`atomic `. - Other threads cannot observe intermediate states during sorting, but the - list appears empty for the duration of the sort. +The :meth:`~list.sort` method is not :term:`atomic `. +Other threads cannot observe intermediate states during sorting, but the +list appears empty for the duration of the sort. - The following operations may allow :term:`lock-free` operations to observe - intermediate states since they modify multiple elements in place: +The following operations may allow :term:`lock-free` operations to observe +intermediate states since they modify multiple elements in place: - .. code-block:: - :class: maybe +.. code-block:: + :class: maybe - lst.insert(idx, item) # shifts elements - lst.pop(idx) # idx not at the end of the list, shifts elements - lst *= x # copies elements in place + lst.insert(idx, item) # shifts elements + lst.pop(idx) # idx not at the end of the list, shifts elements + lst *= x # copies elements in place - The :meth:`~list.remove` method may allow concurrent modifications since - element comparison may execute arbitrary Python code (via - :meth:`~object.__eq__`). +The :meth:`~list.remove` method may allow concurrent modifications since +element comparison may execute arbitrary Python code (via +:meth:`~object.__eq__`). - :meth:`~list.extend` is safe to call from multiple threads. However, its - guarantees depend on the iterable passed to it. If it is a :class:`list`, a - :class:`tuple`, a :class:`set`, a :class:`frozenset`, a :class:`dict` or a - :ref:`dictionary view object ` (but not their subclasses), the - ``extend`` operation is safe from concurrent modifications to the iterable. - Otherwise, an iterator is created which can be concurrently modified by - another thread. The same applies to inplace concatenation of a list with - other iterables when using ``lst += iterable``. +:meth:`~list.extend` is safe to call from multiple threads. However, its +guarantees depend on the iterable passed to it. If it is a :class:`list`, a +:class:`tuple`, a :class:`set`, a :class:`frozenset`, a :class:`dict` or a +:ref:`dictionary view object ` (but not their subclasses), the +``extend`` operation is safe from concurrent modifications to the iterable. +Otherwise, an iterator is created which can be concurrently modified by +another thread. The same applies to inplace concatenation of a list with +other iterables when using ``lst += iterable``. - Similarly, assigning to a list slice with ``lst[i:j] = iterable`` is safe - to call from multiple threads, but ``iterable`` is only locked when it is - also a :class:`list` (but not its subclasses). +Similarly, assigning to a list slice with ``lst[i:j] = iterable`` is safe +to call from multiple threads, but ``iterable`` is only locked when it is +also a :class:`list` (but not its subclasses). - Operations that involve multiple accesses, as well as iteration, are never - atomic. For example: +Operations that involve multiple accesses, as well as iteration, are never +atomic. For example: - .. code-block:: - :class: bad +.. code-block:: + :class: bad - # NOT atomic: read-modify-write - lst[i] = lst[i] + 1 + # NOT atomic: read-modify-write + lst[i] = lst[i] + 1 - # NOT atomic: check-then-act - if lst: - item = lst.pop() + # NOT atomic: check-then-act + if lst: + item = lst.pop() - # NOT thread-safe: iteration while modifying - for item in lst: - process(item) # another thread may modify lst + # NOT thread-safe: iteration while modifying + for item in lst: + process(item) # another thread may modify lst - Consider external synchronization when sharing :class:`list` instances - across threads. See :ref:`freethreading-python-howto` for more information. +Consider external synchronization when sharing :class:`list` instances +across threads. See :ref:`freethreading-python-howto` for more information. .. _typesseq-tuple: